From 09d8fe4783ffc146a28bcbe84500e29c8dec3378 Mon Sep 17 00:00:00 2001 From: Darth Vader Date: Sat, 30 Oct 2021 22:54:22 +0000 Subject: [PATCH] Squashed 'src/' changes from ba5f2ba8..f715edb5 f715edb5 Merge commit '2243d25babbc524e7875b3d591bb6b91c4399a95' 2243d25b Merge commit '013c822f76e5dc2e4fc19e87c6e5777aea6151d2' f8b289df Merge commit 'c1af6f3585142e84fc40ec2c74914ef4a3b5da8b' c1af6f35 added newlines for CRAN 013c822f added newlines for CRAN d3bdd8b5 added newlines for CRAN 60ad724c Merge commit 'e4bd9bafb40c82bff5ef275528ca3dd2836672e5' e4bd9baf [phreeqc3] fixes -Wclass-memaccess warnings for CRAN 33f5157d Merge commit '29f06d269c189449f230e15761d01381cfd51de2' 29f06d26 fixed alignment in Description of solution ae41a1ff Merge commit '09a2680a6ec98cfd9d59e5b40b1594edfd6b4eff' 09a2680a guarded write_banner with NO_UTF8_ENCODING 082edbb5 changed src/print.cpp back to windows-1252 encoding; updated check_utf.sh fd78a499 Merge commit '8d7c1fce37a929778a2c34ee19c29933529f46bc' 8d7c1fce adding mcd_Jtot and mcd_Jconc 9f0f6225 Merge branch 'master' of github.com:usgs-coupled/phreeqc3 47a8b4e3 Merge commit '1040066da8c5dea10f0b29ece9e43082cd745fd8' 1040066d Merge remote-tracking branch 'usgs-coupled/master' d19961bf Merge remote-tracking branch 'usgs-coupled/master' d4dfe590 Merge commit '07a864d1a6ed582ddfae5e1c3bf5b0620c3dd75c' 2a946443 cleaned up to eliminate some prints 07a864d1 all jacobians are consistent. Looks pretty good. 56975a7e Saved surface for numerical derivatives df0d68b9 Runs all the test cases. Numerical derivatives work, but still some changes in residuals before and after jacobian calculations. 6bd936e0 Fixed numerical derivative (non-pitzer) 0dde2b01 removed comments aef51fa3 Finally have derivatives right, I think 20281a0e always reset gases 13ec2fcd best I could do for H2S while maintaining old tests. Used INCREMENTAL reactions 8be1ba83 revised jacobian_pz with new logic. Works with fixed_pressure examples H2S, H2S_pz, H2S_pz_appt, H2S_NaCl_Na2SO4. 71cf2a97 still produces different residuals 9022ded8 Tony H2S. Amm.dat, phreeqc.dat, pitzer.dat, utf8, updated test cases afff58ff Merge commit '48cb5e8969285f6d6e54955b6b7f8db76b2ec99f' cb1f9afc Finished up C, Fortran, documentation. Need to check DOxygen 524bfc46 Finished up C, Fortran, documentation. Need to check DOxygen 9dad4471 Merge remote-tracking branch 'origin/master' into state 26c71a35 Merge remote-tracking branch 'origin/master' into state d647eec2 Added StateSave, StateApply, StateDelete with documentation for C++. Need testing, Fortran, and C c22cbb7b Added StateSave, StateApply, StateDelete with documentation for C++. Need testing, Fortran, and C 48cb5e89 Including OH- in converting units. Revised calculated density for H+ and OH-. Makes a difference in several test cases. Removed timing at end of .out in test cases. Checking in all test cases and selected output. 47e1ce5d added OH in density iteration calculation, test case NaOH_density 4aefb066 allow Fe(+3), equivalent to Fe(3), in TOT and TOTMOL. Previously fixed in SELECTED_OUTPUT -total 9b94f8be Merge commit 'bea0ad10a02659d9964f6c88e0ca4e415a2c0aca' bea0ad10 unused variable, punch Fe(+3) 37de8fa9 Merge commit 'eaf788b473321914e188d3afd7ff0df1500665fb' eaf788b4 fixed add_constant, undefined surface null pointer, added test cases 5b7cf2f8 Merge commit '2212f9ccb1ed55b463746d524ace70e78258d5bc' 2212f9cc fixed bug in reprep when sit had surface species. Added capability of sit + edl, have not tested it 2e334cb4 Merge commit '79956e3d9ffcae4885bb46b893d01f311100dca1' 79956e3d made tally_table a vector of class tally 7a2f8916 made tally_table a vector of class tally aa62814a Merge commit '58b0d1fccfc6763e569bee30f4ab5e4122963ede' 58b0d1fc Merge commit 'd77c11ec700085f19b76af6543013e23ee0739d3' d77c11ec [phreeqci] fixed header error with phast 63175ab7 [phreeqci] fixed header error with phast 2efc7423 Merge commit '0feb71507f3a246e8021a2c4781ab6c814c73e71' 0feb7150 [phreeqci] fixed WINDOWS.H already included error on windows builds 4ae64944 Merge commit '123cc8a5f5b095cfebea092a1e49a8872fc676d4' 123cc8a5 [phreeqci] fixed _ASSERTE error on linux builds 4a848dcc Merge commit '22c4a624a98244cf2706842b9d989e0edc3b91e2' 22c4a624 [phreeqci] struct to class changes 8080cbce Merge commit '4cee19d768863d8f09b3629ee36a11e1a6030f33' 4cee19d7 Merge commit '2d8ca2d0f37d13ad67be582208a4e65edfcf702f' 2d8ca2d0 [phreeqci] added 'new' debugging d0c82123 [phreeqci] added 'new' debugging abacccd0 Merge commit '9661feab0a6da6876e32a18565ac4e79eaec2634' 9661feab tokadd_heading leak 2537a2c3 Merge commit '4565c5d894d2a9f58577183783ba1969cbc680ea' 4565c5d8 catching upMerge remote-tracking branch 'origin/master' into classify c22d792c fix notab leak 4b9b9926 Merge commit '6d2b45a8e168aa9e55ee677afe52367857e8140d' 6d2b45a8 Merge remote-tracking branch 'usgs-coupled/master' 38cfe186 memory leak user_print, pitz/sit store, add uphill_NPa, remove TESTINT bd096259 Merge commit '24f9bf72f092f0c9edcb18d959cb174f74f96c10' 24f9bf72 removed TESTING definition d77dff57 removed TESTING definition 35ea7ef7 updated for classify branch 2697dc95 Merge commit 'e2ce92882c5f21ac206e180cf38812d976025717' e2ce9288 Tony agreed with change for all_cells, new test case 0479fc1a Merge commit 'd2a5d63f30d0e3e2fddfeba7058caf8e8401de78' d2a5d63f reset all_cells in all cases 0aa30d21 Merge commit 'e3c0d61bc7acc87be515f9923da3a68e14e20880' e3c0d61b initialize aphi c960e059 builds on vs2005; still needs to initialize class pitz_param* aphi 71dc944c cl1mp, bad initialization 639490d0 Merge commit '2e5f255c794096790125293dcc3af274bed7a61a' 2e5f255c fixed errors/warnings from ming and intel 369733e9 converted to classes b1efa4f2 Merge commit '7961b1686b4e064ec4ec886e76543074d060129f' 7961b168 release.txt, couple size_t 5d76f825 copy operator works well enough 7ce89470 updated InternalCopy for operator equal 7bd13ffa new/delete theta params, pitz_param_copy 50e8903e new/delete pitz_params 87d67929 reverting changes to sit_params and theta_params. Will consider using new and delet dcb9efe0 sit_params ac3335ed theta_params 88782320 delete rate, unused cptr 492df61d descriptions 25e06210 cell_data 051ddba2 stag_data 33157a20 fixed more size_t and initialized all structs f86f4304 back to original set of files I think af1b7616 removing CReaction and Classes files 006d1de9 reorganizing 287f81cd elt_list vectorized 7228bd0d move struct rxn_token 28de8b5c more size_t d2e3a4e5 Removed cxxChemRxn ce647201 cleaned up, removed struct reaction 028e9089 moving to CReaction dc2dc531 vectorized token 9fd3f2a2 save_values rewritten with map 8a6cef51 vectorized save_values 86852254 fixed clang errors, needed .c_str 318e267c (size_t) max and count 1547d91b finished up spread b5c7ba4a going to work on warnings 4c848b4e all inverse structures vectorized. Starting on solver workspace 980d58eb finished vectorizing struct inverse. Need to do sub structs d13bb764 removed count_elts 89ab28d7 vector inverse elts d575ade3 tidy.cpp, title_x 16fd18fe removed string_duplicate from prep.cpp 82a10d6e revised get_elt and get_token d7e3be4c cleaned up some string_duplicate 76366a6a fixed processing file names 157a4584 description_x 51fec193 class_main c748922b added const qualifier for all the parsing 380a6eaf methods set to const, variables need to follow 6d67e22e copier and dash 48e6b939 fixed a new master, advection punch_temp and print_temp, some tidying 5f21dafa unknown->master now a vector. Using size instead of a null to end list. 3c432d05 user_graph commands, alk_list 2b14f809 last_model 4ca2e4e1 Merge commit '885a2f77289ac2738f6e8e02c8039f7e26ee7828' 7a6b8b6d Merge branch 'warnings_redux' into vectorize_2 885a2f77 Fix memory bug in ex13_impl, tweak Makefile. 6907bb06 base, sit arrays 90e84120 starting on pitzer bd0cad94 vector kinetics arrays 1850c32c basic commands are now std::string 78a83eda c,d in polint d82d5d61 vector llnl parameters, removed hash references 7c538b62 Revert "delete s[i]" 97bcfd73 Merge branch 'warnings_redux' into vectorize 15a8991e delete s[i] 0b194040 master new/delete b100f853 more new/delete. Fixed str_tolower for ming fd93f846 needed to new/delete species and phase structs 7b0c2df0 Merge commit '1986e00a933549022abb335d82f40c65abf2a292' 1986e00a alphabetize tokens ee6fa533 bool analytic cc614e64 add_logk for logk, species, phases 67447c5a Removed hashtable, all hashes have been replaced with maps. ee7d2c57 replaced hash for isotope_ratio, isotope_alpha, calculate_value with maps. Fixed some case errors with new maps. 52e06225 replase master_isotope_hash_table with master_isotope_map c01c8d66 replace logk_hash_table with logk_map. Added str_tolower(std::string) 3e69461c replaced phases_hash_table with phases_map effafe0a replace species_hash_table with species_map 8bff6d36 removed HASH code. replaced elements_hash_table with elements_map 90e9ee02 removed ineq_init. Vector advection_print, advection_punch 2f380479 size_t for subscripts de884e4e Merge commit '5161ea7563e7ed51be9ed623611762890c785858' 5161ea75 Merged origin/master, Alphabetized Basic toks 0c581ea4 Merge commit 'f8e05c176fd6c469ae6c54f21450d48ef52778be' f8e05c17 only call qsort with more than one element 8e9c5b35 only call qsort with more than one element c1c08f2f Merge commit '1ab86414f0b25f986c14a55bdb967dfdef3af2ae' 1ab86414 remove _v, use std::vector only, alloc at least 1 scratch 9732a1c5 cannot qsort size 0 vector 3770354f Merge commit '67fc478a2f7873db5a2cf6306b433fa07fd58575' 67fc478a one more .data 2f0f5e16 Some replacements of .data() were incorrect ecbb26c3 Merge commit 'ba9813a43c6d087851bf5c2ea86a658cc785060c' ba9813a4 remove .data() 43765f86 need 5688f837 Merge commit '0feb20dead731b8dedf3e86d5ef664bc24733127' 0feb20de after merging origin/master, one fix needed f136feb6 Merging origin/master. Merge remote-tracking branch 'origin/master' into warnings_redux 71aa5b9f bug count_sys not incremented e43550cd vector inverse d4cc14ef vector x 6c0edef0 vector rates e3cc46ad vector save_values 41b99655 vector species_list 449a54f6 vector mb_unknowns 51514eb1 vector delta, sum_jacobx f0707aa0 vector sum_mb1, sum_mb2 7d303de1 vector trxn.token 83cfb298 elt_list, moved qsort to elt_list_combine e8c90279 vector elt_list 0957a528 vector theta_params b1af156b vector pitz_params e3ea0100 vector sit_params b87d0cdd vector my_array, residual, delta e43471a7 vector s_x 622d3618 vector s_x 3d41ef82 vector logk e8dd2083 vector sys 3c9f5946 vector master de1ba623 vector s e7c78a88 vector phases f2c64fef vector elements e8af689a vector isotope_alpha ba2601a3 vector isotope_ratio 76da4f89 finished master_isotope 4bb1c800 vector master_isotope 97e574d2 vector calculate_value** 9d9fbfb8 cl1 variables converted to std::vector 1e0d4105 using memset 54b0d4d1 starting on space 8c8f6fd1 Merge commit '5a649c2b485a02008d5bfa3763c25dca41358e67' 5a649c2b Merge pull request #2 from usgs-coupled/gasphasepressures d6e6d11b Merge pull request #2 from usgs-coupled/gasphasepressures a9925376 (void)sscanf, removed SKIP, removed PHREEQ98 710d0096 Merge commit '6a5bb8aba56d568bd139bc3aed7363d929397790' 6a5bb8ab Merge pull request #1 from usgs-coupled/mar10 d9ced82f Fixed uninitialized constructors and couple of other warnings 6b19edba Fixed uninitialized constructors and couple of other warnings c79d2c23 working on UTF-8 fcee4d5d Added delta_h_species, delta_h_phase, dh_a0, dh_bdot Basic functions 81e862db Tonys changes Mar 10. SIs in inverse calulations 62c51bb4 Merge commit '9e8b3828595be0339d3c63b5707412e34a4e5007' 9e8b3828 Merge remote-tracking branch 'usgs-coupled/master' 4473286a Merge commit '053b4c6fb3cb778f08377b932745643472888aa5' 053b4c6f Merge remote-tracking branch 'origin/master' b9f44da2 Merge remote-tracking branch 'origin/master' 20091aa5 Merge branch 'log10molalities' into gasphasepressures c4f9d291 Merge branch 'log10molalities' into gasphasepressures 41e1112f Last of changes for GetGasPhasePressures and GetGasPhasePhi, openmp and mpi. MPI fortrans not tested. 18158db3 Last of changes for GetGasPhasePressures and GetGasPhasePhi, openmp and mpi. MPI fortrans not tested. e1f9cb1c more checking in. Should be down to tweaks for SetGasPhaseMoles. e1554d99 more checking in. Should be down to tweaks for SetGasPhaseMoles. 00ee6e36 C++ is working with OpenMP and MPI for Get/SetGasPhaseMoles. Need to add c and F90. 0a633289 C++ is working with OpenMP and MPI for Get/SetGasPhaseMoles. Need to add c and F90. c3a3153f Added GetSpeciesLog10Molalities. Tested OpenMP with VS. Tested MPI with MinGW. Fortran, C, and C++ seem to work. 566bfbce Added GetSpeciesLog10Molalities. Tested OpenMP with VS. Tested MPI with MinGW. Fortran, C, and C++ seem to work. e8b11f34 added optional 6th argument to Basic function sys to change sort order from molality or moles to the name. Added synonym PAD$. Added new mytest/sys_sort. 20ab4be9 Merge commit '3e4fc7ea18da750b2746422898dc100d74271b34' 3e4fc7ea cleanup commented lines 54b992f5 working on tabs and no newline 21818473 Merge branch 'master' of https://github.com/usgs-coupled/phreeqc3 deeecb0c needed strexpr in ADD_HEADING to allow expressions 2d2f55ad Merge commit '9b7785f0730513093816cfbd1c24fbf068e4f2ca' 9b7785f0 [iphreeqccom] updated date 33f58989 [iphreeqccom] updated date e224625f Merge commit '711b1d061cba1005f028d1b17b0bb34e2b2d9162' 711b1d06 Merge commit '608e74f5d3c55a4d91a4e08d86f2fd6df0ce0a05' 608e74f5 [wphast] updated date 50016849 Merge commit '5128e1337b2edd39ac3d16d2a5ce1c7f1e5f0773' 73d356aa [iphreeqc] updated image location 5128e133 [phreeqc3] updated image location fba8ae2a [phreeqc3] updated image location 43988f06 initialize punch_newline 176fb029 Moved initialization from header to constructor, special characters in As.out c9f796a0 added ADD_HEADING for IPhreeqc 1362f0ff Added EOL_NOTAB$ and NO_NEWLINE$, updated release notes 055ebf5c Merge commit '2b4dbbdb167b77809e7c89bbec406810aa28162a' 2b4dbbdb Merge commit 'cd51d8aeed46909e5f028a19089acfef43d6ede9' 589d88ff Merge commit 'f2023c4d83f55cbf4598ae8e308df3ef06b8f08c' f2023c4d Merge branch 'gtest' into 'master' f4bf55d7 Merge branch 'gtest' into 'master' cd51d8ae reset for dlls 54161f4a reset for dlls a28c2414 reset for dlls e904f5b7 Merge commit '01c99a70a5baf72d53ae8e8f6db53948ddcd990c' 01c99a70 Merge remote-tracking branch 'github/master' 23f39174 Merge remote-tracking branch 'scharlton2/master' f6644e6e check for null pointer. Encoding for .out file e562e09e Merge commit '9319c9daf8fe76ef24cf4d262081e8cd0c8fc3fe' 9319c9da Merge commit '5b816fa1fd82eb94e2702b6bd9df6066fb71267b' ea2c663c Merge commit '07717b1e931b4526f3c33921cd872577599410b6' 5b816fa1 added src/phreeqcpp/common/PHRQ_exports.h 07717b1e added src/phreeqcpp/common/PHRQ_exports.h 0b0fced3 added src/phreeqcpp/common/PHRQ_exports.h d8c638fb Merge remote-tracking branch 'origin/master' into gtest c8be5dae Merge remote-tracking branch 'origin/master' into gtest 18e137ff Merge commit '87bbb6a6f08a3f6cf0e4122e9c080db4ef000344' 87bbb6a6 adjusted alignment for utf-8 strings 3782d442 Merge commit '03bda166155a6b2ce652e4ad6aceaf807c3bc041' 03bda166 added write_banner to non-DOS and added UTF8 define 995de526 converted to utf-8 d4261840 Merge commit 'fc8fe3e98fb3f1e42482636938537ed6028b3ee6' fc8fe3e9 re-added src/ZedGraph.dll 6e7023f8 Merge commit 'fbae3e92947cf79c1d615e657192eb8d5c78b9e9' fbae3e92 code change for extending porosity definition. Change to TonyLitharge2a 46257e73 added googletest and fixed some minor bugs 13ca0551 added googletest and fixed some minor bugs 2760ad0c added googletest and fixed some minor bugs dd058e3b implemented Get/SetErrorOn f1dda6c5 Fixed problem with exchange-related when exchanger is defined as CaX2 b138ed20 Merge commit '20daad41c8999670f933430f1c705384cdfa6947' 20daad41 I guess cxxSurface::NO_EDL is correct 5631ce1e Merge commit '801812ded3961b37ed264997211694bd0563d554' 801812de Tony's changes to implicit Nernst-Planck calculation 6b4892cf added Basic function DEBYE_LENGTH and test case zeta 921ab106 Changed tidy_exchange_min and tidy_exchange_kin to tidy only for new_def and n_user >= 0. Fixed bug if surf_charge not defined for NO_EDL. Added test MoreExchMix 9d480bc1 Merge commit '2aef60aa6f00411ac4491e868c3cf2b6beab11ab' 2aef60aa Finished up surface and exchange related for cases where related phase or kinetic reaction was modified. Proportionality should now be maintained. Added test cases. 569e1e13 Exchange related. Needed to update in case the related entity changed. ea54e02a Free str in callback in PBasic ea21e58a Remove SetErrorFileOn from RunCellsNoPrint, RunStringThread, RunFileThread. Remove Set_error_on from IPreeqc::SetErrorFileOn. 9029c2bc added changes from email dated 2020-07-15 74b541d4 Merge commit 'a87cd1fee89cf0076aa06b9018e8ad3b44566516' a87cd1fe Merge commit '1871b026ca8487c23a025415dbc0b2eca01f9af4' 1871b026 fixed some c2011 warnings, added more info for -formula errors, fixed pressure llnl examples aa4d0234 fixed some c2011 warnings, added more info for -formula errors, fixed pressure llnl examples 1c3edaa7 Merge commit 'e1465e33228a3f867bd822405fb100614d5f6d0c' e1465e33 Commit from David's Email 2020-05-22; Implementing llnl-type databases with higher temperature nad pressure 89ca3d27 Merge commit 'e18e1ec6be5107e051f99e41103638c907b1eaf5' e18e1ec6 Tony bug fix for TRANSPORT. Harmonic mean for boundary? Added Cub example. a208e394 [phast3] Testing subtree merges a4c58264 Merge commit '44f077e8893ba3434c5a0feccfe42be7e9aa83db' 44f077e8 Merge commit 'e68934133fc9cd45e7cccc397c55e13f7ee92e5b' 090f5e68 Merge commit '4f34fd04ff34668494863eab5312157ce746d862' e6893413 [phreeqci] Testing subtree merges 4f34fd04 [phreeqci] Testing subtree merges 9bcc875c [phast3] Testing subtree merges 8e901d06 Merge commit '69c0bb3e62a7d3759d685b37652cac80373a58e7' 69c0bb3e fixed conflict on merge bb5bcbf1 fixed conflict on merge 55c4dbad Merge commit 'b25fc5bdd48b6d3ab8d677f7d38ad3a462789500' b25fc5bd fixed conflict on merge ca80be6e fixed conflict on merge 49a74a6a [phreeqc3] Testing subtree merges aec6f90b [phreeqc3] Testing subtree merges c4c224ad Merge commit '84865ad5ac30a9edb86c89ced4194d127ee896fd' 10875555 Merge commit '0bf4138d63cd63d9cb3cd2081c16e706b8c867d2' 0bf4138d Merge commit '4a8727cecd9fefd1587485820e913c0e666b77d9' 92807718 Merge commit '4a8727cecd9fefd1587485820e913c0e666b77d9' 4a8727ce Merge commit '553875fa3799217dc937ee3ef6e9d874e545ccf5' 553875fa Merge commit 'aab8bc12ea8be8aec5943e1c77a54b19d28168cb' efbddbf6 Merge commit 'aab8bc12ea8be8aec5943e1c77a54b19d28168cb' aab8bc12 Merge commit '84865ad5ac30a9edb86c89ced4194d127ee896fd' 7bd02ff8 Fixed bug with more porosities than cells in TRANSPORT. Added silica sorption to databases. Revised CalPortDiff 84865ad5 Added .gitlab-ci.yml d3981950 Added .gitlab-ci.yml eed9de90 Added .gitlab-ci.yml 40c2787c Added .gitlab-ci.yml b6921025 Added .gitlab-ci.yml 3b6ce6c1 Added .gitlab-ci.yml daf64a14 Added .gitlab-ci.yml 757927da Merge commit 'ae06f3533db376699e82c3b0b183fb2bdd43721a' ae06f353 Fixed GFW bug on new elements in TRANSPORT aa963a9c Merge commit '9cc783b02f8bc437a0572eb32d523f69344724bf' 9cc783b0 added Basic functions for PHAST: velocity_x, velocity_y, velocity_z, transport_cell_no 0366caf1 added Basic functions for PHAST: velocity_x, velocity_y, velocity_z, transport_cell_no d97736a3 Merge commit '79f768a7bd6c88e064ba25a33880cb84cfd1489f' 79f768a7 Merge branch 'master' into 'master' bd7634a8 removed j = j in loop d0e5bac5 Merge commit '542394c95add8bc99c697c8dc33887efe2b1ae20' 542394c9 IPhreeqc: ifdef'd out references to std::cerr and std::cout 6fe5c0e2 IPhreeqc: ifdef'd out references to std::cerr and std::cout e5bf8b0a Merge commit '6067ce8ce599b9cbfaf012c0abc377f20e616b42' 6067ce8c Merge branch 'implicit3' into 'master' 21bd20f5 Fixed more compile warnings. Removed andra_kin_ss from testing, results are inconsistent between Linux and Windows, presumably the ifs in RATES fd242bd3 Merge commit '97b9c5848ee2b8ae6413ffa09e0b79be7d739ef8' 97b9c584 Merge branch 'implicit3' into 'master' 45db5cf0 Another Linux warning, lower tol on andra_kin_ss. 3049467b Merge commit '443be1cdb1adce5a8da310d883bf4d97fc661576' 443be1cd Merge branch 'implicit3' into 'master' 9a29aaf8 Last Linux compile warnings. Added more precision to andra_kin_ss. d7188d25 Merge commit '6dafd7d75e3dc4b97ed0b5efa7424bf0630829e0' 6dafd7d7 Merge branch 'implicit3' into 'master' fbde6338 Fixing Linux compiler warnings, checking in new regression test files. 679c637e Merge commit '22077113d63c5f086de917486dcf4df678a9c464' 22077113 Merge branch 'implicit3' into 'master' 77e36a23 Tony fixed some transport, revised colloid_U. New cases added to Makefile. f07caf97 Changed back print to allow incremental_reactions to work correctly d8ba4ac6 Merge commit 'beadd0738b548a8bebe9934e5de2497cd6e3a7e0' beadd073 Merge commit '5947da90657d1cb8f832152b4573dca0bbefb49e' 63108ac7 Merge commit '6a49d41253c7705c0273262d0290389b18643d32' 6a49d412 changes to make related and mixing items independent of case. surface_mix test case. 5947da90 initial Tony changes 8089c102 initial Tony changes 86888c46 Merge commit '009aec74d1cae1a8fc361a5560d89f44173fd7ee' 009aec74 Merge remote-tracking branch 'coupled/master' 4676ee4d added more P-R gas paramteters 26c751fd Merge commit 'c07314c7e58a9e3fda0d3157863a3a8634864f02' c07314c7 Merge commit '492a4d257f300b7a9e0b5dc7e212c8f85ecb7f6e' d9a6717e Merge commit '81ca6334dc10ebd6f24a7f6a76358751524a7f0f' 492a4d25 Merge remote-tracking branch 'coupled/master' 81ca6334 Merge remote-tracking branch 'coupled/master' 86c54339 Merge remote-tracking branch 'coupled/master' 950fca24 CRAN: replaced deprecated std::ptr_fun with lambda function 597bcd76 CRAN: replaced deprecated std::ptr_fun with lambda function 1b13e09d CRAN: replaced deprecated std::ptr_fun with lambda function 5b3379b9 Merge commit '044e0ea2c0337ab4d175f3303e1d6d192a451782' 044e0ea2 phreeqc_ptr bug in internal copy 67342a4d Merge commit '59342977ed27c7b504432da94511229f5998437a' 59342977 Merge commit '5c53fb207238bc0e846123a7e0d71a48bd9976ab' 84128239 Merge commit '5c53fb207238bc0e846123a7e0d71a48bd9976ab' 5c53fb20 Merge commit '1327e93127e40e7a55ec629dcc9dd91ec29e77fe' c117e182 Tony fix of index error b90ddb56 Fixed Tony's fix, added implicit_as example 03acc3f7 changed abs to fabs 1fef40e1 added implicit, max_mixf to internal copy 5a46f0d7 Merge commit '32939bae4bdfa4b8c6c448688524bc35d5ce6baa' 32939bae Merge commit '1327e93127e40e7a55ec629dcc9dd91ec29e77fe' aa34ac11 Merge commit 'b3bf691b4964b3eedbaf04d6ee9bd35f98afb113' b3bf691b fixed > > in templates for gcc c9291134 Tony fix May 31 1327e931 Implicit seems to be working with Tony's latest changes 55ea163d Implicit seems to be working with Tony's latest changes c7111f77 Sort of works, still bugs and serious errors compared to explicit 79c28e50 Merge commit '600c7ee5728aa23c963828df22b6e0c94038a3ed' 600c7ee5 Fixed some bugs with iso.dat inverse modeling, added test case. Still does not generate [13C](4) and [13C](-4) from SOLUTION 94155a63 Merge commit '2291700eebddc4374b8cd88d19fc0978ee84c7e9' 2291700e Fixed gas_phase_mix bug, added test case 51fbb3d5 Merge commit '035a4e0f7db61f7256e63659b0e7853dd299263e' 035a4e0f Tony tweak to transport.cpp 1e4a2941 Merge commit 'bd4fc256a6f168e127f6d2b783653c890c23a231' bd4fc256 Merge branch 'tony20190117' into 'master' 71c994b6 skipping restart 3b730866 Merge commit '1257f8c7c984818ead2402595470f93d96db76ca' 1257f8c7 Merge branch 'issue-3' into 'master' f0c3701b Merge branch 'issue-3' into 'master' fbde6453 Merge branch 'issue-2' into 'master' ce33478c Fixed -Wcatch-value warnings reported by CRAN c88ff034 Fixed -Wcatch-value warnings reported by CRAN 548aead5 Merge commit '040fd95fadbc2cd62df3c056ef7a41e10c8b7a29' 040fd95f include restart, remove ex20_debug 12df06b3 Merge commit 'd57264d9733b6f5687d8b7d87f5e9a8aad7e7d71' d57264d9 2. changes to solid solution numerical method 43386a0b Merge commit '3fd8155d566798e121a1f54418f901cdd0db5e6c' 3fd8155d changes to solid solution numerical method f90fc5af Merge commit '2b14a9475c1cb0c64d3adaf67a907b63ea454c9c' 2b14a947 Tony's changes 20190117 9d51f5cf Added code to record git commit hash 1a4422b8 Merge commit 'ae6e8b05440ea1db35226a479daf5d3b4a05690a' 1bfc9f49 Merge branch 'master' into 'master' ae6e8b05 added modify methods for restart files fa7776b8 added modify methods for restart files 749c3e09 Merge commit 'b500c5448b613a20c48a08237460c3b495d3d390' b500c544 changed restart file to include UZ 1150a4a2 changed restart file to include UZ c2dada99 Merge commit 'fffac6d359c01544c617aae6717678f0fd884193' fffac6d3 another try for ex20_debug 4235170d Merge commit 'fa5ee5049432867a5161fcd97aebe475854fffdb' fa5ee504 fix problem with ex20_debug 65dbd2c1 Merge commit 'd99390113028519f41895af04cedba4a43258336' d9939011 encoding, limit.h 6e92ecf9 Merge commit '92c81f9fdd43f175d302028b56e7edbab81a7d06' 92c81f9f Revised logic for nmix dbefc44f Merge commit '3cc84da8ceac5d4a819fc0a6779b7ca361c17226' 3cc84da8 Merge remote-tracking branch 'coupled/master' into ss_trans c83e4bf7 Merge commit '56b5bf3f438e0b99c7a35b79ad169be36c2fa352' 56b5bf3f create valid ranges when sscanf doesn't return 2 c43c9af3 tweaked ss, changed surf function per Kinniburgh b10df16b Corrected syntax of integer limit, previous commit actually changed ss convergence parameter, used to multiply by 0.99 d74c8ff5 Corrected syntax of integer limit 906cfd49 Check value of nmix 058375c2 removed check of ss when sum of components is small c4fa021e Merge commit '2977db4424a04e3643bb02d356984c7aa78d1fde' 2977db44 Tonys fix to diffusion bug with porosity change ec4ec0a3 Merge commit 'f90446715b85306f1c8489219316d4c962fd4659' f9044671 revised lists to be cumulative for eq, gas, kin, ss 3a0292c6 revised lists to be cumulative for eq, gas, kin, ss 7b836dd6 Merge commit '9285985dc0222cf6fde33c32625027d376159b3b' 9285985d merging coupled/master into copy 7c23b625 Fixed string_duplicate memory error 2d5551a3 fixed sc7 for copy and initial time 4842d9ef inverse iter 100000; finished copy operator; a bit more testing to go 4eefe43a ex20_debug fix 78e39cde still debugging copy cee10e7a fixing bugs in copy operator ebab4bce fixing bugs in copy operator 5a35e027 Fixed Linux warnings, memory errors b86f7938 Beginning to test copy operator b3e7b2ec Merge commit '5d40e69cc7fcc5bca0037ee70d9d64e4c370e5dd' ae05e0a6 Merge branch 'cran-warnings' into 'master' 5d40e69c [IPhreeqc] added parens for clang++ -Wlogical-op-parentheses 67d807cc [IPhreeqc] added parens for clang++ -Wlogical-op-parentheses 113fd9aa Merge commit '936de384743fe782f702ed0ab96e4a549ca3dd2e' 6894b4a7 Merge commit 'd96f386211ad5fbace94d550ca371d9eacb3262f' d96f3862 Merge commit 'ebeddcde9d54e3e79c8923496a0b7573432d9388' 936de384 removed register keywords and updated for misc clang warnings af408f7c removed register keywords and updated for misc clang warnings ec9de4cf beginning of checking copy operation b447434d [iphreeqc] Changes for CRAN 3.4.7 ebeddcde [iphreeqc] Changes for CRAN 3.4.7 b4894423 Merge commit '9592d6e8015707da5eb033f136590ba40015da61' 9592d6e8 Merge branch 'dlpark-phreeqc3-TonyApr2018' into 'master' 7c0fb65b [phreeqc3] needed to check gas phase type for same model, added test case ca5e069a Closes #1 98d075d4 Merge commit '9152ca292e2ea2a042a654b6393b6fcd9160e0d4' 9152ca29 Closes #1 78b05b07 Merge commit 'ebc4f69cd45b414f2fce86455cd025a2eb762134' ebc4f69c Merge branch 'dlpark-phreeqc3-TonyApr2018' into 'master' 97a0cec3 Fixed bug where 1W was interpreted as an isotope 2deb4edc added option -ddl to surface. Added test case 9e316ab6 Merge commit 'df7d5debc6ce37c341b48ad123e4839ab409da38' df7d5deb Merge branch 'gammas' into 'master' 35d8d926 Merge branch 'gammas' into 'master' cf35bf3b fixed some bugs in the reaction lists and got to the point that data is now all available in the phreeqcrm object once FindComponents is called. 34abb5b0 gammas finished, working on reactants e8ae8322 gammas finished, working on reactants 311fa04f Merge commit '53148278fc5456fddfcffc2d46aa7ccb7fff0ee9' 53148278 Tony's changes; diffuse layer with pitzer 83e3b8c0 Merge commit '4271ca42adb6957392c22a564d82baf99933b166' 4271ca42 Tonys corrections, added balonis test ae5624d5 Merge commit '2e390fd5ab713c0c135208931dd015935da01f19' 2e390fd5 commit fix for Mtg 1d5c59a1 Add 'phreeqcpp/' from commit 'da9d06b423a87ed5eae503c0faf779ac11c96027' da9d06b4 Split 'common/' into commit '077abe0106b2c1d44d8e996d62ae3e8b2c23bf62' 114cea67 minor memory leak in fail2 7d044e79 Tony fixed error with sc2 and aquia 86a55b5b Tony's changes from 20180305 b0fdcc41 Added Mtg(g) in calc_PR 6c9e9843 One more compiler warning 8e121390 Fixed bug from Shell. Segv because s_list.size differed from count_s_x. fa7a3c71 Tony's changes and bug fixes d801026f moved concrete-2005 and renamed to trunk 800f2345 added Form1.2005.resX to configure dist db60ab0e fixed to compile in vs2005 a46a85a9 Making consistent 077abe01 Tony's changes. b5ef46d0 Tony's changes. 299188f3 Fixed dist_x function for transport. bdd21cd1 Tony's fix for LG function for exchange. 29a5848d Tony's fix for LG function for exchange. 5812e211 Synced to PHREEQC3 12702 e6a13b70 Error when reading PHASES and -no_check was read instead of equation. 92aa19a4 Synced with concrete 12683. 2c1f1b33 Synced with PHREEQC 12683. bdef01ca Expanded error report for equation balancing. e3bac3db Updated to svn 12361 on trunk 73a34a99 Added "calculate" capability to density in SOLUTION_SPREAD. bf6dcd08 Iterate to calculate density in initial solution calculation. 6190fb36 New Basic function TITLE 43ecf1e3 New Basic function TITLE 66481ea6 added tokequiv_frac to tokeq_frac 5f165e7e Duplicate tok fixed up some of the new features. 8421d08e Updated to phreeqc3-trunk 21475 Adds some pr h2o tests. Adds Aphi calculation for Jonathon Toner Latest Tony tweak on PR. 6a6e07e0 Tony's tweak. b121ec3b Added tests for PR calculations with water ex7_T_P pr_h2o. Added to Makefile Tony's change to prep.cpp to keep phi(H2O) in a good range. Changes to allow Jonathon Toner to define A(phi). 739dfeb9 Tony's fix for potential null pointer. a15efe53 Minor updates to databases. Tony fixed potential null pointer. 63ac5f3c Fixed null check, also needs elements > 0. Added more test files that were missing. 4df098a2 comment check for null after find_J. Don't know how it works. 62286ff4 Commented out check for null. Not sure how it works for Clogging, will investigate on Linux. Checking in new SC values! 9d2762b1 Merge to trunk 12441. cff3ae90 Wrote tranport_cleanup. 873b98ff Caught error I think caused by defining X as a SURFACE and an EXCHANGEr. Added test case. Initialized unknown structure variables. Caught null pointer in Tony's multi_D. 6a60c92f Need to set Dw 35458cd0 Initialize correct_Dw to 0. e9fe756b updated Makefile? fixed compiler warnings in transport.cpp cf4a14f0 no gmp 025d2a1e Merged in Tony's changes for electro diffusion. 9bf1411f Updated to PHREEQC3 12386 d1cf5f96 Running tester on Windows. It's been a while since the test cases have been checked in. With lobo gone, will do primary checks on Windows. 8d624a7f fixed compilation for linux c++11 424c0703 fixed compilation for linux c++11 80f143a6 changed macros for unique_ptr b06b4f2c replace auto_ptr with unique_ptr 9a6ec5b1 Error in sys("equi",...). Returned the values from the phases structure rather than from the unknown structure. So, the value from the beginning of the simulation was given rather than the reacted amount. 52ea7271 Had a problem with mismatch between mass_water (1e-6) and total_o (~55.5) from Colman phast run. Now set mass_water based on total_o. 53fade44 Bug when related to surface related to kinetics and proportion was zero. Think there was a bug anyway trying to identify the right surface total. Hopefully fixed. baf36d4a fix memcpy overwrite 85021963 removing shrink for pitzer c6e27337 shrink_array for pitzer in model.cpp 32b2b166 removed dw.cpp 51974df3 removed dw.cpp 22fa4499 Moved mpi.h to beginning of each file. Conflict with stdio? Fixed TestRM.vcxproj to have zlib defined correctly. 9cf105c9 Kinniburgh problem with P(O2) = 10^-51 e8aa08af Tony's latest. f65078e9 added function phase_vm and test case. 06e41e92 Tony's changes for advection/diffusion with irregular grid. He was not able to get a robust result. edf7f8f6 Added Basic functions for KINETICS_FORMULA$ and SYS("kin" 96839072 Needed to write around signbit. 03c9d56c Tony's updates to allow fixed current. fe3f36ea Need to zero moles in tally struct in addition to zeroing totals. ad55ede3 fixed selected_output missing headers when SelectedOutputFileOn == true c02d598b wrong format in read 394f918e changed preprocessor guard for windows _set_output_format call e37e06ad fixed selected output missing headers given the following input: solution selected_output end e9837bb0 fixed bug that overwrote selected output files when (re-)opening a .pqi file d918a52d switch back to 1e-25 79a49656 Change MIN_TOTAL to 1e-18 MIN_TOTAL added to KNOBS. 8604336e Update from phreeqc3 through 10983. d2dba8c2 Tony's latest changes. New test case SC_Ohm, added to Makefile. 48b4d870 Added asserts to inverse.cpp (had -1 for a row number). Ensure that master species for an element contains the element. Fixed memory leaks if problems detected in read_master_species. d5e7aa0a Tony's changes. Call calc_delta_v always. 810f51a2 two changes for vm_tc, always calc_delta_v 4405e60e Merged source up to phreeqc3-trunk SVN 10892 626d6782 Added setdiff_c method. 8bc58627 Switch back for now. Tony will fix later. ed231a91 calc_logk_s, removed if (s_ptr->logk[vm_tc]) at line 489. 78c5daa0 Negative mass of water bug. 2a9ab265 For concrete, this is correct. For phreeqc-trunk without the new transport changes, previous is needed. 81525819 Needed for old test cases. bde722c2 Another try on porosities. 7c77eda9 Forgot to remove testing code for differences in porosity between old and new version. 320aef18 Merged Tony's 2/3/16 changes for setting porosities. 998f194b To read multiple lines of porosities. 2c3cd3e3 To read multiple lines of porosities. 38322728 Tony's fixes 0bd85c16 reverting to old porosity definition. 279acc25 Merging Tony's changes for reading porosity. 72991f1a First fix of day. b8ee7f64 Another temporary fix. 4cd0224e Temporary fix for Jenkins. Need Tony's fix for porosity of stagnant zone if porosity defined for mobile zone. a6390ed8 free pors 7c56b919 free pors. f5ea53e0 Added porosities. 95133f2b Tony's changes. Added porosities. 8dc42fee Tony fixed the memory problem. Will rerun Valgrind one more time. 96e537f0 Initialization, memory leak. 891e33bf More initialization 24dd5ede Working on valgrind uninitialized variables. 1fcb85d6 use space for cell_data 964b1665 Initialize sol_D[l_cell_no].spec 7f842da6 removed MPI. Fixed memory leak. 7fbf8779 Removed parallelization. 41bb9483 fix for linux, but Cl has NaN dbaabe01 initialize variables 9644278e added Dictionary and Serializer Deleted unused files. 174c739d Updated concrete source from concrete_parallel source (not using parallel). Now going to run test cases on Linux. 5af54884 Switching back to diagonal_scale false for default. No obvious advantage in test cases to change, and it could break someone's runs. 716a978b Made diagonal_scale true by default. 516d6937 Another check for null pointers. 6f7a2e50 Fixed segvs from Kinniburgh. a9465932 Fixed formatting for temperature of Specific Conductance. Added test cases missing_surf_related_equi, missing_surf_related_kin, and SC_temp. 8f51d927 Need to set new_def to trap error with surface related to rate. dec66bb8 tweak if tidy_min_surface is invoked on a _raw definition. ae2286eb Added serializer for rebalance by cell. Made fix to tidy_min_surface, same as tidy_kin_surface; skip if not new def. a7020351 totals of surface comp set incorrectly after read_raw. Skipping tidy for these cases. 6ae25495 added ifdef for R (R_SO) b33a28fa fgets warning b88bd3f9 more Linux warnings eb7e91eb fixed Makefile.old, Linux warnings af2dbfdc Added changes for serializing. Copied files from concrete_parallel, without merging or svn_copy. Added Dictionary.cpp and Serializer.cxx + header files. 4dc54757 Bug showed up in PHAST. Errors in definition of surface related to mineral or kinetics were not detected. c6f09779 added equi_delay to the convergence sets 54769821 roughed in equi_delay ca4c8f9e delay removing phases in ineq, should be unchanged until delay is > 0 0970c440 using old viscosity 301d2d00 transport.cpp, ifdefd old viscosity, initialized variables; basicsubs call viscosity();phreeqc, revert ineq_tol c15e56ad -potential not defined for solution read_raw. fb18be24 Tony's implementation of electric current. Test case current1. 2fe6313f Avoid a null pointer. Not really sure what causes it, combination of charge balance, pe constraint, alkalinity, and C(4). bef66de3 Fixed formula for pressure dependence of B1, B2, F1, F2. Previously had a limit of -10C, otherwise produced a floating point exception. 218f4f5f Avoid negative moles of equilibrium phase. Typo in RELEASE 33b4ae37 fixed BUILD_SHARED_LIBS build on windows 34f79213 fixed BUILD_SHARED_LIBS build on windows f997d62a [ not handled as upper for solution_spread in copy_token_tab. e8553de4 Debug prints for Peter de Moel. Possible null reference in tidy_solution. 3f332d18 Trap null pointer missing gas components. 64703778 updated for R 190128c1 updated for R 6e752e26 windows java working with callback fbaeb7dc working on swig callback 8f1eb25c Added SetScreenOn to C++, C, and Fortran. Updated PhreeqcRM tests to include a call to the new methods. Set minimum concentration (moles) to 1e-18 in Solution.cxx. 560306e0 reran test cases. Diffs caused by CO2 changes in database e6d44638 Extra print for Donnan Layer psi from Tony. caecf8b8 added common directory for Parser/PHRQ_base/PHRQ_io/phrqtype/Utils adbe4d66 added common directory for Parser/PHRQ_base/PHRQ_io/phrqtype/Utils eef31c95 Iterator bug. 1b4a3076 Null pointer if K-Cl interaction parameters (b0, b1, c0) were not defined. 2dd34675 Tony's fix for erm (enrichment factors). New test case erm. 72352f53 mu=0 in one of Kinniburgh's problems. 2fcfd8e7 Hack for bug with pressure, caused ternary to have low pressure result. e7915af7 Test case edl_species tests the function. Works, but maybe questions on thickness for Debye length calculation. 49ba8bca Working on edl for PHREEQCRM Adding edl_species for PHREEQC. f15d9155 GetSpeciesLogGammas GetSurfaceDiffuseLayerConcentrations GetSurfaceDiffuseLayerNames 37b8b3f4 Have surface species data saved in the charge class. Now need to expose it with PhreeqcRM e5a96004 Removed dead code from pitzer b9b84141 Tweaking lists. 7e2a34cc Make sure MacInnis pitzer parameters are calculated. 6de09799 Trying Pitzer lists again. c319dc83 reverted Pitzer optimization. Not sure what goes wrong. 4e07ab28 Optimizations for pitzer.cpp Change in PTEMP did not work for unknown reasons. 0e78b172 memory leak when chart was closed before it plotting was finished. ex15b was used for debugging. 7b75b1c9 removed building of 32-bit and 64-bit linux versions; added missing CMake files 51db3a15 Shrink array for SIT b157d11a Reading a dumped SURFACE was processed as a new surface, which caused a problem. 3e1e2bae created IPHREEQC_ENABLE_MODULE option ef8611e0 Switch bases for SIT. ca572ee9 Revised Basic function surf. I broke some functionality when I modified to get correct values for H and O. Reverted, but check for H and O, which uses another method (surf_total_no_redox). 383cc7f2 Estimate activities in PhreeqcRM 75cce6a2 Do not use full pitzer for all retries in run_wrapper only number 4. 9779f907 Revised sit to use lists. Runs maybe 2-3 times faster. 413fa2a7 fixed g++ warnings 8e9134e3 fixed crash that occurred when sit.dat was loaded twice 371548b5 added initializing to pitz_param ba6f7f7b fixed crash that occurred when pitzer.dat was loaded twice c6671809 Bad input for SURFACE_SPECIES and SURFACE_MASTER_SPECIES generated a NULL pointer. Added additional error checks. 18f0512b len=l for callback fortran string. 678fd85c Debugged Basic callback for PHAST mpi. 76e16420 Added METHOD_REGISTERBASICCALLBACK for MPI. CALL register_basic_callback_fortran() in InitializeRM CALL register_basic_callback_fortran() in mpi_methods phast_manager my_basic_fortran_callback and register_basic_callback_fortran PBasic defined tokcell_pore_volume, tokcell_volume, tokcell_porosity, and tokcell_saturation b255de16 length argument needed for callback. 9ca23025 worked on pressure dependence in lk_phase ab440058 Working on IPhreeqc Basic callback. Compiles with ISO_C_BINDING, but may need some more debugging. 793df0c2 error check for Pitzer parameter was wrong a6d63e33 Commented dead code related to density of water in Pitzer. 4d47ed13 limit on eps_r in utilities.cpp. ffd459c6 Added SYS("equi", count, name$, type$, moles). Equi option was never implemented. 66f047f9 default initial solution for the reaction calculation was not defined properly when using only SOLUTION_SPREAD. 544db5a2 Tony fixed problem with Peng Robinson SO2 (Marc Parmentier). Added test case peng_SO2, which should test for fix. eafa16d0 CCM only for NO_DL (not donnan or diffuse_layer). 6309425a First try at constant capacitance model. 8914d2c5 needed to check sum_fractions for zero cfa5f98e Fixed bug in SURF(O/H,surf). H and O not counted correctly. Went back to old method before it was changed to account for redox master species (Fe(2)), for example. H(0) H(+1), O(-2) and O(0) probably do not work correctly, but I think everything else will. a67f52b8 Bug with quick_setup, pp_assemblage did not compare without case. Failed in transport when one cell had Ferrihydrite and the other had ferrihydrite. c3defbf4 Fixing VS static checker warnings. 4a2f590f fixed for Keyword a9906af5 Fixing VS static checker warnings. c4bf48e6 updated for PhreeqcKeywords 82d57719 moved keywords to PhreeqcKeywords directory 8e104d54 Roughed in surface area calculation by DeClercq (sa_declercq for now). cf9fa724 added initialization code for cxxPPassemblage 1d876f55 Initialize type in NameDouble.cpp f6931e58 removed USE_GMP fcff4cc3 Fixed left shift operator error and null pointer error for R UBSAN check; Added INVERSE_CL1MP guard to cl1mp.cpp; Added test for ex10 3ad52ff7 Fixed bug, H+ was missing from SYS("AQ", ... 8262d5f2 Added diff_c function to basic. Added test case diff_c to mytest. 77aa5ae5 latest test cases, minor changes except for .pat file, which were wrong bbd0d318 Bryant Jurgens, bug with pat file for Netpath. dff3691e Made native a28ca9a3 changed all fortran string length parameters to be size_t simplified fortran wrappers for windows eb41112a Looks like kinetic names were not saved with string_hsave. Need to check again. e04d2539 Added andsurfcomplex4, USER_PUNCH was redefined without commands, caused a segv. e036c2c4 Added logic to run only one step in RunCells 8945b19b Another bug in mixing gas phase. e62cc9cb try/catch in class_main.cpp 4f60c877 PBasicStop accidently changed to PhreeqcStop at version 8906. test negexp_totmol failed. Switched back to PBasicStop. 0eb0264b fixed for bug that occurred when elt_ptr was NULL a2c90e9e changed : to ; 9f77e000 fixed some of the easier clang++ scan-build bugs f1438518 fixed some of the easier clang++ scan-build bugs fbb95d10 made catch(...) always rethrow; changed catches to const references f2a25dac copy/paste error for surface in totalize system. db91bf07 ready for next release 88c8cbee initialized local_database_file and local_output_stream 0236d42a memset not used for NDEBUG e1686910 Another try to speed up rates. 98741bbe finalizing compute_gfw using map for rates 685a4d45 Optimizing compute_gfw 9f0ee021 Trying to optimize compute_gfw. 87ad7693 Working with gas phase. Bug in calculation of total pressure of mix. Other issues when element of a gas component was not present in the system. 1c3b3965 r-found error in type mismatch of conditional 76bd0c29 Fixes for dumped no_edl SURFACE. cf02876f after Valgrind fixes 86a2b046 Clean up PBasic memory on syntax error. 9689a12d no need to check for null with delete. 660042da Changed variable from max to max_cells. Added variable all_cells to be able to initialize new cell_data. moved old_cells, max_cells, all_cells to phreeqc class. 96f839e1 fixed uninitialized values 8b6631a0 memory error on units line fd1e3265 remove R_SO from class_release_64 ccfac52c init ctor; changed success to include 0; fixed delete[] be4e769e fixed if R_SO not set c7d153e7 unused variables 04ecf346 Avoid overlapping memcpy in cl1.cpp 25508288 removed set but not used variables using gcc 4.8.2[-Wunused-but-set-variable] 38ded3b2 valgrind changes/set-but-unused vars b93eda46 Fixed leaks: SELECTED_OUTPUT redefinition, read_selected_output zero in cl1mp.cpp leak in eq_frac Basic function if file open failed, leaked ofstream pointer e4229340 fixed memory leaks. commented lines are directives for visual studio _CRTDBG_MAP_ALLOC, _CrtSetDbgFlag VarClear in GetSelectedOutputHeading. RebalanceLoadPerCell fixed for serial version, clear clock times. delete out stream and punch stream before new, RunCells also set streams to NULL after deleted. also serial version. VarClear in RunCellsThreadNoPrint timing only if rebalance cell, RunCellsThreadNoPrint VarClear in RunCellsThread. changed methods to return pointer instead of reference, Get_out_stream ~IPhreeqcPhast, delete streams if necessary. phast_manager did not delete module for flow-only. phast_worker did not delete module for flow-only. ab43d75b Had timing reversed in PhreeqcRM.cpp for heterogeneous case. 1c495583 Revised error message to give solution number, mainsubs.cpp. aa40f2e7 latest, fixed bug from R with relative dielectric 6a75a7f3 Changed calc_logk_s to use original equation. Checking for 0 gfws caf75546 Added solution number method. 0ff6f481 Fixed bug with 18O, redox states not defined in default. Also, the logic for using -isotopes data was changed so that the last defined applies to all remaining solutions. 9d05f14c removed extra comma 48ec1a38 added LICENSE to cvode files 8e81c17e Next try on DELETE; -all Fixed warnings for MAC lion operating system 9b9e34a2 git-svn-id: svn://136.177.114.72/svn_GW/phreeqc3/trunk@8721 1feff8c3-07ed-0310-ac33-dd36852eb9cd b2a5ed44 added undef SS suggested by Ripley for x86 Solaris 8b2c1a8d R problem for ex16. memcpy was overwriting the same space if i = row. c3433309 removed no longer needed R_SO ifdefs f92afa36 moved Phreeqc methods process_file_names, open_input_stream, open_output_stream, and open_output_file from PHRQ_io_output.cpp to class_main.cpp 504bcf6d Gentisic problem. H2O was defined as a SURFACE_SPECIES. cfa58371 changes req'd for R d77460e6 Added error print after each worker RunString. 112feb81 Delete all before filling module. 53e3bd59 Moved uz_bin to workers to avoid possible data conflicts. PartitionUZ updated Added TransferCellsUZ Added Add(src_bin, n) to StorageBin 3945c0d7 bug in writing error, loop limit used wrong counter. (PhreeqcRM.cpp) Saturation was not set correctly (phast_manager.F90) Added OLD_STYLE_XYZ to configuration for debug (phreeqcpp.vcxproj) Basic function equiv_frac was not handled. de0ec2ad Fixed bug with redefinition of sit parameters 0030e8da replaced printf(...) with output_msg(PhreeqcPtr->sformatf(...)) req'd for R. 3abc37a8 replaced putchar('\n') with output_msg("\n") in PBasic.cpp (reqd for R) 7c0b1106 first pass @ removing std::cerr, std::cout, exit, stderr, stdout from R version. 7359b9dc changed input_units_* to units_*. thru GetMpiTasks Changed GetIPhreeqcId to GetIPhreeqcPointer added to tests RM_GetIPhreeqcId 47eec62e Redefinition of Pitzer parameter was bad wrong. First exchange and surface species were included in lists. Second, a map used to lookup the pitzer parameter number got out of date when tidy_pitzer was called. 78a273eb All IPhreeqc checks passing on windows c843de8f Got all Tests working with MPI except the units tester. f10f5137 Save only aqueous species in phreeqc species_map. Added species_map to copy operator made mg/L default for Solution.cxx default species_save_on is false. Added some defaults to documentation added species_cpp test. 3f0cd06d Added Peng/Robinson gas parameters and molar volume for solids to keyword PHASES c43019c4 Roughing in mcd capability. 38bfd332 Modification to use REACTION_PRESSURE for a fixed-pressure gas phase. e284f116 Next fix for MCD a59b8b7a Tony's fix for MCD after unequal cell lengths for diffusion. b5e86c63 Do not dump negative entities. 01a54bdb added assert header file for MINGW-R 4922f9ef Tony's fix for unequal cells in diffusion. c9fb95a0 Tony's change to connecting graphs aee604b5 Changed read _modify to warning if entity not found. Tony's bug in phrq_io. punch_ostream was deleted twice. 063ba620 Tony's punch_stream error when Basic error. 63a9c36a changed so that 'dist' and 'time' are always doubles for R 5bbe7db6 changes to Tests and include mix in RUN_CELLS b6f96242 guarded gzstream in FileHandler.cpp rewrote WriteRestart to use DumpModule Don't call DumpModule from manager/worker, now only in WriteFils fixed DumpModule to work for serial and parallel. Uses dump_file_name with no adjustments to extensions. added LastInitialPhreeqc2Module(vector cell_numbers). Changed args in RunFile and RunString in PhreeqcRM to bools, affected RM_interface.cpp, RM_interface_C.cpp, Tests/advection_cpp.cpp In Copy for cxxStorageBin, check if destination == source. 7cad09da Only solutions, not mix in run_cells 5e788df1 modifications to STORAGEBIN, added Copy. Check for missing solution in run_cells. f9eff4b8 Use MIX_RAW in dump to read negative numbered mixes. 473ea39b Added additional check to LoadDatabase 9d9e86f8 Added next_user_number to Phreeqc. Added a template function Rxn_next_user_number. aab6347c changed EOL to native ab9b2ddd ex4 works with old flow from 8056. Will now try to keep all other changes, but go back to updated flow calculation. aee34267 Removed yellow for graphing. Changed Li molar volume in Pitzer, Amm, phreeqc.dat Added test case Vm_LiCl_sol 1e48b85c Removed error trap in write_mass_action_eqn_x 6f849b21 More changes for Marco. 2a37e806 Do not set master_bsearch[i]->total = 0.0 in clear() for now. 9048a9be Bugs fixes noted by Marco De-Vroed e9b05399 Clean up initial_data 4ec2784a Tony's fix to solution volume. Added test case, updated Makefile Added comment to Release.txt 7c40b44e removed all refs to MERGE_FILE, MPICH_NAME, GZIP_RESTART, and HDF5_CREATE ifdefs (except HDF5.props). 57014d0f Renamed Reaction_module to PhreeqcRM 23deef14 Revised GetDensity to retrieve from workers. Also, density was not saved correctly with the solution (mainsubs.cpp updated). InitialPhreeqc2Module had an id argument which was not needed. a3110d47 Should zero soln_vol And missed calculation of patm (pressure). 11de0894 removed unnecessary erase to fix bug in IPhreeqc a191ef4b Still working on selected output. 70482839 Added GetMpiTasks ab5384de Tony's vm revisions, phreeqc.dat gfw for N redox states c80ebbc6 added soln_vol to cxxSolution. 634211a2 added SelectedOutput and UserPunch to automake be614498 added const routines fda47b4b recovering tortoisesvn deletion of UserPunch.h (i think it's a problem with externals (was working on iphreeqc)) 8c78708f added const versions for Get_headings and Get_rate 257b2805 fixed reset now acts like setting as-is vars 314db2a5 rearranged vars and added code to track changes 5edcc20b added assert for phreeqci b956c837 fixed memory leak 9d1f0e34 set unknown state just in case. b3d02ae5 Added river turning into drain in IPhreeqc version of PHAST c1687e21 updates reqd for vs2010 4983afc6 Got a request for interacting sites, such as this surface complex. d3ed6904 updated to allow charts to stay open in phreeqci 7c66a50b changed back to using high_precision bool for diffc and disp in dump_cpp 39abad33 makefile's to run test cases 92a78618 Added -high_precision to PRINT for USER_PRINT. adf7d984 Merged revision(s) 7863-7986 from phreeqc3/branches/multi_punch: Branching at 7862 for multiple SELECTED_OUTPUT, USER_PUNCH ........ Have multiple SELECTED_OUTPUT working. b88c4901 fixed for phreeqc3-multi-punch; passes all unit tests ab6875b5 solid_solutions had extra s in selected_output headings 32b9285d changes req'd for multi_punch a99ef8b3 changes req'd for multi_punch 43f5baf0 Tony's latest changes, including F1 and F2. 695d6d4e Tony trying to fix v_m in model.cpp 2c6f8c22 working on multi_punch tests for IPhreeqc ec4fa77c changed print.cpp for Molar volume when not PR 25a99075 Tony's rewrite of tidy_gas_phase, still pretty ugly. a6f82d99 minor reformatting 9069b4a8 logic for headings of selected_output 1; logic of opening selected_output file 1773d7e5 removed inline from Set_file_name 9faaee66 fixes compile error on linux; changes made for TestIPhreeqc::case2 ee4cd5ec Next try on tidy_gas_phase 2abb2f22 Revised tidy_gases. Structure was wrong and j was used in two loops. 6eaca680 all tests passing for one selected_output file; may still need additional tests for TestIPhreeqc case1 and case2 59b456bd Change in store_tally_table to try to line up with Fortran. b18d2ff0 Guard namespace. 8609b3df Added row ntally_row + 1 that contains the moles of each reactant. a49f540e Tony's wording for fugacity 30164dc7 functions are fcn ca3e1128 Initialize basic_callback_ptr 1e8a8c03 Update RELEASE.TXT Added print about fugacity for SI. 88742cdb Fixed post-build in project 3682cff0 use reset -false by default for SELECTED_OUTPUT n != 1 305cd863 Revised name to BasicFortran 346cf223 Added a Fortran callback. 04141a30 Still working to make backward compatible. 62feec3f Making backward compatible. 8474b535 dump precision ce4284a5 working on high_precision, using SELECTED_OUTPUT 1 27b3c056 Working on multipunch d436c9cf bugs linux caught, something different about when to print headings 6fd2cc8b Fixed missing -pressure in solution dump_raw and read_raw. 1afe3e43 Fixed up inverse.cpp a bit. 38934142 Think multiple instances of both SELECTED_OUTPUT and USER_PUNCH are working. 276322a9 Have multiple SELECTED_OUTPUT working. e9202575 Caught SS definition errors if 2 components not defined. d26571fb Tony cleaned up XI in Pitzer. 79698b01 Tony's fixes for Pitzer, High Pressure is still not right, will require more fitting. 7da9c7b1 Merge Tony's changes. 6b8ccd82 latest tests 7837; some Linux warnings in src 7848242e Fixed error for malloc introduced in recent Basic function. Fixed error with Pitzer paramenters that were not recalculated with a change in pressure. Parameters depend on density of water I think. Also changed SIT, although I am not sure the parameters have a pressure dependence. a03d2ae8 Added f = EQ_FRAC("AlX3", eq, x$) 4b643b86 Finishing touches on species_formula. ff764f3f Added species_formula("CaOH+", count, elt$, coef) returns Ca 1 O 1 H 1 charge 1 in elt$ and coef. count is 4. 7d90df4e Pointers for MacInnis scaling were not reset when -B0 was redefined with Cl- K+ order. Now check for either order. 6a0b9146 change req'd to compile in MinGW 6b36a705 Lost break after tokstr_ case 102ce319 Revised manual: Figure 8 had part B twice and was missing part A. Fixed in Version 3 in previous check in. Fixed in doc. Fixed in HTMLversion examples.fm, and reset cross refs so all .fm files changed. Replaced phreeqc3-232.gif with revised figure. f1a98f8c Decided to widen fields if necessary rather than print *** for STR_F$. 56ca1c2c Added Basic function STR_F$(x, w, d), where x is the number to format, w is the field width, and d is the number of decimal places. f2cccb00 Isotopes moles and molalities were not correct when mass of water was not equal to 1. Added test case 14C_isotopes; added to Makefile. 87fea869 Tony's changes 6/1 for pr. Set gas volume to zero if fixed pressure and no moles of gas. Add test cases low_t (minimum mu), modify_zero (set total to zero in SOLUTION_MODIFY), and volume-zero (no moles of gas in fixed pressure gas phase, volume should be zero). 8fdb7313 fixed bug in DELETE reported by laurin.wissmeier@web.de 2013-06-01 037b2bb8 added Set_append 6ab5932f Removed lower limit on mu. At low T, Kwater can be much smaller and pure water has lower ionic strength. 6c4f7253 added some const and added cell for use in phreeqci 145fcc00 vcxproj has some change. 701878af If total set to zero in Solution_modify, took log of zero to update la and failed to converge. Added check not to update la unless both old and new totals are greater than zero. a52fc625 added get/set StorageBinList method 26614f70 minor optimizations of tidy_model 0b7b5f7c added pointers for solid solution and ss component in unknown struct to speed calculations, all test cases run 11ef24eb replaced Find with pp_assemblage_comp_ptr stored in struct unknown f127add4 Default_pe is now const char *, saves some string_hsave in clear in prep. d7321bd3 skip inverse in no inverse defined. 96285f22 put back piece in molalities that was necessary after all ba25fabd optimizing surface references in molalities. ed46140f Remove strcmp from calc_vm, calc_delta_v, calc_lk_phase c176fe3b Cleaning up code. c9c8a7ac Fixed volume print for -gas in SELECTED_OUTPUT. Added Gas_FixVolume test case. 47d0c62f Added new Basic function kin_time, which is the time interval for the current kinetic integration. d71ea17a Added logic for rate limiter, but not used. 8e3394ca fixed checks for successful opening of std::ofstream 28d649ab fixed for input file having spaces b23fb304 still working on print of saturation indices for gases 0607e086 Fix print of PR gases in saturation_indices. 69112597 CLOCKS_PER_SEC 24ed8b5c Tony's changes for gases. e60d708c Some of Tony's changes that I missed. f73a156b revised cvode is working I think. Had to be careful when updating m_temp and use the saved solutions. 0cb1337f added patch from tmishima@jcity.maeda.co.jp for multi-threaded IPhreeqc 2c9611d0 tony fix to print 20130425 ce037cb2 Fixed gas_phase problem. total_p was reset for fixed pressure in a couple of places (prep and tidy). 4cb5d32f Added phi to gas_phase print. Test case gas_phase_pressure 40e97fd9 Fix to the incremental updating. f uses saved pp_assemblage and ss_assemblage. 6917e9be Error, used last_good_y instead of kinetic_y at final step. 4733f935 Working on REVISED_CVODE ifdef. 5330549e Revising cvode dc977aee LDBLE for SScomp Changed sundials to use LDBLE d5fa02a7 a little rearranging of global_structures and under da493bbb One more fix for gas_phase (new_def) 8696224a Bug with gas_phase_modify. 28e082bc NameDouble dump failed with string greater than 21 characters. 31ebd3d3 LDBLE fix c3bd2eca LDBLE fix 0d6e0bc8 used small activity water step for pizter, sit 6a9624d2 ah2o_x should be set to zero. ah2o is then summed from solution or mix. c42bd892 LDBLEs 7bb4d371 cvode MAX_ITERS 40 hnew = hg predict_fail CVMEM cvode_error == TRUE || predict_fail after CVnls if (CVMEM cvode_error == TRUE) after CVNewtonIteration CVDoErrorTest exit(4); 2ba41a62 More fixes to saturation index. 82fc3b7e Reverting from Tony's fix to my fix for saturation index of pure phases with pressure. acd7d32d Fixed bug in SI of pure phase assemblage that did not account for pressure (Tony's version). 6899bc4b fixes bug with cd_psi in model.cpp. 9849ed1a added Setters for time params ebe3cbf7 SOLUTION_MASTER_SPECIES was not defined correctly. Fixed Null pointer. Abort if input_error in build_model. a2a1aafe PBasic use phreeqc_ptr warning msg instead of default phrq_io warning msg. Fixed bug in s_s assemblage print 1342a5c5 Alternate method for sum_species 3461797c Copied Advection and Transport data in InternalCopy. 27e3f75c Optimized sum_species. Keeps lists of species in a map > sum_species_map. c6261d5f added GetAllItems method - with fixed history 342bf39f trying to fix history dd104e8d trying to fix history b44c54f9 for some reason TortoiseSVN deleted StorageBinList.h on checkin bdadfbeb added GetAllItems method deace44a make IPhreeqcPhast and Reaction_module a friend of Phreeqc. 85528955 more testing of copy operator. 916ccdaf Working on copy operator. 6412a827 Forgot to copy sit_param f38caf06 Lost revisions to model.cpp in reorg. fc1c2b11 class main to test copy. 04741427 Working on Phreeqc copy Running ion association model with equilibrium phases Running Pitzer. b3e59d93 has _DEBUG #defines for extended ascii/ANSI character set to avoid assert. c9380090 There was a long gap before this was checked in. a1d93167 Tony's changes to the status timer. 4cb54559 added some UNREFERENCED_PARAMETER; removed g_spread_sheet 2628e3aa added NOTICE to configure c8c26e81 Tony's new Pitzer test cases 67c08b44 Fixed activity and gamma Basic functions to include EX and SURF 1199a003 merged branches/reorg -r7307-7330 into the trunk ea2bd445 working on 3.0 dist d759535c check for zero before log10 11b00551 tweaked changes to make zn.test run on linux 801d7ed3 Linux changes d23bd01f Changes for Kinniburgh's underflow and overflow problems. 7ef536ff working on linux dist 662894ff renamed GNUmakefile to Makefile bb2ea3d7 updated dependencies 9976920f updated dependencies 709e1176 modified source files for rearranged directories 564a0f8a moved phreeqc files into src 0daf341e Use PHR_ISFINITE b1241f43 check exp for isfinite 2832b3ad Flushing error file. a42ef3aa updated for COM 2109cc8f Added active_charts to ChartHandler. Incremented when form starts, decremented when form ends. e765dc43 Updated makefiles for parallel testing. bd446838 accidently checked in 2-digit exponent bd10ce02 Fixed memory leak for CD_MUSIC af148da6 Freed s_x by mistake 6f221185 Changed Rxn_new from vector to set. f74bc444 Solution_spread did not set Rxn_new_solution b75ce1b7 Fixed bug with _modify. Did not get correct n_user! c2f8b3bb Saved list of newly read entities (including _raw and _modify). c0a8627f fixed all System::Threading::Interlocked::CompareExchange and added some debugging code 65c2d528 Tony change of numtostr. 5852b6a3 added check for gmp.h and -lgmp 21801d62 added stdio.h 58fdf968 stdarg for fpunchf_helper 1f52fff8 Kinniburgh found bug in surf_total. Made a mistake when allowing redox states. Added test case surf_check.out ed6ad167 fixed hidden var declaration 378f2ff7 fixed buffer overwrite in fpunchf; updated unit tests for phreeqc3 31efde46 working on configure f6498fee DELETE; -cells #no list of cells Causes all reactants to be deleted, same as DELETE; -all cfdbf58d new def removed for raw Gas Phase and Surface. 6ac877e7 Step, problem mu requires ah2o to be 1.0. d7b2ab1b Fixed spacing for PRINT command high precision. 283ff2df Tony turned off PR in mainsubs.cpp af6f13be changed read_delete to reuse StorageBinList for each call 03b0b884 Remove Dictionary, Linux changes 9d4bb2e1 PHREEQC should now have no USE_MPI 63a1879a Finished zone flow chemistry write for MPI. 98baecfe Added two new functions. e13a4d80 Think rewrite of rebalance is working, need to fix zone chemistry file for mpi. d4721e49 Wrote rebalance_load for MPI_Bcast ed73e17f Working on copy constructor. Roughed in Pitzer, need to do Sit and TRANSPORT. Test e3638b34 Revised phreeqc constructor, init, and initialize. All data members are now initialized in init. Almost all allocation is in initialize. There is a little bit of duplication in initialize, but not much. 03d70eeb Set pr.headings true in class_main.cpp pr.headings not reset in do_status. pr.headings not changed for IPhreeqc call to do_status. d13ffaa6 Fixed Dieke's Viet Nam problem, la was limited to -300, now can go lower. 4c2e8c33 Modified to tidy exchange, gas_phase, ppassemblage, solution, ssassemblage, and surface when _raw or _modify are read. Set new_def for each. Had problem with equilibrium_phases as shown in eq_phase_mod test case. Probably will add amdtreat test case from which it was derived. d0d48fca added additional initializing code to g_spread_sheet 475cf8f1 added some (parse_all) logic to avoid crashes in phreeqci 83f7c0c4 Adjusted convergence for small numbers. 3503962f bug with saturaation curve pressure. e1ef00c5 Tony's changes for dates 3b42a98f (int) c352283a Updated to Tony's latest. Amm.dat and Pitzer.dat have extra terms. d96dc80f Runs ex4 correctly. fb36b693 Deleted dead code in reaction module. 5e62aeb2 Runs, getting xyz file right, checking results. 881ca8f2 debugging and optimizing 8b0481a1 Working on efficiency of updating Solution.cxx Need to revise solution_modify. 31c1ae86 Optimizing activity update e5177c60 void null pointer for master 1a1950c4 Added total_h and total_o to tally table. 545dbd10 Added some status_on = true in status. 1f8d8608 fixed location, resizing, tab order. baf45663 Added second logical in batch. First is color, second is grid. 54ec7c4a Removed toggle hints and toggle background Added Chart options... a7acb6ff added trys between System::Threading::Interlocked::CompareExchange and System::Threading::Interlocked::Exchange d3a8fab5 added const version of Get_pressures; added reaction_pressure to error_msg list; 0440b0c7 Think restart files should work, but cannot test yet. 1f52826b Have initial conditions working. c0463f6f Does initial conditions, but is really slow. c6c27b03 Allow negative numbers on entity mixes. 60151014 Fixed bugs with mixing calculations (keywords not set up right). 06cc39e8 Added new keywords to mix SOLUTION, EXCHANGE, GAS_PHASE, KINETICS, EQUILIBRIUM_PHASES, SOLID_SOLUTIONS, SURFACE e20fd6af modified to use auto_ptr for early termination memory cleanup aa903967 bug in totmol(water) ad3e78e2 Added Basic functions GFW and SOLN_VOL. Added test case gfw in mytest and added to Makefile b0b007ca const static vopts 065f7ea3 BUG FIX: runs that threw didn't close the input file(s) and do_status was never called. a826fa57 Added Update method, removed unused prototypes. 87c00324 Avoiding null pointer. 64024732 Don't print chart detached unless there are charts. 81521287 Tony's fix for reaction in transport.cpp Used Sleep(5) instead of Sleep(1) throughout. Removed timeout on detaching charts. It will just hang if something goes wrong. 3295cd5b Deleted ifdef skips f069c8e5 renamed c files. b664e431 Removed unneeded declaration. 0ca96543 Reversed logic to NO_STATIC_VOPTS, default is now static vopts. No changes to Makefile or project needed now. 8823f0ff Added STATIC_VOPTS to make vopts static, not static unless defined. 66f3b9b8 PHRQ_io::error_msg now throws on STOP 01495bfb changes reqd for merge version 782106e4 Tony's changes 232bb18f Added time unit to advection, kinetics, transport, and run_cells fc2d4425 Added SOLUTION_MIX m-n keyword that mixes solutions without doing a full reaction calculation. Results of mix are stored as SOLUTION m-n. 77b082d0 Fixed handling of pressure for solution_spread. 7413759a csv to tsv 2222bd96 Added check on residual > MIN_TOTAL for MB equations. 5c551753 MMS friend added f45f490e Trying to optimize calculate values. With isotopes, all the isotope ratios and isotope alphas are kind of slow. bd328e58 Revised method of calculating equiv for EX and SURF in tity_species dcc27441 Calculate equiv for surface and exchange in tidy. a8b09b7a tsv updated in code. 50907986 Updated tally table to include Alkalinity and secondary master species (solutions only). e55ff96b Tony's latest water properties in utilities.cpp. c1886eb2 fixed for phreeqci (basic renumber) modified listtokens to put single quotes around strings containing double quotes d369d0f7 added __cplusplus_cli defines for methods that require CLR for faster linkage with USER_GRAPH editing in phreeqci 692cd611 added const methods c9dfe616 Rewrote -batch of USER_GRAPH to handle filenames with spaces. 0fcfb42a Cleanup of temporary code 4a8e2ca9 fixed heap error: 6dd55677 Added -batch option to USER_GRAPH. b984ef5a Tony added function DH and Qbrn. 1510698e Write image without red hints in boxes. 94460826 Added toggle background. 693e36ce Tony's changes through May 16. 0eef1187 Modified for new handling of surf related to phase. 3f982ac5 Don't check zero charge for CD_MUSIC master species in surface related to charge. d07b45cc Revised logic for surf related to phase 204108a6 Latest examples 07e9545f clearloops after basic_run to avoid memory leak. PBasic.cpp. cbba2c24 added const versions for ChartHandler/ChartObject made ExtractCurveInfo static in order to use in phreeqci 6a865f65 fixed minor assert 2e6e2bc3 re-added malloc_dbg for string_duplicate 54c328a3 equal_increments synonym in Kinetics. Increased max_tries in mainsubs for responding thread. removed extra calc_dielectrics in prep f1e2ecdd Tony's changes 20120423. 2ca408ed updated for linux (added headers) added build dirs to ignore list fbe3108b removed Phreeqc_class.h from dependencies 74c97b0e removed dependencies Phreeqc.h and Parser.h from Utils.h aa8047e1 minor formatting a4d8672a made get_line and get_logical_line virtual eace3df0 removed Utils.h dependency 59016d1c added GNUmakefile which rebuilds dependencies during each compile the dependencies are stored in each configurations .deps directory 3eb956bd removed dependency on phrqtype.h by using a template for addPair removed unnecessary Phreeqc.h 3d2e4ae4 Removed Phreeqc_class.h. 8e0844ed Fixed memory all memory leaks from mytest. bf96306c output file was closed before free_all, no messages appeared. 9c1a63dc Bug with key in pitzer parameters. Needed type, in addition to ions to make the key unique. a718706e fixed string_duplicate leaks 67b089f8 changed ifdefs for string_duplicate 2f10f2e7 fix for level 4 warning level 495a2200 merged in http://internalbrr.cr.usgs.gov/svn_GW/phreeqcpp/branches/ErrorHandling 6269-6445 e972c511 trying to recover Phreeqc.h w/ history this should have been -r 6391 969e5df5 trying to recover Phreeqc.h w/ history cce0fe25 fix for level 4 warning level 07fdb2cb added memory cleanup code 4d8f7893 added memory cleanup code ac861bde replaced WaitForSingleObject w/ PhreeqcIWait; 4309bd45 minor cleanup of reading code 6167acc9 sped up pitz_param/sit_param reading; added memory debug code for string_duplicate a512faaf setup for ERROR_OSTREAM ae27e8a3 added init code ae7c893c added initialization code 6a921663 const modifications 828eaf16 const modifications 0e640f45 const modifications 733d093f const modifications 8e9a4e0c made istream_getc static; made some routines virtual; added virtual getc method; 8ea8082f added const version of Get_pp_assemblage_comps 7347290a const modifications d2577f8b const modifications 82d49974 const modifications 02460252 added const version of Get_SSs 0574952b added const version of Get_temps 33eabd03 SOLUTION_SPREAD 7c3f0b5b changed implementation of trim_left trim_right and trim. see http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring 69711a42 made iso_defaults public and static 96294247 trying to fix loss of history with Phreeqc.h c98ac9ac trying to fix loss of history with Phreeqc.h c8c82a99 resurrected Phreeqc.h; Don't know how it was deleted 3df08dd0 made iso_defaults public and static 1ad10142 added serializing code for selected_output file name 54d517d4 added const versions 4f70c394 added const versions 4b5f2692 updated to use windows malloc debug c892ae27 BasicObj fd26a7ed fixed memory leak caused by duplicate numbered solutions (see ex12 -- solution 0) (previous fix was just a band-aid) a35fb059 fixed memory leak caused by duplicate numbered solutions (see ex12 -- solution 0) 91f650a5 Merged ErrorHandling 6119-6268 changes. All reactant structs have been removed. Tony's pressure uses mu in pressure term of log_k. Test cases run, discriminant check at 1e-8. 20b360fe modified for warnings in 2010 50ba9dbe Made minor class of valrec 5f058aec memory initialization for cvode 23121d78 Initialize PBasic structures. 02f2e253 Fixed bug in jacobian calculation for Cvode dd06c2ea Runs most kinetics, kinsurf is a problem. 18906cdd Removed skipped code. ea54bb8f reworked logic for using previously read line from parser. 82bd21f4 Running Linux with new SS bcdeaf22 Tony's 17Jan2012 changes to gases, prep, read.cpp 02ece436 New format for SOLID_SOLUTION_RAW. d051a777 compiles and runs solid_solution test case. 2673f81b Working on ss 6bd23b9b Rename item from http://internalbrr.cr.usgs.gov/svn_GW/phreeqcpp/branches/ErrorHandling/src/rename_cpp to http://internalbrr.cr.usgs.gov/svn_GW/phreeqcpp/branches/ErrorHandling/src/phreeqc f5b51e1d moved -r6061 http://internalbrr.cr.usgs.gov/svn_GW/phreeqc/branches/rename_cpp to http://internalbrr.cr.usgs.gov/svn_GW/phreeqcpp/branches/ErrorHandling/src/rename_cpp 549ba5c2 Jonathan Toner changes for low temperature pitzer included as comments. Change when his paper is published. 875a5f1a floating point exceptions on Linux. f108e75a long double mismatch 76839828 changed all double to LDBLE 9aef1acf out of range subscript in get_line 93aa3317 n_mix_user_orig setter bug. 765f7b8d Linux Makefile 765d5413 first bug with use 568a2ab7 classified struct use 4f0fb6c9 Finished ss_assemblage rename. cbd736da Renaming SSassemblageSS to SS and cxxSS f0855460 Renaming SSassemblageSS 619e7824 Tony's 1/10/12 changes 89153db8 turn off warnings for dll linkage for std::list 3458661f bug with case for phase name in surf_related_pz 2590f549 Bug fixes for pp_assemblage 48639132 pp_assemblage is mostly working. Need final checking ff5fc98a removed extra qualification cxxGasComp:: on member multiply 1ed24bf1 added GasComp.cxx to unit project; fixed typo in GasPhase.cxx e295db8b New files d0c049ed windows log functions passed 67eb8271 Tony's updates relative to 5941. 9758c574 Last of SKIPs 7257281b Fixed bug in read_gas_phase_modify, appended components already in list. 18eba6bd cleaning up bugs with exchange 90934690 started removing SKIP 16e00762 eof handling in cleanup_after_parser. 15dbbaa3 New Makefile. acf62c78 gas_phase and irrev removed. ddafdd17 Added missing members to dump/read _raw 97a38235 Made const char * b52ab4df Exchange changes have been made, but need to debug. 02839d60 Removed struct mix. 4bb72929 Change count in dump_raw for temperature. b5c652e2 Removed struct temperature. Uses only cxxTemperature. a05de5e5 Removed old hash for strings and replaced with standard map. a8ad8258 hsave_string returns const char *. 2095c5bf Linux changes e0ef4941 changed error_string to const char *. 70eb5e27 Revisions from Tony. 84644301 added force_numerical_jacobian 28534b50 removed REVISED_GASES from Makefile. 9fa2b74b Removed gas_pressure unknown. f7518987 Modified to run all fixed volume calculations through REVISED_GASES code. 8107f787 have old activity of water and correct tanh version of activity of water. 267b5934 Incorporated Tony's changes for -vm fe15de34 Fixed long heading problem from Kinniburgh 8a8547be Think fixed volume P-R gases are working correctly with numerical derivatives. c46e4876 Added pr_in to C++ gas_phase class. 4ed743d0 added Set_file_name method for use in IPhreeqc 36bf6b7f Compiles with and without REVISED_GASES. 1715e7b4 Numerical derivatives with REVISED_GASES works with the P_Vm test case. 877f19e6 define bool slack to allow a slack variable in array (default false). 88dc8b96 Changed activity of water equation to use tanh to allow activity of water to approach zero smoothly. 57620590 sync'd warning_msg with error_msg 2211621f istreams moved to phrq_io a4c8e993 moved istreams from Phreeqc class to PHRQ_io class changed modes std::ifstream:: to std::ios_base:: deleted some #ifdef SKIP c3d019a0 Fixed action of dumper. Retains information until next read, only changes what is read. A DUMP keyword with no body will repeat current definitions. 8cbaba8e Converted output to streams. b260023b replaced dump_file with dump_ostream 1f7e4e16 added IPQ_DLL_EXPORT for IPhreeqc dll versions 6fb5faa2 added PHRQ_io member 23f209d9 added guards for phrq_io 6bca2105 Linux version of all examples for version 3. 2e64a171 Minor fix to REACTION_PRESSURE e803c8db Fixed but in TRIM. 2887acf8 my bug in read dumper 0a98983f Think reaction_pressure is done. 23a80097 updated for g++ 4a7158c4 Getting there with adding REACTION_PRESSURE capabilities. 8a678fa9 removed ../ from include 99b8eac5 Have read_reaction_pressure and read_reaction_pressure_raw working. Dump reaction_pressure raw also works. 0f6547cc IPhreeqc passes all unit tests on VS2005 ae868f0a Got reader for reaction_pressure working. 90f30845 Added erase basic function. 2c80bb5f Needed header file on Linux? 278ce5a9 New keywords files 17a4c844 Moved keywords to separate class 584b79bd roughed in cxxPressure class. A lot more to do. 041db991 Removed keyword hash. Added enum for unique keywords, static map of phreeqc keywords, map of keyword names. 02119ae8 Fixed but for David Kinniburg. 03bc3d38 Going to try checking cl1 results for inverse modeling. c552aab8 updated reading for istream cookies removed PFN_READ_CALLBACK 14ef22ef reordered calls to calc_PR in prep.c. c5a5c33d printing out pressure in examples. aa77b78d Tony's changes to gases. 5ec591a0 Update to VS2010. b160c3da Header file change 06ef90e5 initialize basic_interpreter 79b256e4 added code to handle IDS_* added code for HANDLE #ifdef'd the WaitForSingleObject 4268bd93 Merging Basic1. 2a8089a8 Merging Basic1 from PhreeqcI into Basic. 9d1dc096 Made PBasic commands static, initialized only once. 46b50f49 Made NameDouble use LDBLE eb2fab2e Indented last try/catch. 97caf60b Indented Phreeqc.h, read_delta_v_only 9c73b964 Needed to merge two read_delta_v_only 4c938e63 Crapsi changes 29276b98 More cleanup of PBasic.cpp Removed PASCAL_MAIN. Removed Anyptr. 7271b9d5 Replaced TRY/RECOVER with try/catch c20d3ca2 Latest version (5757) with fixed up PBasic (std::map). 122f2b87 Cleaned up PBasic some. f8d1baf0 deleted obsolete QBasic directory ff2d48f1 Going with new PBasic. 5930d762 Changes to set_pz and set_sit. Avoids setting lg = 0 for the first attempt in set_and_run_wrapper, but then sets = 0 for additional attempts. 579d5a3f Fixed up PBasic for Linux. 27168dbe Has PR calculations. 40f0539e PBasic files. 516bd2ea Added PBasic with #ifdef PBASIC. 7f18c67b fixed bug in phqalloc.cpp. f8887170 Implemented Tony's Peng-Robinson changes. Need to run his test cases. d2c28c17 safe_close now takes pointer to file pointer fdd1cee2 Phreeqc_class.h case in #include statements. Fixed warnings and errors in Unix. Updated Makefile for class only versions. 2efc1ee2 Rearranged header files. 7e0ae150 Fixed up eol for error messaging. cc03f8d1 QBasic 21da7bae Renamed output_temp_ to output_. 38373629 phreeqc_class header needed. 980e6802 removed setjmp except in Basic. 95aa3278 Removed phrqproto.h from project and svn deleted. be8d41f2 removed MERGE_INCLUDE_FILES some static and extern unused static maps de377b66 CLASS_STATIC replaced with static STATIC replaced with empty 9eb1ba4e Removed EXTERNAL, PITZER_EXTERNAL, and kinetics.h. e0cbcff7 CLASS_QUALIFIER is gone, svnid gone removed dead code from PHRQ_io.cpp f1e5f4c7 About to do CLASS_QUALIFIER 7e26eec1 Last checkin svn deleted obsolete files output.h, output.cpp, phreeqc_files, main.cpp. 601d8515 Removed PHREEQC_CPP and some PHREEQC_CLASS. 05495494 Removed output.h throughout. 53a00d4a Finished fpunchf. 6c2f0b5b Did OUTPUT_PUNCH. ccc05114 Accidently had wrong PHRQ_io.cpp on checkin. d57af053 OUTPUT_MESSAGE removed, need to check a bit. 92d8a3b9 OUTPUT_CHECKLINE and OUTPUT_ECHO done. efe04692 OUTPUT_SCREEN and OUTPUT_LOG are done. fbaa4906 Done with OUTPUT_WARNING 3630733e Will not compile non-class versions! c0cc6d8b Removed OUTPUT_DUMP. e2e3ba09 Renamed methods and added _msg to output (_temp for now), punch, error, log, dump. ab425548 Beginning to remove enum OUTPUT_MESSAGE, OUTPUT_ERROR, etc. ecd1d9c6 Working on interface for run_reactions. 53400537 Cleaned up kinetics.cpp. Changed io in Parser.cxx a little. 1fb2150c Removed OUTPUT_CVODE, OUTPUT_STDERR, OUTPUT_BASIC, and commented OUTPUT_SEND_MESSAGE. 79541da6 Removing struct system. Not used. cbc2c210 Added ptr_to_user for mix, irrev, and temperature. ee9dcce4 writing cxxStorageBin2phreeqc. 6e4445fa Working on reaction module and RM_interface. 03dcbf8e Added Simplify_totals method and Update method to aid in PHAST implementation. 7ccdb161 removed dead code, mostly P_INSTANCE and PHREEQC_PTR 58c0e3f7 Added optional argument to dump_raw to a different user number. fe9f3144 Replaced modify_activities so that PHREEQC instance is not needed. 5b39b2f7 NumKeyword reads negative user numbers. a7aed179 Fixed segv with GAS_PHASE that had gas component with element not in system. d0075d7c Converted phrq_io to pointer rather than an instance. e89dd593 Removed SKIP code in PHRQ_io and Parser. 6fa41ac0 Rewrote PHRQ_io. Removed handlers. 4d323147 Changed longjmp to throw for PHREEQC_CLASS 9201846c Added isopen_handler 9fe532ea Cleaned up Get_ and Set_ except for parser. bf84746d Converted last StorageBin to remove P_INSTANCE. b18bda26 Working on StorageBin. Moved 3 routines to structures to eliminate P_INSTANCEs. 6acf05a1 Removed ORCHESTRA 4d728afc Removed MOVE_TO_STRUCTURES ifdefs. 9efcd6ec Finished all but loose ends with P_INSTANCEs. 9561576a Finished SSassemblage.cxx Cleaned up other files some. e42590e5 Cleaned up NameDouble, still need to remove elt_list. 62a55134 Worked through solution removing PHREEQC_PTR_ARG. 02ab5c20 Finished PPassemblage and Reaction P_INSTANCE removal. 2dab6f8e Removed PHREEQC_PTR_ARG from gas phase and temperature. 06f4e08e Finished with Exchange removing all P_INSTANCE I think. 7c991319 Worked through kinetics to remove PHREEQC_PTR. 12d62e8e Removed P_INSTANCE for error_msg, which removed entirely from some methods. c2987e2d Think ErrorHandling reproduces original results. b7729cf2 Changed all check of input_error to get_input_errors for phreeqc. 0cf22bd9 Class_release and Release compare with phreeqcpp-trunk. 40bb6533 Fixed bugs with end of lines for status, warning msg count. 50b369a2 Compiles all configurations of phreeqcpp.vcproj 11da9fec Worked on output. Added output_string to PHRQ_io. 7eeeab45 all classes inherit from PHRQ_base, except f6ed50af Added base to kinetics, mix, and exchange. aba47964 Added file_on variables to PHRQ_io. 02797e69 Added PHRQ_base and PHRQ_io to project. a57c89d7 New version of parser 70d290b5 Removed headers from header files where possible. 79e978a7 reverting Tony's change. 978f1515 Removed unnecessary header file ISolution.h 00430524 Modified for "+" sign; makes Fe(+3) synonymous with Fe(3), for example. 3be93e8f Renaming Form2 to Form1. ae170dc3 Removing CHART compile option, replaced by MULTICHART. dc45fa36 Tony changed csv symbols. 3d989d5f fixed compile error in g++ 4.4.3 7d523c89 fixed compile error in g++ 4.4.3 c43921fa fixed compile error in g++ 4.4.3 68b9c041 Sorted results for list_s_s c9b1781f new sit.dat database. cd41ca95 Removed some header files from phreeqc.h. 4e02d925 Guarded ChartHandler.h in Phreeqc.h. 865cdad9 missed list_s_s prototype for class 2da02dd0 Updated revisions for revisions and PHREEQCPP-RELEASE.txt 646207fa Added phase_formula function. 2c906a06 Fixed missing reaction_modify in Class version a36a7924 Worked on bug with DUMP and read_raw of isotopes. 67258ef6 reading with parser differed from reading from stream, and did not handle missing id number correctly (NumKeyword.cxx) 2b117727 Rewrote run_cells to do all of the calculations that would be done if a series of USE and SAVE. 41a6354f Added example 20 to examples.fm. db5f8e6a Removed dead code and dead logic for adding series. 72298ad6 Simplified logic, but left old commented. c3230634 Trying modification of logic of user_graph 1ac4976f Latest output for test cases. 24eabcb5 Couple errors in parsing chart parameters. 1238db91 Made fixes for Tony's 5/06/11 zedgraph tests. f2fcaea1 Revised for Tony's comments. CSV colors have separate default_color_csv. Added -active. If csv for graph_y or graph_sy, no symbols. 2d59bd8c Worked on logic for Tony. If headings are defined, don't remove Basic code. If no definitions in USER_GRAPH, remove Basic code. 5af51e0c May need to free rate even if Form has not started. 99f7eab6 Had duplicate chart_start code. 2a491b71 merged MULTICHART branch back to trunk 8060c065 Removed chart files that were accidently checked in. eb2227ac Before reloading from home. cd87eed3 Added some LDBLE, particularly for pow(). c5fc6bef Added version/date replacement strings 13910c9b moved and renamed revisions to src/PHREEQCPP-RELEASE.txt 27bc1114 removed old revisions from phreeqcpp/src 16c2689a Fixed adjustments of activities after solution_modify. Better treatment of redox states; bug in get_total_element. 1e8147c0 Added note on automatic modification of activities after SOLUTION_MODIFY. e746bb8e Added code to adjust activities of master species in solution when totals are adjusted by SOLUTION_MODIFY. 4419cb42 Changed output to output_file. cb4073bb Added Tony's ceil and floor Basic Functions. 8ad0987b Added ndiff option 89f399c2 fixed uninitialized variables punch.new_def, status_timer, count_strings; Changed #include "time.h" to #include 5ac33b0f Cleaned up some more memory leaks reported by Intel Inspector XE 2011 (Kinneburgh) e3ec792a Tony's changes. e2c2949e latest phreeqcpp f3f50f02 Added EOL$ function. 6f9a13b1 Redid include as include$. e0ed0a61 Updated project to include chart icon in managed resources. 3e8e7927 Some spec cond diffs. d6d3c223 Initialized solid solution in read.c 75e0df6e Through REACTION_MODIFY and REACTION_RAW. 7a46255d Eliminated requirement for equal_steps in kinetics_raw read. Thought this would make it more backward compatible. 8b69f05f Added missing -dw parameters for MgCO3, MgHCO3, and MgSO4 aqueous species. 61580e15 added equal_steps to cxxKinetics. Had not implemented the "in steps" option for the Kinetics class. 3c280751 exchange_modify, recalculate formula_totals after redefinition. 11b0315d Made pitzer_exchange_gammas true the default. 5aafb6d5 Added some identifier synonyms for DUMP and DELETE. 8e65d00c Fixed duplicate lines in echoing output at end of file Streamify checks for OPTION_KEYWORD; check_line is KEYWORD. aa5cafea Use OPTION_KEYWORD for stremify KEYWORD for check_line. d34b328b Added -cell to dump, copy, delete. f41c9446 Redid capability to include input files using #include fn. 619dc25c Modified merging #include files to use file pointer instead of stream. fd55082c wrote code to allow files to be included recursively in the main input file. b1fca87a Added -mix, -reaction, -temperature, -cells to DUMP. 8ff9cd79 Fixed negative exponent problem. 2034b500 Moved some initialization from initialize to class. 1b6f05cd Works for class, need to split up initialization for static variables. 20cf5c40 Fixed uninitialized variable in V_M structure in transport.c, which caused sc7 to fail under Class_release configuration (NDEBUG). 4de8e60e Changed phqalloc to allow 3 cases: 89090c46 added stdlib.h for exit on SunOS 4b311977 Redid modifications for hidden variables using l_ prefix. 053c817d added stdlib.h for exit prototype on SunOS 579a2837 Fixing hidden variables in 261bc011 fixed p_instance bug for raw and ppdump 778c7b05 changed p_instance to p_instance1 hiding variables ba762e6d fixed hiding variables bf1e9bff fixed hiding variables 1e9f6e57 Fixed up charting changes for class version. 98e6f469 Modified class version to detect end of USER_GRAPH thread. 24f5a295 Added code to allow phreeqc_instance to stay in scope until the thread count drops to 8. Don't know if this will always work. 31eafb92 Sortof works on class clr version, but need to be able to detect when chart thread ends. 0b4092e1 Still need to work on Form1 to pass a phreeqc instance. 5b8e7fdf added output_message prototype to output.h removed prototype from phreeqcpp/Phreeqc.h and phreeqcpp/phreeqc/print.c e1b52e15 reworking output_message prototype 09bf8787 deleted unused variables sit_remove_unstable_phases and pitzer_remove_unstable_phases. 4940d97f combined src/Makefile.am src/phreeqcpp/Makefile.am src/phreecpp/phreeqc/Makefile.am into one file cba7d11b modified Makefile.am's to handle each src dir independently e3888e1f added newline at end of files 0833d79e changed svn:eol-style to native for many files 7ca9917f Remove from unit: TestInterface.cpp TestInterface.h IPhreeqc.hpp IPhreeqc.h IPhreeqc.cpp module_output.cpp module_files.cpp fwrap.cpp fwrap2.cpp fwrap3.cpp IPhreeqc.cpp CVar.hxx ErrorReporter.hxx OutputFiles.hxx Phreeqc.hxx PhreeqcParser.hxx SelectedOutput.hxx Var.h Remove from IPhreeqc IPhreeqc.cpp IPhreeqc.h IPhreeqc.hpp module_output.cpp module_files.cpp adcd4447 passes all tests on win32; before changing to multiple instances 8db3c1ac Added prototypes to class for Basic functions: total_mole iso_value iso_unit 4d050487 added prototypes for dump routines; added ostream header 7e52b0c3 Did not pick up redox species from solution. ffb8ee86 moved list_components from class_main.cpp to Phreeqc.cpp 4eb0dbc9 list_components method to extract complete list of elements in all solutions, equilibrium_phases, etc that are currently defined within the object. a22a6db0 need to test on linux 9ed87e33 std header files added. e245f8af Updated for IPhreeqc to use PHREEQC_CPP and PHREEQC_CLASS. Currently doesn't include gmp. MemDebug project still needs work. Needs to be tested on linux. 4ddc8358 Added equilibrium_phase keyword to Phreeqc.cpp constructor 81717e18 Removing zero editing. ef4a5b53 Before modification to use standard database files, shorten run times, and limit output. 5e23f440 Made pressurs a synonym of total_p in gas_phase_modify. 7b777201 Added viscosity function for SC at temperature. ef46b2f3 Fixed remove_unstable_phases for class 694c0748 Bugs with kinetics in PHAST. 490fd2be Added options to the UNITS keyword to allow moles of solid reactants to be interpreted as moles per liter of WATER or moles per liter of ROCK: d8506413 mpi.h needs to be defined first because of a conflict with stdio (SEEK_SET ifdef). 7dbc8d06 PHREEQC runs as class. b4544293 Fixed error with callback. Had not saved the phreeqc pointer in the parser constructor. 65ff0a94 Fixed Makefile on Linux to make class version. 3131740c Compiles as class, debug, and Release. 9f4eefd2 Made all the changes to add phreeqc pointer as argument to Phreeqcpp class methods. 157e5cc7 Slowly adding arguments and qualifiers to pass phreeqc class instance to routines that need it. 6859605b Debug and release compile. 2b76f04a Cleaned up header files to remove unnecessary includes of .h files. Moved includes to source code where possible. e65ab2ea cleaned up storagebin headers. 0a91aa5b phreeqcpp (nonclass) compiles. b33830cc Moving class_main, phreeqc.cpp and phreeqc.h into phreeqcpp.sln 07d5fa74 merged 3750-3846 of plusify; system.cxx and system.h need to be formated removed char * and used std::string's and std::map's dd1c83ed eol 51183867 eol 9f7e982f eol a4bac119 eol for system.cxx 3fe2fcd3 Modified to run_cells if solution exists, even with no reactants. 30386412 updated for COM object 5de5c6f3 Needed to turn of echo when rereading components. ff19c86d The tellg and seekg did not work for gz files because the methods are not implemented. e3567320 Typo of file instead of stream for setting echo to file. c73fc4b2 Modifications for Linux. b3b22f3f New class 259c0839 Added new class runner. 5f9cf78f Modified for parallel version. Had some lists in the pack and unpack routines that now are maps. cd38a803 Have done preliminary testing with all MODIFY keywords. d54c0403 Have converted std::list to std::map for all entities. f0157592 exchange_modify is working. b9e38f4d Updated phreeqcpp to have examples from phreeqc. cbfb7683 Initial testing of SOLUTION_MODIFY and EQUILIBRIUM_PHASES_MODIFY. 2fd35ce4 Delete, Dump seem to work. 74bb0bc5 For dump and delete, added StorageBinList 79092e94 Have added delete, but parser is not working correctly. Am going back to 3705 to see if it worked correctly then. 6ca3a6d3 Added the suite of modify keywords: SOLUTION_MODIFY EQUILIBRIUM_PHASES_MODIFY EXCHANGE_MODIFY SURFACE_MODIFY SOLID_SOLUTIONS_MODIFY GAS_PHASE_MODIFY KINETICS_MODIFY c3d4c1c4 Subsequent simulation wiped out dump file even though there was no dump. Check to see if dump file is needed before opening file in dump_entities. 4d717951 Added dump files. a81cd945 Added dump datablock for phreeqcpp ccd31029 SOLUTION_RAW: c++ version used in PHAST misinterpreted the element Tc as the temperature identifier and crashed when additional elements followed in -totals or -activity_coefficients. 45d120f6 Fixed bug with reallocation for Read_raw for storage bin. Was allocating space for a pointer array instead of structure arrays for exchange, surface, etc. ReadClass.cxx. ff282a40 Missed one line for struct to class conversion of ppassemblage for precipitate_only. e398c418 Updated PHREEQCPP to have precipitate_only option for pp_assemblage. cefe071f Reindented, fixed errors, ran test cases. d37ed2c6 indented header files. 3313c814 Ran indent program on phreeqcpp 60d57c50 Delete kinetics.h at PhreeqcPP src level. Duplicate I assume 193e8ef8 Bug with adding mixing solution when already in list. Replaced old fraction with new fraction. Now adds the two fractions. 7a60bb98 Write around assert. d106dbea Added missing tests from phreeqc/mytest and related files: netpathxl eq_EC salt_ec 25666df9 Used new gcc on srv2rcolkr. b9520a20 Worked on dependencies for Makefile. bfec3cc7 Missing break in kinetics_read_raw switch. 3f9693c6 Updated C++ to have new variables for KINETICS cvode_steps, cvode_order. f630dd83 added linker option to check for --hash-style availability 711f5f94 Fixed syntax for bash 5764f894 Simplified get_total_element a bit f4777a19 Fixed bug, and streamlined. 3f93ab5a Tried optimizing get_total_element using std::strings e7ec5c76 Removed echo that was always true. Made huge string that crashed restart. Results are same without the removed lines. 8d475282 Added read_raw_keyword. 75255ea2 No memory leak check in output files. e3859a08 reordering mpi.h 37b1add9 Ifdef'd ORCHESTRA. 7ab1e21c Works with reaction calculation of pure phase equilibrium. c78dd87d Have version working that formats an input file for Orchestra, runs Orchestra with a system call to a batch file, reads results from Orchestra, stores results in global phreeqc storage, and prints results. 0f2ef116 Have version working that formats an input file for Orchestra, runs Orchestra with a system call to a batch file, reads results from Orchestra, stores results in global phreeqc storage, and prints results. ea61d055 Can read a solution result from Orchestra.cxx 1848045b Added new functions to allow multiply. c5d2d6d8 Fixed some warnings found by Studio (casts) b2dd9ad8 Simplified test for missing solution. 0bebfae5 Fixed bug in get_total_element 4145b60e Fixes for cxx version of xsolution_save (constructor(n_user)) 8a84d795 End of line for now, have solution and exchange saving from c storage after calculation. a7a19e57 Still has problems, but roughed in xsolution_save and xexchange_save as constructor methods for Solution and Exchange. Takes c storage and makes C++ objects after a calculation. 7830f46d Merged 1727 through 2055 from orchestra/phreeqcpp_for_orch into phreeeqcpp trunk 4a5bbef3 New compiler, lots of diffs in test cases. 546c3e8e Had extra :: qualifiers, not needed in header file. 2b442eef git-svn-id: svn://136.177.114.72/svn_GW/phreeqcpp/trunk@1723 1feff8c3-07ed-0310-ac33-dd36852eb9cd 93a7759e Beginning to revise cpp classes. bd852de5 removed extraneous cxxDictionary:: from int2char method 4f7eec29 gcc did not like comma at end of enumeration 07f03ea2 Fixed echo in parser for ReadClass.cxx. 2dc33fb7 Working on echoing for CParser 3320ef3c removed error increment in get_option. Logic allows a missing option in component reads, which bubble up to keyword reads. 888140c4 Updated with phreeqc files. eb5b9511 Stray debug print removed from StorageBin. 42300692 fixed warn again. f8c7968c Fixed incorrect casts. 52ab13ec Fixed some bugs in C++ classes. 812bb1ad Updated with Tony's MCD additions to Surface and SurfaceComp classes. e4ca1d77 Forgot to include force_equality in PPassemblageComp 7953591e Working through changes to surface structure 119b0325 Had not included formula_totals and formula_z in structure. 89c12ec0 using type and dl_type in Surface 217bdd77 Converted to VS2005. cb46a1de Removed warning messages from compiler 309d3d7b Fixed cast warnings in c++ a524c461 Updated to latest PHAST cpp files. 382075cc Couple changes for exchange mixing 1e28de64 changed automatic arrays to dynamic arrays 6df20749 Added mix_cxxExchange to storage bin with assorted routines and changes to make it work. d8a1b2d7 Renaming some files 764d31c7 Added Dictionary to svn 11431ae2 packing and unpacking cxx classes for mpi is running need to check result. 297744ea Updated for VS7.Net a38caf49 Serial version is working for Linux with restart option. 8356664d workds on serial and parallel using StorageBin for uz, sz, and phreeqc cb5ccd55 Works with szBin on serial and parallel. a372f9a8 Simplified mix with add(int, factor) method 88e3ff60 Initialize default NumKeyword n_user = 1. 8c747cbb Almost working on unsaturated zone serial version. b3028f79 Roughed in some printing for debugging phast c41fb4e5 phastpp works on linear_ic in debug mode. 574da9a1 Working on phast driver to use classes for main storage 3261c998 Added class SolutionIsotopeList 957551d0 Updates e2b5a470 Dump_raw for storage bin works, but have not tested. bc119814 Adding StorageBin 90a787e4 Changed name to ISolutionComp. 2fe8f7d4 Working on saving state. c42d9855 Moved Isotope to SolutionIsotope 39494b02 Renamed SolutionComp to ISolutionComp because it is not used in cxxSolution. 75b28d81 Moved Conc to SolutionComp 0d1acff7 removed classes and headers that are in phreeqc directory. 3da5643a Makefile is working. dff0e345 House cleaning for phreeqcpp 60a15440 Copying new classes (cxx) and cpp files to src git-subtree-dir: src git-subtree-split: f715edb5897985acf0692d2b0bde4ab34379469f --- .gitlab-ci.yml | 123 + CSelectedOutput.hxx | 4 +- IPhreeqc.cpp | 60 +- IPhreeqc.f.inc | 2 + IPhreeqc.f90.inc | 17 + IPhreeqc.h | 44 + IPhreeqc.hpp | 45 +- IPhreeqcF.f | 21 + IPhreeqcLib.cpp | 34 +- IPhreeqc_interface.F90 | 40 +- IPhreeqc_interface_F.cpp | 17 +- IPhreeqc_interface_F.h | 10 +- Makefile.am | 1 + Var.h | 6 +- Version.h | 1 + fimpl.h | 8 + fwrap.cpp | 12 + fwrap.h | 4 + phreeqcpp/.gitlab-ci.yml | 111 + phreeqcpp/ChartHandler.cpp | 234 + phreeqcpp/ChartHandler.h | 59 + phreeqcpp/ChartObject.cpp | 1382 ++++ phreeqcpp/ChartObject.h | 444 ++ phreeqcpp/CurveObject.cpp | 42 + phreeqcpp/CurveObject.h | 79 + phreeqcpp/Dictionary.cpp | 41 + phreeqcpp/Dictionary.h | 28 + phreeqcpp/ExchComp.cxx | 398 + phreeqcpp/ExchComp.h | 117 + phreeqcpp/Exchange.cxx | 466 ++ phreeqcpp/Exchange.h | 74 + phreeqcpp/Form1.2005.resX | 33 + phreeqcpp/Form1.h | 1151 +++ phreeqcpp/Form1.resX | 36 + phreeqcpp/GasComp.cxx | 265 + phreeqcpp/GasComp.h | 59 + phreeqcpp/GasPhase.cxx | 659 ++ phreeqcpp/GasPhase.h | 103 + phreeqcpp/ISolution.cxx | 40 + phreeqcpp/ISolution.h | 53 + phreeqcpp/ISolutionComp.cxx | 202 + phreeqcpp/ISolutionComp.h | 138 + phreeqcpp/KineticsComp.cxx | 318 + phreeqcpp/KineticsComp.h | 81 + phreeqcpp/Makefile | 639 ++ phreeqcpp/Makefile.am | 160 + phreeqcpp/Makefile.old | 986 +++ phreeqcpp/NA.h | 1 + phreeqcpp/NameDouble.cxx | 537 ++ phreeqcpp/NameDouble.h | 66 + phreeqcpp/NumKeyword.cxx | 190 + phreeqcpp/NumKeyword.h | 67 + phreeqcpp/PBasic.cpp | 7546 ++++++++++++++++++ phreeqcpp/PBasic.h | 559 ++ phreeqcpp/PHRQ_io_output.cpp | 411 + phreeqcpp/PPassemblage.cxx | 375 + phreeqcpp/PPassemblage.h | 70 + phreeqcpp/PPassemblageComp.cxx | 441 ++ phreeqcpp/PPassemblageComp.h | 83 + phreeqcpp/Phreeqc.cpp | 2077 +++++ phreeqcpp/Phreeqc.h | 2079 +++++ phreeqcpp/PhreeqcKeywords/Keywords.cpp | 232 + phreeqcpp/PhreeqcKeywords/Keywords.h | 99 + phreeqcpp/Pressure.cxx | 417 + phreeqcpp/Pressure.h | 43 + phreeqcpp/Reaction.cxx | 284 + phreeqcpp/Reaction.h | 57 + phreeqcpp/ReadClass.cxx | 1150 +++ phreeqcpp/SS.cxx | 609 ++ phreeqcpp/SS.h | 128 + phreeqcpp/SSassemblage.cxx | 317 + phreeqcpp/SSassemblage.h | 59 + phreeqcpp/SScomp.cxx | 297 + phreeqcpp/SScomp.h | 66 + phreeqcpp/SelectedOutput.cpp | 115 + phreeqcpp/SelectedOutput.h | 209 + phreeqcpp/Serializer.cxx | 209 + phreeqcpp/Serializer.h | 42 + phreeqcpp/Solution.cxx | 1757 +++++ phreeqcpp/Solution.h | 148 + phreeqcpp/SolutionIsotope.cxx | 337 + phreeqcpp/SolutionIsotope.h | 85 + phreeqcpp/StorageBin.cxx | 1507 ++++ phreeqcpp/StorageBin.h | 141 + phreeqcpp/StorageBinList.cpp | 358 + phreeqcpp/StorageBinList.h | 81 + phreeqcpp/Surface.cxx | 808 ++ phreeqcpp/Surface.h | 101 + phreeqcpp/SurfaceCharge.cxx | 586 ++ phreeqcpp/SurfaceCharge.h | 131 + phreeqcpp/SurfaceComp.cxx | 509 ++ phreeqcpp/SurfaceComp.h | 70 + phreeqcpp/System.cxx | 103 + phreeqcpp/System.h | 89 + phreeqcpp/Temperature.cxx | 423 ++ phreeqcpp/Temperature.h | 42 + phreeqcpp/Use.cpp | 78 + phreeqcpp/Use.h | 159 + phreeqcpp/UserPunch.cpp | 32 + phreeqcpp/UserPunch.h | 39 + phreeqcpp/ZedGraph.dll | Bin 0 -> 307200 bytes phreeqcpp/advection.cpp | 140 + phreeqcpp/basicsubs.cpp | 4080 ++++++++++ phreeqcpp/chart_icon.gif | Bin 0 -> 2141 bytes phreeqcpp/cl1.cpp | 881 +++ phreeqcpp/cl1mp.cpp | 1141 +++ phreeqcpp/class_main.cpp | 643 ++ phreeqcpp/common/.gitlab-ci.yml | 46 + phreeqcpp/common/PHRQ_base.cxx | 117 + phreeqcpp/common/PHRQ_base.h | 48 + phreeqcpp/common/PHRQ_exports.h | 20 + phreeqcpp/common/PHRQ_io.cpp | 913 +++ phreeqcpp/common/PHRQ_io.h | 207 + phreeqcpp/common/Parser.cxx | 1331 ++++ phreeqcpp/common/Parser.h | 310 + phreeqcpp/common/Utils.cxx | 194 + phreeqcpp/common/Utils.h | 28 + phreeqcpp/common/phrqtype.h | 18 + phreeqcpp/cvdense.cpp | 566 ++ phreeqcpp/cvdense.h | 267 + phreeqcpp/cvode.cpp | 3939 ++++++++++ phreeqcpp/cvode.h | 940 +++ phreeqcpp/cxxKinetics.cxx | 617 ++ phreeqcpp/cxxKinetics.h | 78 + phreeqcpp/cxxMix.cxx | 154 + phreeqcpp/cxxMix.h | 58 + phreeqcpp/dense.cpp | 175 + phreeqcpp/dense.h | 341 + phreeqcpp/dumper.cpp | 277 + phreeqcpp/dumper.h | 60 + phreeqcpp/gases.cpp | 697 ++ phreeqcpp/global_structures.h | 1653 ++++ phreeqcpp/input.cpp | 130 + phreeqcpp/integrate.cpp | 1087 +++ phreeqcpp/inverse.cpp | 5153 +++++++++++++ phreeqcpp/isotopes.cpp | 1824 +++++ phreeqcpp/kinetics.cpp | 3180 ++++++++ phreeqcpp/mainsubs.cpp | 2301 ++++++ phreeqcpp/model.cpp | 5846 ++++++++++++++ phreeqcpp/nvector.cpp | 272 + phreeqcpp/nvector.h | 485 ++ phreeqcpp/nvector_serial.cpp | 1031 +++ phreeqcpp/nvector_serial.h | 369 + phreeqcpp/parse.cpp | 1043 +++ phreeqcpp/phast.xsd | 98 + phreeqcpp/phqalloc.cpp | 316 + phreeqcpp/phqalloc.h | 47 + phreeqcpp/phreeqc.Makefile | 237 + phreeqcpp/phreex.ico | Bin 0 -> 318 bytes phreeqcpp/pitzer.cpp | 2707 +++++++ phreeqcpp/pitzer_structures.cpp | 225 + phreeqcpp/prep.cpp | 6205 +++++++++++++++ phreeqcpp/print.cpp | 3634 +++++++++ phreeqcpp/read.cpp | 9698 ++++++++++++++++++++++++ phreeqcpp/readtr.cpp | 1495 ++++ phreeqcpp/runner.cpp | 158 + phreeqcpp/runner.h | 33 + phreeqcpp/sit.cpp | 1686 ++++ phreeqcpp/smalldense.cpp | 324 + phreeqcpp/smalldense.h | 261 + phreeqcpp/spread.cpp | 1296 ++++ phreeqcpp/step.cpp | 1485 ++++ phreeqcpp/structures.cpp | 3379 +++++++++ phreeqcpp/sundialsmath.cpp | 133 + phreeqcpp/sundialsmath.h | 162 + phreeqcpp/sundialstypes.h | 183 + phreeqcpp/tally.cpp | 1288 ++++ phreeqcpp/test.xml | 70 + phreeqcpp/tidy.cpp | 5576 ++++++++++++++ phreeqcpp/transport.cpp | 6183 +++++++++++++++ phreeqcpp/utilities.cpp | 1463 ++++ 171 files changed, 126226 insertions(+), 42 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 phreeqcpp/.gitlab-ci.yml create mode 100644 phreeqcpp/ChartHandler.cpp create mode 100644 phreeqcpp/ChartHandler.h create mode 100644 phreeqcpp/ChartObject.cpp create mode 100644 phreeqcpp/ChartObject.h create mode 100644 phreeqcpp/CurveObject.cpp create mode 100644 phreeqcpp/CurveObject.h create mode 100644 phreeqcpp/Dictionary.cpp create mode 100644 phreeqcpp/Dictionary.h create mode 100644 phreeqcpp/ExchComp.cxx create mode 100644 phreeqcpp/ExchComp.h create mode 100644 phreeqcpp/Exchange.cxx create mode 100644 phreeqcpp/Exchange.h create mode 100644 phreeqcpp/Form1.2005.resX create mode 100644 phreeqcpp/Form1.h create mode 100644 phreeqcpp/Form1.resX create mode 100644 phreeqcpp/GasComp.cxx create mode 100644 phreeqcpp/GasComp.h create mode 100644 phreeqcpp/GasPhase.cxx create mode 100644 phreeqcpp/GasPhase.h create mode 100644 phreeqcpp/ISolution.cxx create mode 100644 phreeqcpp/ISolution.h create mode 100644 phreeqcpp/ISolutionComp.cxx create mode 100644 phreeqcpp/ISolutionComp.h create mode 100644 phreeqcpp/KineticsComp.cxx create mode 100644 phreeqcpp/KineticsComp.h create mode 100644 phreeqcpp/Makefile create mode 100644 phreeqcpp/Makefile.am create mode 100644 phreeqcpp/Makefile.old create mode 100644 phreeqcpp/NA.h create mode 100644 phreeqcpp/NameDouble.cxx create mode 100644 phreeqcpp/NameDouble.h create mode 100644 phreeqcpp/NumKeyword.cxx create mode 100644 phreeqcpp/NumKeyword.h create mode 100644 phreeqcpp/PBasic.cpp create mode 100644 phreeqcpp/PBasic.h create mode 100644 phreeqcpp/PHRQ_io_output.cpp create mode 100644 phreeqcpp/PPassemblage.cxx create mode 100644 phreeqcpp/PPassemblage.h create mode 100644 phreeqcpp/PPassemblageComp.cxx create mode 100644 phreeqcpp/PPassemblageComp.h create mode 100644 phreeqcpp/Phreeqc.cpp create mode 100644 phreeqcpp/Phreeqc.h create mode 100644 phreeqcpp/PhreeqcKeywords/Keywords.cpp create mode 100644 phreeqcpp/PhreeqcKeywords/Keywords.h create mode 100644 phreeqcpp/Pressure.cxx create mode 100644 phreeqcpp/Pressure.h create mode 100644 phreeqcpp/Reaction.cxx create mode 100644 phreeqcpp/Reaction.h create mode 100644 phreeqcpp/ReadClass.cxx create mode 100644 phreeqcpp/SS.cxx create mode 100644 phreeqcpp/SS.h create mode 100644 phreeqcpp/SSassemblage.cxx create mode 100644 phreeqcpp/SSassemblage.h create mode 100644 phreeqcpp/SScomp.cxx create mode 100644 phreeqcpp/SScomp.h create mode 100644 phreeqcpp/SelectedOutput.cpp create mode 100644 phreeqcpp/SelectedOutput.h create mode 100644 phreeqcpp/Serializer.cxx create mode 100644 phreeqcpp/Serializer.h create mode 100644 phreeqcpp/Solution.cxx create mode 100644 phreeqcpp/Solution.h create mode 100644 phreeqcpp/SolutionIsotope.cxx create mode 100644 phreeqcpp/SolutionIsotope.h create mode 100644 phreeqcpp/StorageBin.cxx create mode 100644 phreeqcpp/StorageBin.h create mode 100644 phreeqcpp/StorageBinList.cpp create mode 100644 phreeqcpp/StorageBinList.h create mode 100644 phreeqcpp/Surface.cxx create mode 100644 phreeqcpp/Surface.h create mode 100644 phreeqcpp/SurfaceCharge.cxx create mode 100644 phreeqcpp/SurfaceCharge.h create mode 100644 phreeqcpp/SurfaceComp.cxx create mode 100644 phreeqcpp/SurfaceComp.h create mode 100644 phreeqcpp/System.cxx create mode 100644 phreeqcpp/System.h create mode 100644 phreeqcpp/Temperature.cxx create mode 100644 phreeqcpp/Temperature.h create mode 100644 phreeqcpp/Use.cpp create mode 100644 phreeqcpp/Use.h create mode 100644 phreeqcpp/UserPunch.cpp create mode 100644 phreeqcpp/UserPunch.h create mode 100644 phreeqcpp/ZedGraph.dll create mode 100644 phreeqcpp/advection.cpp create mode 100644 phreeqcpp/basicsubs.cpp create mode 100644 phreeqcpp/chart_icon.gif create mode 100644 phreeqcpp/cl1.cpp create mode 100644 phreeqcpp/cl1mp.cpp create mode 100644 phreeqcpp/class_main.cpp create mode 100644 phreeqcpp/common/.gitlab-ci.yml create mode 100644 phreeqcpp/common/PHRQ_base.cxx create mode 100644 phreeqcpp/common/PHRQ_base.h create mode 100644 phreeqcpp/common/PHRQ_exports.h create mode 100644 phreeqcpp/common/PHRQ_io.cpp create mode 100644 phreeqcpp/common/PHRQ_io.h create mode 100644 phreeqcpp/common/Parser.cxx create mode 100644 phreeqcpp/common/Parser.h create mode 100644 phreeqcpp/common/Utils.cxx create mode 100644 phreeqcpp/common/Utils.h create mode 100644 phreeqcpp/common/phrqtype.h create mode 100644 phreeqcpp/cvdense.cpp create mode 100644 phreeqcpp/cvdense.h create mode 100644 phreeqcpp/cvode.cpp create mode 100644 phreeqcpp/cvode.h create mode 100644 phreeqcpp/cxxKinetics.cxx create mode 100644 phreeqcpp/cxxKinetics.h create mode 100644 phreeqcpp/cxxMix.cxx create mode 100644 phreeqcpp/cxxMix.h create mode 100644 phreeqcpp/dense.cpp create mode 100644 phreeqcpp/dense.h create mode 100644 phreeqcpp/dumper.cpp create mode 100644 phreeqcpp/dumper.h create mode 100644 phreeqcpp/gases.cpp create mode 100644 phreeqcpp/global_structures.h create mode 100644 phreeqcpp/input.cpp create mode 100644 phreeqcpp/integrate.cpp create mode 100644 phreeqcpp/inverse.cpp create mode 100644 phreeqcpp/isotopes.cpp create mode 100644 phreeqcpp/kinetics.cpp create mode 100644 phreeqcpp/mainsubs.cpp create mode 100644 phreeqcpp/model.cpp create mode 100644 phreeqcpp/nvector.cpp create mode 100644 phreeqcpp/nvector.h create mode 100644 phreeqcpp/nvector_serial.cpp create mode 100644 phreeqcpp/nvector_serial.h create mode 100644 phreeqcpp/parse.cpp create mode 100644 phreeqcpp/phast.xsd create mode 100644 phreeqcpp/phqalloc.cpp create mode 100644 phreeqcpp/phqalloc.h create mode 100644 phreeqcpp/phreeqc.Makefile create mode 100644 phreeqcpp/phreex.ico create mode 100644 phreeqcpp/pitzer.cpp create mode 100644 phreeqcpp/pitzer_structures.cpp create mode 100644 phreeqcpp/prep.cpp create mode 100644 phreeqcpp/print.cpp create mode 100644 phreeqcpp/read.cpp create mode 100644 phreeqcpp/readtr.cpp create mode 100644 phreeqcpp/runner.cpp create mode 100644 phreeqcpp/runner.h create mode 100644 phreeqcpp/sit.cpp create mode 100644 phreeqcpp/smalldense.cpp create mode 100644 phreeqcpp/smalldense.h create mode 100644 phreeqcpp/spread.cpp create mode 100644 phreeqcpp/step.cpp create mode 100644 phreeqcpp/structures.cpp create mode 100644 phreeqcpp/sundialsmath.cpp create mode 100644 phreeqcpp/sundialsmath.h create mode 100644 phreeqcpp/sundialstypes.h create mode 100644 phreeqcpp/tally.cpp create mode 100644 phreeqcpp/test.xml create mode 100644 phreeqcpp/tidy.cpp create mode 100644 phreeqcpp/transport.cpp create mode 100644 phreeqcpp/utilities.cpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..764e2204 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,123 @@ +# +# https://code.chs.usgs.gov/coupled/subtrees/iphreeqc-src +# SRC 2020-12-02T18:39:55-07:00 +# +image: ${CI_REGISTRY}/coupled/containers/buildpack-deps:bionic-scm + +stages: + - sync + - trigger + +before_script: + - eval $(ssh-agent -s) + - echo "${SSH_PRIVATE_KEY_ENC}" | base64 --decode | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - ssh-keyscan ${CI_SERVER_HOST} >> ~/.ssh/known_hosts + - chmod 644 ~/.ssh/known_hosts + - git config --global user.email "darth@empire.com" + - git config --global user.name "Darth Vader" + +subtree-sync: + stage: sync + + ## + ## Only run if on the master branch and the variable GROUP is set + ## + ## change this to + ## only: + ## - master@$GROUP/subtrees/iphreeqc-src + ## and set GROUP to coupled before merge + only: + refs: + - master + variables: + - $GROUP + + script: + ## + ## Must re-clone in order for the subtree merge to work + ## tried re-setting the url for the origin but didn't work + ## + - cd .. + - rm -rf ${CI_PROJECT_NAME} + - git clone git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git + - cd ${CI_PROJECT_NAME} + + ## + ## Sync subtrees + ## + - | + #!/bin/bash -ex + # + # IPhreeqc/ git@${CI_SERVER_HOST}:${GROUP}/IPhreeqc.git + # ├─database/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-database.git database + # ├─examples/ │ └─examples + # │ ├─c/ │ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc-COMManuscript-CGfinal-examples-c.git examples/c + # │ ├─com/ │ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc-COMManuscript-CGfinal-examples-com.git examples/com + # │ └─fortran/ │ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc-COMManuscript-CGfinal-examples-fortran.git examples/fortran + # ├─phreeqc3-doc/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-doc.git phreeqc3-doc + # ├─phreeqc3-examples/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-examples.git phreeqc3-examples + # └─src/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/IPhreeqc-src.git src + # └─phreeqcpp/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src.git src/phreeqcpp + # └─common/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src-common.git src/phreeqcpp/common + # + # IPhreeqc-src/ git@${CI_SERVER_HOST}:${GROUP}/subtrees/IPhreeqc-src.git + # └─phreeqcpp/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src.git phreeqcpp + # └─common/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src-common.git phreeqcpp/common + + git_subtree() { + git subtree "${1}" --prefix="${2}" "${4}" master 2>&1 | grep -v "^[[:digit:]].*/[[:digit:]].*" + } + + declare -A urls=( \ + ["phreeqc3-src"]="git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src.git" \ + ) + + declare -A prefixes=( \ + ["phreeqc3-src"]="phreeqcpp" \ + ) + + export GIT_EDITOR=true + + for remote in "${!urls[@]}"; do + git_subtree "pull" "${prefixes[$remote]}" "$remote" "${urls[$remote]}" + done + + for remote in "${!urls[@]}"; do + git_subtree "push" "${prefixes[$remote]}" "$remote" "${urls[$remote]}" + done + + git push origin master + git status + +trigger-downstream: + stage: trigger + ## + ## Only run if on the master branch and the variable GROUP is set + ## + ## change this to + ## only: + ## - master@$GROUP/subtrees/iphreeqc-src + ## and set GROUP to coupled before merge + only: + refs: + - master + variables: + - $GROUP + + ## Downstream Projects + ## triggers and ids are stored at the group level + ## iphreeqc https://code.chs.usgs.gov/coupled/iphreeqc + ## iphreeqccom https://code.chs.usgs.gov/coupled/iphreeqccom + ## phreeqcrm-src https://code.chs.usgs.gov/coupled/subtrees/phreeqcrm-src + script: + - echo triggering iphreeqc + - curl -X POST -F token=${IPHREEQC_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${IPHREEQC_ID}/trigger/pipeline + - echo triggering iphreeqccom + - curl -X POST -F token=${IPHREEQCCOM_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${IPHREEQCCOM_ID}/trigger/pipeline + - echo triggering phreeqcrm-src + - curl -X POST -F token=${PHREEQCRM_SRC_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${PHREEQCRM_SRC_ID}/trigger/pipeline + + ## Upstream Projects + ## phreeqc3-src https://code.chs.usgs.gov/coupled/subtrees/phreeqc3-src diff --git a/CSelectedOutput.hxx b/CSelectedOutput.hxx index fd1ab794..c15e1240 100644 --- a/CSelectedOutput.hxx +++ b/CSelectedOutput.hxx @@ -15,7 +15,9 @@ #include #include "CVar.hxx" -class CSelectedOutput +#include "PHRQ_exports.h" + +class IPQ_DLL_EXPORT CSelectedOutput { public: CSelectedOutput(void); diff --git a/IPhreeqc.cpp b/IPhreeqc.cpp index 4c1de513..d5f4640c 100644 --- a/IPhreeqc.cpp +++ b/IPhreeqc.cpp @@ -213,9 +213,19 @@ bool IPhreeqc::GetErrorFileOn(void)const return this->ErrorFileOn; } +bool IPhreeqc::GetErrorOn(void)const +{ + return this->Get_error_on(); +} + const char* IPhreeqc::GetErrorString(void) { static const char err_msg[] = "GetErrorString: ErrorStringOn not set.\n"; + static const char err_msg2[] = "GetErrorString: ErrorOn not set.\n"; + if (!this->error_on) + { + return err_msg2; + } if (!this->ErrorStringOn) { return err_msg; @@ -554,6 +564,12 @@ std::list< std::string > IPhreeqc::ListComponents(void) { this->Components.clear(); this->PhreeqcPtr->list_components(this->Components); + this->PhreeqcPtr->list_EquilibriumPhases(this->EquilibriumPhasesList); + this->PhreeqcPtr->list_GasComponents(this->GasComponentsList); + this->PhreeqcPtr->list_KineticReactions(this->KineticReactionsList); + this->PhreeqcPtr->list_SolidSolutions(this->SolidSolutionComponentsList,this->SolidSolutionNamesList); + this->PhreeqcPtr->list_Surfaces(this->SurfaceTypeList, this->SurfaceNamesList); + this->PhreeqcPtr->list_Exchangers(this->ExchangeNamesList); this->UpdateComponents = false; } return this->Components; @@ -585,7 +601,7 @@ int IPhreeqc::load_db(const char* filename) this->PhreeqcPtr->phrq_io->push_istream(&ifs, false); this->PhreeqcPtr->read_database(); } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { this->close_input_files(); } @@ -596,7 +612,7 @@ int IPhreeqc::load_db(const char* filename) { this->PhreeqcPtr->error_msg(errmsg, STOP); // throws IPhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -648,7 +664,7 @@ int IPhreeqc::load_db_str(const char* input) this->PhreeqcPtr->phrq_io->push_istream(&iss, false); this->PhreeqcPtr->read_database(); } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { this->close_input_files(); } @@ -659,7 +675,7 @@ int IPhreeqc::load_db_str(const char* input) { this->PhreeqcPtr->error_msg(errmsg, STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -737,7 +753,7 @@ int IPhreeqc::RunAccumulated(void) // this may throw this->do_run(sz_routine, &iss, NULL, NULL, NULL); } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -749,7 +765,7 @@ int IPhreeqc::RunAccumulated(void) { this->PhreeqcPtr->error_msg(errmsg.c_str(), STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -762,7 +778,7 @@ int IPhreeqc::RunAccumulated(void) { this->PhreeqcPtr->error_msg(errmsg, STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -808,7 +824,7 @@ int IPhreeqc::RunFile(const char* filename) // this may throw this->do_run(sz_routine, &ifs, NULL, NULL, NULL); } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { this->close_input_files(); } @@ -820,7 +836,7 @@ int IPhreeqc::RunFile(const char* filename) { this->PhreeqcPtr->error_msg(errmsg.c_str(), STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -833,7 +849,7 @@ int IPhreeqc::RunFile(const char* filename) { this->PhreeqcPtr->error_msg(errmsg, STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -871,7 +887,7 @@ int IPhreeqc::RunString(const char* input) // this may throw this->do_run(sz_routine, &iss, NULL, NULL, NULL); } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { this->close_input_files(); } @@ -883,7 +899,7 @@ int IPhreeqc::RunString(const char* input) { this->PhreeqcPtr->error_msg(errmsg.c_str(), STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -896,7 +912,7 @@ int IPhreeqc::RunString(const char* input) { this->PhreeqcPtr->error_msg(errmsg, STOP); // throws PhreeqcStop } - catch (IPhreeqcStop) + catch (const IPhreeqcStop&) { // do nothing } @@ -967,6 +983,11 @@ void IPhreeqc::SetErrorFileOn(bool bValue) this->ErrorFileOn = bValue; } +void IPhreeqc::SetErrorOn(bool bValue) +{ + this->Set_error_on(bValue); +} + void IPhreeqc::SetErrorStringOn(bool bValue) { this->ErrorStringOn = bValue; @@ -1137,7 +1158,6 @@ void IPhreeqc::check_database(const char* sz_routine) { this->ErrorReporter->Clear(); this->WarningReporter->Clear(); - std::map< int, CSelectedOutput* >::iterator it = this->SelectedOutputMap.begin(); for (; it != this->SelectedOutputMap.end(); ++it) { @@ -1248,14 +1268,14 @@ void IPhreeqc::do_run(const char* sz_routine, std::istream* pis, PFN_PRERUN_CALL } ASSERT(this->PhreeqcPtr->SelectedOutput_map.size() == this->SelectedOutputMap.size()); ASSERT(this->PhreeqcPtr->SelectedOutput_map.size() == this->SelectedOutputStringMap.size()); - if (this->PhreeqcPtr->title_x != NULL) + if (!this->PhreeqcPtr->title_x.empty()) { ::sprintf(token, "TITLE"); this->PhreeqcPtr->dup_print(token, TRUE); if (this->PhreeqcPtr->pr.headings == TRUE) { - char *p = this->PhreeqcPtr->sformatf("%s\n\n", this->PhreeqcPtr->title_x); - this->PhreeqcPtr->output_msg(p); + this->PhreeqcPtr->output_msg(this->PhreeqcPtr->title_x.c_str()); + this->PhreeqcPtr->output_msg("\n\n"); } } @@ -1755,7 +1775,7 @@ void IPhreeqc::fpunchf(const char *name, const char *format, double d) ASSERT(this->SelectedOutputMap.find(this->PhreeqcPtr->current_selected_output->Get_n_user()) != this->SelectedOutputMap.end()); this->SelectedOutputMap[this->PhreeqcPtr->current_selected_output->Get_n_user()]->PushBackDouble(name, d); } - catch (std::bad_alloc) + catch (const std::bad_alloc&) { this->PhreeqcPtr->malloc_error(); } @@ -1774,7 +1794,7 @@ void IPhreeqc::fpunchf(const char *name, const char *format, char *s) ASSERT(this->SelectedOutputMap.find(this->PhreeqcPtr->current_selected_output->Get_n_user()) != this->SelectedOutputMap.end()); this->SelectedOutputMap[this->PhreeqcPtr->current_selected_output->Get_n_user()]->PushBackString(name, s); } - catch (std::bad_alloc) + catch (const std::bad_alloc&) { this->PhreeqcPtr->malloc_error(); } @@ -1793,7 +1813,7 @@ void IPhreeqc::fpunchf(const char *name, const char *format, int i) ASSERT(this->SelectedOutputMap.find(this->PhreeqcPtr->current_selected_output->Get_n_user()) != this->SelectedOutputMap.end()); this->SelectedOutputMap[this->PhreeqcPtr->current_selected_output->Get_n_user()]->PushBackLong(name, (long)i); } - catch (std::bad_alloc) + catch (const std::bad_alloc&) { this->PhreeqcPtr->malloc_error(); } diff --git a/IPhreeqc.f.inc b/IPhreeqc.f.inc index 97ead48c..ebe942f8 100644 --- a/IPhreeqc.f.inc +++ b/IPhreeqc.f.inc @@ -54,6 +54,7 @@ INTEGER(KIND=4) GetDumpStringLineCount LOGICAL(KIND=4) GetDumpStringOn LOGICAL(KIND=4) GetErrorFileOn + LOGICAL(KIND=4) GetErrorOn INTEGER(KIND=4) GetErrorStringLine INTEGER(KIND=4) GetErrorStringLineCount LOGICAL(KIND=4) GetLogFileOn @@ -78,6 +79,7 @@ INTEGER(KIND=4) SetDumpFileOn INTEGER(KIND=4) SetDumpStringOn INTEGER(KIND=4) SetErrorFileOn + INTEGER(KIND=4) SetErrorOn INTEGER(KIND=4) SetErrorStringOn INTEGER(KIND=4) SetLogFileName INTEGER(KIND=4) SetLogFileOn diff --git a/IPhreeqc.f90.inc b/IPhreeqc.f90.inc index 1d6d2ade..77cc6a35 100644 --- a/IPhreeqc.f90.inc +++ b/IPhreeqc.f90.inc @@ -147,6 +147,14 @@ END INTERFACE + INTERFACE + FUNCTION GetErrorOn(ID) + INTEGER(KIND=4), INTENT(IN) :: ID + LOGICAL(KIND=4) :: GetErrorOn + END FUNCTION GetErrorOn + END INTERFACE + + INTERFACE SUBROUTINE GetErrorStringLine(ID,N,LINE) INTEGER(KIND=4), INTENT(IN) :: ID @@ -496,6 +504,15 @@ END INTERFACE + INTERFACE + FUNCTION SetErrorOn(ID,ERR_ON) + INTEGER(KIND=4), INTENT(IN) :: ID + LOGICAL(KIND=4), INTENT(IN) :: ERR_ON + INTEGER(KIND=4) :: SetErrorOn + END FUNCTION SetErrorOn + END INTERFACE + + INTERFACE FUNCTION SetErrorStringOn(ID,ERR_STRING_ON) INTEGER(KIND=4), INTENT(IN) :: ID diff --git a/IPhreeqc.h b/IPhreeqc.h index cf60f878..93adac90 100644 --- a/IPhreeqc.h +++ b/IPhreeqc.h @@ -433,6 +433,25 @@ extern "C" { */ IPQ_DLL_EXPORT int GetErrorFileOn(int id); +/** + * Retrieves the current value of the error on switch. + * @param id The instance id returned from @ref CreateIPhreeqc. + * @return Non-zero if errors are generated, 0 (zero) otherwise. + * @see SetErrorOn + * @par Fortran90 Interface: + * @htmlonly + * + *
+ *  FUNCTION GetErrorOn(ID)
+ *    INTEGER(KIND=4),  INTENT(IN)  :: ID
+ *    LOGICAL(KIND=4)               :: GetErrorOn
+ *  END FUNCTION GetErrorOn
+ *  
+ *
+ * @endhtmlonly + */ + IPQ_DLL_EXPORT int GetErrorOn(int id); + /** * Retrieves the error messages from the last call to @ref RunAccumulated, @ref RunFile, @ref RunString, @ref LoadDatabase, or @ref LoadDatabaseString. @@ -1852,6 +1871,31 @@ Headings */ IPQ_DLL_EXPORT IPQ_RESULT SetErrorFileOn(int id, int error_on); +/** + * Sets the error switch on or off. This switch controls whether or not + * error messages are generated and displayed. The initial setting after calling + * @ref CreateIPhreeqc is on. + * @param id The instance id returned from @ref CreateIPhreeqc. + * @param error_on If non-zero, writes errors to the error file and error string; if zero, no errors are written to the error file or stored in the error string. + * @retval IPQ_OK Success. + * @retval IPQ_BADINSTANCE The given id is invalid. + * @see GetErrorOn, GetErrorStringLine, GetErrorStringLineCount, OutputErrorString + * @par Fortran90 Interface: + * @htmlonly + * + *
+ *  FUNCTION SetErrorOn(ID,ERR_ON)
+ *    INTEGER(KIND=4),  INTENT(IN)  :: ID
+ *    LOGICAL(KIND=4),  INTENT(IN)  :: ERR_ON
+ *    INTEGER(KIND=4)               :: SetErrorOn
+ *  END FUNCTION SetErrorOn
+ *  
+ *
+ * @endhtmlonly + */ + IPQ_DLL_EXPORT IPQ_RESULT SetErrorOn(int id, int error_on); + + /** * Sets the error string switch on or off. This switch controls whether or not the data normally sent * to the error file are stored in a buffer for retrieval. The initial setting after calling diff --git a/IPhreeqc.hpp b/IPhreeqc.hpp index 5a2b71ae..22bdf150 100644 --- a/IPhreeqc.hpp +++ b/IPhreeqc.hpp @@ -14,11 +14,7 @@ #include "Var.h" /* VRESULT */ #include "PHRQ_io.h" -#if defined(_WINDLL) -#define IPQ_DLL_EXPORT __declspec(dllexport) -#else -#define IPQ_DLL_EXPORT -#endif +#include "PHRQ_exports.h" class Phreeqc; class IErrorReporter; @@ -193,6 +189,14 @@ public: */ bool GetErrorFileOn(void)const; + /** + * Retrieves the current value of the error switch. + * @retval true Error messages are sent to the error file and to the string buffer + * @retval false No errors are sent. + * @see SetErrorOn + */ + bool GetErrorOn(void)const; + /** * Retrieves the error messages from the last call to @ref RunAccumulated, @ref RunFile, @ref RunString, @ref LoadDatabase, or @ref LoadDatabaseString. * @return A null terminated string containing error messages. @@ -768,12 +772,21 @@ public: /** * Sets the error file switch on or off. This switch controls whether or not * error messages are written to the phreeqc.id.err (where id is obtained from @ref GetId) file. - * The initial setting is false. + * The initial setting is true. * @param bValue If true, writes errors to the error file; if false, no errors are written to the error file. * @see GetErrorStringLine, GetErrorStringLineCount, GetErrorFileOn, OutputErrorString */ void SetErrorFileOn(bool bValue); + /** + * Sets the error switch on or off. This switch controls whether + * error messages are are generated and displayed. + * The initial setting is true. + * @param bValue If true, error messages are sent to the error file and error string buffer; if false, no error messages are generated. + * @see GetErrorOn, GetErrorStringLine, GetErrorStringLineCount, GetErrorFileOn, OutputErrorString + */ + void SetErrorOn(bool bValue); + /** * Sets the error string switch on or off. This switch controls whether or not the data normally sent * to the error file are stored in a buffer for retrieval. The initial setting is true. @@ -945,6 +958,26 @@ protected: std::vector< std::string > DumpLines; std::list< std::string > Components; + std::list< std::string > EquilibriumPhasesList; + const std::list &GetEquilibriumPhasesList() { return this->EquilibriumPhasesList; }; + std::list< std::string > GasComponentsList; + const std::list &GetGasComponentsList() { return this->GasComponentsList; }; + std::list< std::string > KineticReactionsList; + const std::list &GetKineticReactionsList() { return this->KineticReactionsList; }; + std::list< std::string > SolidSolutionComponentsList; + const std::list &GetSolidSolutionComponentsList() { return this->SolidSolutionComponentsList; }; + std::list< std::string > SolidSolutionNamesList; + const std::list &GetSolidSolutionNamesList() { return this->SolidSolutionNamesList; }; + //std::list< std::string > SurfaceSpeciesList; + //const std::list &GetSurfaceSpeciesList() { return this->SurfaceSpeciesList; }; + std::list< std::string > SurfaceTypeList; + const std::list &GetSurfaceTypeList() { return this->SurfaceTypeList; }; + std::list< std::string > SurfaceNamesList; + const std::list &GetSurfaceNamesList() { return this->SurfaceNamesList; }; + //std::list< std::string > ExchangeSpeciesList; + //const std::list &GetExchangeSpeciesList() { return this->ExchangeSpeciesList; }; + std::list< std::string > ExchangeNamesList; + const std::list &GetExchangeNamesList() { return this->ExchangeNamesList; }; std::map< int, std::string > SelectedOutputFileNameMap; diff --git a/IPhreeqcF.f b/IPhreeqcF.f index c5586dc6..856700ba 100644 --- a/IPhreeqcF.f +++ b/IPhreeqcF.f @@ -145,6 +145,18 @@ GetErrorFileOn = .TRUE. ENDIF END FUNCTION GetErrorFileOn +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + FUNCTION GetErrorOn(ID) + IMPLICIT NONE + INTEGER(KIND=4) :: ID + LOGICAL(KIND=4) :: GetErrorOn + INTEGER(KIND=4) :: GetErrorOnF + IF (GetErrorOnF(ID).EQ.0) THEN + GetErrorOn = .FALSE. + ELSE + GetErrorOn = .TRUE. + ENDIF + END FUNCTION GetErrorOn !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! GetErrorString !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -536,6 +548,15 @@ INTEGER(KIND=4) :: SetErrorFileOnF SetErrorFileOn = SetErrorFileOnF(ID,ERROR_ON) END FUNCTION SetErrorFileOn +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + FUNCTION SetErrorOn(ID,ERROR_ON) + IMPLICIT NONE + INTEGER(KIND=4) :: ID + LOGICAL(KIND=4) :: ERROR_ON + INTEGER(KIND=4) :: SetErrorOn + INTEGER(KIND=4) :: SetErrorOnF + SetErrorOn = SetErrorOnF(ID,ERROR_ON) + END FUNCTION SetErrorOn !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FUNCTION SetErrorStringOn(ID,ERROR_STRING_ON) IMPLICIT NONE diff --git a/IPhreeqcLib.cpp b/IPhreeqcLib.cpp index df489636..b57755ab 100644 --- a/IPhreeqcLib.cpp +++ b/IPhreeqcLib.cpp @@ -236,6 +236,24 @@ GetErrorFileOn(int id) return IPQ_BADINSTANCE; } +int +GetErrorOn(int id) +{ + IPhreeqc* IPhreeqcPtr = IPhreeqcLib::GetInstance(id); + if (IPhreeqcPtr) + { + if (IPhreeqcPtr->GetErrorOn()) + { + return 1; + } + else + { + return 0; + } + } + return IPQ_BADINSTANCE; +} + const char* GetErrorString(int id) { @@ -781,6 +799,7 @@ SetBasicCallback(int id, double (*fcn)(double x1, double x2, const char *str, vo } return IPQ_BADINSTANCE; } +#if !defined(R_SO) #ifdef IPHREEQC_NO_FORTRAN_MODULE IPQ_RESULT SetBasicFortranCallback(int id, double (*fcn)(double *x1, double *x2, char *str, size_t l)) @@ -805,7 +824,8 @@ SetBasicFortranCallback(int id, double (*fcn)(double *x1, double *x2, const char } return IPQ_BADINSTANCE; } -#endif +#endif /* IPHREEQC_NO_FORTRAN_MODULE */ +#endif /* !defined(R_SO) */ IPQ_RESULT SetCurrentSelectedOutputUserNumber(int id, int n) { @@ -882,6 +902,18 @@ SetErrorFileOn(int id, int value) return IPQ_BADINSTANCE; } +IPQ_RESULT +SetErrorOn(int id, int value) +{ + IPhreeqc* IPhreeqcPtr = IPhreeqcLib::GetInstance(id); + if (IPhreeqcPtr) + { + IPhreeqcPtr->SetErrorOn(value != 0); + return IPQ_OK; + } + return IPQ_BADINSTANCE; +} + IPQ_RESULT SetErrorStringOn(int id, int value) { diff --git a/IPhreeqc_interface.F90 b/IPhreeqc_interface.F90 index c8b30e71..6c726c83 100644 --- a/IPhreeqc_interface.F90 +++ b/IPhreeqc_interface.F90 @@ -289,6 +289,22 @@ LOGICAL FUNCTION GetErrorFileOn(id) return END FUNCTION GetErrorFileOn +LOGICAL FUNCTION GetErrorOn(id) + USE ISO_C_BINDING + IMPLICIT NONE + INTERFACE + INTEGER(KIND=C_INT) FUNCTION GetErrorOnF(id) & + BIND(C, NAME='GetErrorOnF') + USE ISO_C_BINDING + IMPLICIT NONE + INTEGER(KIND=C_INT), INTENT(in) :: id + END FUNCTION GetErrorOnF + END INTERFACE + INTEGER, INTENT(in) :: id + GetErrorOn = (GetErrorOnF(id) .ne. 0) + return +END FUNCTION GetErrorOn + INTEGER FUNCTION GetErrorStringLineCount(id) USE ISO_C_BINDING IMPLICIT NONE @@ -680,8 +696,8 @@ INTEGER FUNCTION GetSelectedOutputValue(id, row, col, vtype, dvalue, svalue, sle sz_fortran = sz GetSelectedOutputValue = GetSelectedOutputValueF(id, row, col, vtype, dvalue, svalue, sz) if (present(slength)) then - slength = 0 - if (sz > sz_fortran) then + slength = sz_fortran + if (sz < sz_fortran) then slength = sz endif endif @@ -1049,6 +1065,26 @@ INTEGER FUNCTION SetErrorFileOn(id, error_file_on) return END FUNCTION SetErrorFileOn +INTEGER FUNCTION SetErrorOn(id, error_on) + USE ISO_C_BINDING + IMPLICIT NONE + INTERFACE + INTEGER(KIND=C_INT) FUNCTION SetErrorOnF(id, error_on) & + BIND(C, NAME='SetErrorOnF') + USE ISO_C_BINDING + IMPLICIT NONE + INTEGER(KIND=C_INT), INTENT(in) :: id, error_on + END FUNCTION SetErrorOnF + END INTERFACE + INTEGER, INTENT(in) :: id + LOGICAL, INTENT(in) :: error_on + INTEGER :: tf = 0 + tf = 0 + if (error_on) tf = 1 + SetErrorOn = SetErrorOnF(id, tf) + return +END FUNCTION SetErrorOn + INTEGER FUNCTION SetErrorStringOn(id, error_string_on) USE ISO_C_BINDING IMPLICIT NONE diff --git a/IPhreeqc_interface_F.cpp b/IPhreeqc_interface_F.cpp index a6e3f465..ab1125de 100644 --- a/IPhreeqc_interface_F.cpp +++ b/IPhreeqc_interface_F.cpp @@ -4,6 +4,7 @@ #include /* memcpy */ #include /* assert */ #include /* sprintf */ +#include #include "phrqtype.h" #include "IPhreeqc.h" @@ -30,13 +31,13 @@ padfstring(char *dest, const char *src, int* len) { int sofar, c_len; - c_len = (int)strlen(src); + c_len = (int)strlen(src); for (sofar = 0; (sofar < *len) && (*src != '\0'); ++sofar) *dest++ = *src++; while (sofar++ < *len) *dest++ = ' '; - *len = c_len; + *len = c_len; } IPQ_RESULT @@ -144,6 +145,12 @@ GetErrorFileOnF(int *id) return ::GetErrorFileOn(*id); } +int +GetErrorOnF(int *id) +{ + return ::GetErrorOn(*id); +} + /* GetErrorStringF */ @@ -456,6 +463,12 @@ SetErrorFileOnF(int *id, int* error_file_on) return ::SetErrorFileOn(*id, *error_file_on); } +IPQ_RESULT +SetErrorOnF(int *id, int* error_on) +{ + return ::SetErrorOn(*id, *error_on); +} + IPQ_RESULT SetErrorStringOnF(int *id, int* error_string_on) { diff --git a/IPhreeqc_interface_F.h b/IPhreeqc_interface_F.h index 660dcd38..d3fe29ec 100644 --- a/IPhreeqc_interface_F.h +++ b/IPhreeqc_interface_F.h @@ -1,11 +1,7 @@ #ifndef __IPHREEQC_INTERFACE__H #define __IPHREEQC_INTERFACE__H -#if defined(_WINDLL) -#define IPQ_DLL_EXPORT __declspec(dllexport) -#else -#define IPQ_DLL_EXPORT -#endif +#include "PHRQ_exports.h" #ifdef SKIP #if defined(FC_FUNC) @@ -25,6 +21,7 @@ #define GetDumpStringOnF FC_FUNC (getdumpstringonf, GETDUMPSTRINGONF) #define GetErrorFileNameF FC_FUNC (geterrorfilenamef, GETERRORFILENAMEF) #define GetErrorFileOnF FC_FUNC (geterrorfileonf, GETERRORFILEONF) +#define GetErrorOnF FC_FUNC (geterroronf, GETERRORONF) #define GetErrorStringLineF FC_FUNC (geterrorstringlinef, GETERRORSTRINGLINEF) #define GetErrorStringLineCountF FC_FUNC (geterrorstringlinecountf, GETERRORSTRINGLINECOUNTF) #define GetErrorStringOnF FC_FUNC (geterrorstringonf, GETERRORSTRINGONF) @@ -66,6 +63,7 @@ #define SetDumpStringOnF FC_FUNC (setdumpstringonf, SETDUMPSTRINGONF) #define SetErrorFileNameF FC_FUNC (seterrorfilenamef, SETERRORFILENAMEF) #define SetErrorFileOnF FC_FUNC (seterrorfileonf, SETERRORFILEONF) +#define SetErrorOnF FC_FUNC (seterroronf, SETERRORONF) #define SetErrorStringOnF FC_FUNC (seterrorstringonf, SETERRORSTRINGONF) #define SetLogFileNameF FC_FUNC (setlogfilenamef, SETLOGFILENAMEF) #define SetLogFileOnF FC_FUNC (setlogfileonf, SETLOGFILEONF) @@ -99,6 +97,7 @@ extern "C" { IPQ_DLL_EXPORT int GetDumpStringOnF(int *id); IPQ_DLL_EXPORT void GetErrorFileNameF(int *id, char* filename, int* filename_length); IPQ_DLL_EXPORT int GetErrorFileOnF(int *id); + IPQ_DLL_EXPORT int GetErrorOnF(int *id); IPQ_DLL_EXPORT void GetErrorStringLineF(int *id, int* n, char* line, int* line_length); IPQ_DLL_EXPORT int GetErrorStringLineCountF(int *id); IPQ_DLL_EXPORT int GetErrorStringOnF(int *id); @@ -144,6 +143,7 @@ extern "C" { IPQ_DLL_EXPORT IPQ_RESULT SetDumpStringOnF(int *id, int* dump_string_on); IPQ_DLL_EXPORT IPQ_RESULT SetErrorFileNameF(int *id, char* fname); IPQ_DLL_EXPORT IPQ_RESULT SetErrorFileOnF(int *id, int* error_file_on); + IPQ_DLL_EXPORT IPQ_RESULT SetErrorOnF(int *id, int* error_on); IPQ_DLL_EXPORT IPQ_RESULT SetErrorStringOnF(int *id, int* error_string_on); IPQ_DLL_EXPORT IPQ_RESULT SetLogFileNameF(int *id, char* fname); IPQ_DLL_EXPORT IPQ_RESULT SetLogFileOnF(int *id, int* log_file_on); diff --git a/Makefile.am b/Makefile.am index 9ca76526..bd994e65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ libiphreeqc_la_SOURCES=\ phreeqcpp/common/Parser.h\ phreeqcpp/common/PHRQ_base.cxx\ phreeqcpp/common/PHRQ_base.h\ + phreeqcpp/common/PHRQ_exports.h\ phreeqcpp/common/PHRQ_io.cpp\ phreeqcpp/common/PHRQ_io.h\ phreeqcpp/common/phrqtype.h\ diff --git a/Var.h b/Var.h index 39ae1e71..0169bd16 100644 --- a/Var.h +++ b/Var.h @@ -6,11 +6,7 @@ #ifndef __VAR_H_INC #define __VAR_H_INC -#if defined(_WINDLL) -#define IPQ_DLL_EXPORT __declspec(dllexport) -#else -#define IPQ_DLL_EXPORT -#endif +#include "PHRQ_exports.h" #if defined(R_SO) || defined(NO_NAMELESS_UNION) #define VAR_UNION_NAME u diff --git a/Version.h b/Version.h index 562e2f53..94c88663 100644 --- a/Version.h +++ b/Version.h @@ -3,6 +3,7 @@ #define VER_MINOR 0 #define VER_PATCH 0 #define VER_REVISION 0 +#define GIT_COMMIT 0000000000000000000000000000000000000000 #define RELEASE_DATE "@RELEASE_DATE@" diff --git a/fimpl.h b/fimpl.h index e01095f6..de9eb214 100644 --- a/fimpl.h +++ b/fimpl.h @@ -63,6 +63,10 @@ IPQ_DLL_EXPORT int IPQ_DECL IPQ_CASE_UND(geterrorfileon, GETERRORFILEON, geterr { return GetErrorFileOnF(id); } +IPQ_DLL_EXPORT int IPQ_DECL IPQ_CASE_UND(geterroron, GETERRORON, geterroron_, GETERRORON_)(int *id) +{ + return GetErrorOnF(id); +} // GetErrorString IPQ_DLL_EXPORT void IPQ_DECL IPQ_CASE_UND(geterrorstringline, GETERRORSTRINGLINE, geterrorstringline_, GETERRORSTRINGLINE_)(int *id, int *n, char* line, size_t line_length) { @@ -232,6 +236,10 @@ IPQ_DLL_EXPORT int IPQ_DECL IPQ_CASE_UND(seterrorfileon, SETERRORFILEON, seterr { return SetErrorFileOnF(id, error_on); } +IPQ_DLL_EXPORT int IPQ_DECL IPQ_CASE_UND(seterroron, SETERRORON, seterroron_, SETERRORON_)(int *id, int *error_on) +{ + return SetErrorOnF(id, error_on); +} IPQ_DLL_EXPORT int IPQ_DECL IPQ_CASE_UND(seterrorstringon, SETERRORSTRINGON, seterrorstringon_, SETERRORSTRINGON_)(int *id, int *error_string_on) { return SetErrorStringOnF(id, error_string_on); diff --git a/fwrap.cpp b/fwrap.cpp index ae23e5dd..2c096f93 100644 --- a/fwrap.cpp +++ b/fwrap.cpp @@ -174,6 +174,12 @@ GetErrorFileOnF(int *id) return ::GetErrorFileOn(*id); } +int +GetErrorOnF(int *id) +{ + return ::GetErrorOn(*id); +} + /* GetErrorStringF */ @@ -539,6 +545,12 @@ SetErrorFileOnF(int *id, int* error_file_on) return ::SetErrorFileOn(*id, *error_file_on); } +IPQ_RESULT +SetErrorOnF(int *id, int* error_on) +{ + return ::SetErrorOn(*id, *error_on); +} + IPQ_RESULT SetErrorStringOnF(int *id, int* error_string_on) { diff --git a/fwrap.h b/fwrap.h index 0f2740f5..c95758d0 100644 --- a/fwrap.h +++ b/fwrap.h @@ -25,6 +25,7 @@ #define GetDumpStringOnF FC_FUNC (getdumpstringonf, GETDUMPSTRINGONF) #define GetErrorFileNameF FC_FUNC (geterrorfilenamef, GETERRORFILENAMEF) #define GetErrorFileOnF FC_FUNC (geterrorfileonf, GETERRORFILEONF) +#define GetErrorOnF FC_FUNC (geterroronf, GETERRORONF) #define GetErrorStringLineF FC_FUNC (geterrorstringlinef, GETERRORSTRINGLINEF) #define GetErrorStringLineCountF FC_FUNC (geterrorstringlinecountf, GETERRORSTRINGLINECOUNTF) #define GetErrorStringOnF FC_FUNC (geterrorstringonf, GETERRORSTRINGONF) @@ -66,6 +67,7 @@ #define SetDumpStringOnF FC_FUNC (setdumpstringonf, SETDUMPSTRINGONF) #define SetErrorFileNameF FC_FUNC (seterrorfilenamef, SETERRORFILENAMEF) #define SetErrorFileOnF FC_FUNC (seterrorfileonf, SETERRORFILEONF) +#define SetErrorOnF FC_FUNC (seterroronf, SETERRORONF) #define SetErrorStringOnF FC_FUNC (seterrorstringonf, SETERRORSTRINGONF) #define SetLogFileNameF FC_FUNC (setlogfilenamef, SETLOGFILENAMEF) #define SetLogFileOnF FC_FUNC (setlogfileonf, SETLOGFILEONF) @@ -98,6 +100,7 @@ extern "C" { int GetDumpStringOnF(int *id); void GetErrorFileNameF(int *id, char* filename, size_t filename_length); int GetErrorFileOnF(int *id); + int GetErrorOnF(int *id); void GetErrorStringLineF(int *id, int* n, char* line, size_t line_length); int GetErrorStringLineCountF(int *id); int GetErrorStringOnF(int *id); @@ -139,6 +142,7 @@ extern "C" { IPQ_RESULT SetDumpStringOnF(int *id, int* dump_string_on); IPQ_RESULT SetErrorFileNameF(int *id, char* fname, size_t fname_length); IPQ_RESULT SetErrorFileOnF(int *id, int* error_file_on); + IPQ_RESULT SetErrorOnF(int *id, int* error_on); IPQ_RESULT SetErrorStringOnF(int *id, int* error_string_on); IPQ_RESULT SetLogFileNameF(int *id, char* fname, size_t fname_length); IPQ_RESULT SetLogFileOnF(int *id, int* log_file_on); diff --git a/phreeqcpp/.gitlab-ci.yml b/phreeqcpp/.gitlab-ci.yml new file mode 100644 index 00000000..25c56f8c --- /dev/null +++ b/phreeqcpp/.gitlab-ci.yml @@ -0,0 +1,111 @@ +# +# https://code.chs.usgs.gov/coupled/subtrees/phreeqc3-src +# SRC 2020-12-02T18:39:55-07:00 +# +image: ${CI_REGISTRY}/coupled/containers/buildpack-deps:bionic-scm + +stages: + - sync + - trigger + +before_script: + - eval $(ssh-agent -s) + - echo "${SSH_PRIVATE_KEY_ENC}" | base64 --decode | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - ssh-keyscan ${CI_SERVER_HOST} >> ~/.ssh/known_hosts + - chmod 644 ~/.ssh/known_hosts + - git config --global user.email "darth@empire.com" + - git config --global user.name "Darth Vader" + +subtree-sync: + stage: sync + + ## + ## Only run if on the master branch and the variable GROUP is set + ## + ## change this to + ## only: + ## - master@$GROUP/subtrees/phreeqc3-src + ## and set GROUP to coupled before merge + only: + refs: + - master + variables: + - $GROUP + + script: + ## + ## Must re-clone in order for the subtree merge to work + ## tried re-setting the url for the origin but didn't work + ## + - cd .. + - rm -rf ${CI_PROJECT_NAME} + - git clone git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git + - cd ${CI_PROJECT_NAME} + + ## + ## Sync subtrees + ## + - | + #!/bin/bash -ex + # + # phreeqc3/ git@${CI_SERVER_HOST}:${GROUP}/phreeqc3.git + # ├─database/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-database.git database + # ├─doc/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-doc.git doc + # ├─examples/ ├─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-examples.git examples + # └─src/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src.git src + # └─common/ └─git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src-common.git src/common + + git_subtree() { + git subtree "${1}" --prefix="${2}" "${4}" master 2>&1 | grep -v "^[[:digit:]].*/[[:digit:]].*" + } + + declare -A urls=( \ + ["phreeqc3-src-common"]="git@${CI_SERVER_HOST}:${GROUP}/subtrees/phreeqc3-src-common.git" \ + ) + + declare -A prefixes=( \ + ["phreeqc3-src-common"]="common" \ + ) + + export GIT_EDITOR=true + + for remote in "${!urls[@]}"; do + git_subtree "pull" "${prefixes[$remote]}" "$remote" "${urls[$remote]}" + done + + for remote in "${!urls[@]}"; do + git_subtree "push" "${prefixes[$remote]}" "$remote" "${urls[$remote]}" + done + + git push origin master + git status + +trigger-downstream: + stage: trigger + ## + ## Only run if on the master branch and the variable GROUP is set + ## + ## change this to + ## only: + ## - master@$GROUP/subtrees/phreeqc3-src + ## and set GROUP to coupled before merge + only: + refs: + - master + variables: + - $GROUP + + ## Downstream Projects + ## triggers and ids are stored at the group level + ## iphreeqc-src https://code.chs.usgs.gov/coupled/subtrees/iphreeqc-src + ## phreeqc3 https://code.chs.usgs.gov/coupled/phreeqc3 + script: + - echo triggering iphreeqc-src + - curl -X POST -F token=${IPHREEQC_SRC_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${IPHREEQC_SRC_ID}/trigger/pipeline + - echo triggering phreeqc3 + - curl -X POST -F token=${PHREEQC3_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${PHREEQC3_ID}/trigger/pipeline + + ## Upstream Projects + ## phreeqc3-src-common https://code.chs.usgs.gov/coupled/subtrees/phreeqc3-src-common diff --git a/phreeqcpp/ChartHandler.cpp b/phreeqcpp/ChartHandler.cpp new file mode 100644 index 00000000..b9236865 --- /dev/null +++ b/phreeqcpp/ChartHandler.cpp @@ -0,0 +1,234 @@ +// ChartHandler.cpp: implementation of the ChartHandler class. +// +////////////////////////////////////////////////////////////////////// +#if defined MULTICHART +#include "Phreeqc.h" +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "ChartHandler.h" +#include + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +ChartHandler::ChartHandler(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for ChartHandler + // +{ + current_chart = NULL; + current_chart_n_user = -1000; + u_g_defined = false; + timer = true; + active_charts = 0; +} + +ChartHandler::~ChartHandler() +{ + std::map::iterator it; + for (it = this->chart_map.begin(); it != chart_map.end(); it++) + { + delete it->second; + } +} +void +ChartHandler::Punch_user_graph(Phreeqc * phreeqc_ptr) +{ + std::map::iterator it = this->chart_map.begin(); + for ( ; it != chart_map.end(); it++) + { + if (it->second->Get_active()) + { +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 4, 0)) + { + System::Threading::Thread::Sleep(5); + } +#endif + try + { + this->current_chart = it->second; + phreeqc_ptr-> punch_user_graph(); + } + catch (...) + { +#if defined(__cplusplus_cli) + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 4); +#endif + throw; + } +#if defined(__cplusplus_cli) + System::Threading::Interlocked::Exchange(it->second->usingResource, 0); +#endif + } + } +} + +bool +ChartHandler::Read(Phreeqc * phreeqc_ptr, CParser &parser) +{ + int n_user; + std::string token; + + // reads line, next character is after keyword + parser.check_line("ChartHandler", true, false, true, false); + + std::istringstream iss(parser.line()); + // keyword + iss >> token; + // number + if (!(iss >> n_user)) + { + n_user = 1; + } + + // makes new ChartObject if necessary + std::map::iterator it = this->chart_map.find(n_user); + if (it == this->chart_map.end()) + { + chart_map[n_user] = new ChartObject(this->Get_io()); + it = this->chart_map.find(n_user); + it->second->Set_phreeqc(phreeqc_ptr); + } + + // Read/update ChartObject +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 5, 0)) + { + System::Threading::Thread::Sleep(5); + } +#endif + try + { + { + it->second->Read(parser); + current_chart_n_user = n_user; + current_chart = it->second; + u_g_defined = true; + } + + // if detached, set timer_end and free + if (it->second->Get_detach() && it->second->Get_form_started()) + { + it->second->Set_end_timer(true); + it->second->Rate_free(); + } + } + catch(...) + { +#if defined(__cplusplus_cli) + // Release lock + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 5); + throw; +#endif + } +#if defined(__cplusplus_cli) + // Release lock + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 5); +#endif + + // if detached, wait for thread to acknowledge and then erase chart + if (it->second->Get_detach()) + { + while (it->second->Get_form_started() && it->second->Get_done() != true) + { +#if defined(__cplusplus_cli) + System::Threading::Thread::Sleep(5); +#endif + } + delete it->second; + this->chart_map.erase(it); + } + return true; +} +bool +ChartHandler::End_timer() +{ + + size_t max_tries = 6000; // 1 h, but not used + std::map::iterator it = this->chart_map.begin(); + if (chart_map.size() > 0) + { + screen_msg("Detaching charts..."); + if (io != NULL) + { + io->error_flush(); + } + } + size_t i(0), i2(0); + for ( ; it != chart_map.end(); it++) + { + i = 0; + it->second->Rate_free(); + if (it->second->Get_form_started()) + { +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 6, 0)) + { + //if (i > max_tries) break; + i++; + System::Threading::Thread::Sleep(60); + } +#endif + it->second->Set_end_timer(true); + //it->second->Set_phreeqc(NULL); +#if defined(__cplusplus_cli) + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 6); +#endif + + i2 = 0; + while (it->second->Get_done() != true) + { + //if (i2 > max_tries) break; + i2++; +#if defined(__cplusplus_cli) + System::Threading::Thread::Sleep(60); +#endif + } + //if (i >= max_tries || i2 >= max_tries) + //{ + // error_msg("\nChart did not respond.", CONTINUE); + //} + } + } + if (chart_map.size() > 0) + { + screen_msg("\rCharts detached. \n"); + if (io != NULL) + { + io->error_flush(); + } + } + this->timer = false; + + return true; +} +bool +ChartHandler::dump(std::ostream & oss, unsigned int indent) +{ + std::map::iterator it = this->chart_map.begin(); + for ( ; it != chart_map.end(); it++) + { + size_t i = 0; + it->second->dump(oss, indent); + } + return true; +} +#endif + diff --git a/phreeqcpp/ChartHandler.h b/phreeqcpp/ChartHandler.h new file mode 100644 index 00000000..f0ca405b --- /dev/null +++ b/phreeqcpp/ChartHandler.h @@ -0,0 +1,59 @@ +#if !defined(CHARTHANDLER_H_INCLUDED) +#define CHARTHANDLER_H_INCLUDED +#if defined MULTICHART +#include +#include +#include +#include "Parser.h" +#include "ChartObject.h" +#include "PHRQ_base.h" + +class ChartHandler: public PHRQ_base +{ + +public: + ChartHandler(PHRQ_io *io = NULL); + virtual ~ChartHandler(); + + size_t Get_chart_count()const + { + return this->chart_map.size(); + } + ChartObject * Get_current_chart() + { + return this->current_chart; + } + const ChartObject * Get_current_chart()const + { + return this->current_chart; + } + bool Get_timer() + { + return timer; + } + int Get_active_charts() {return this->active_charts;} + void Increment_active_charts() + { + System::Threading::Interlocked::Increment(this->active_charts); + } + void Decrement_active_charts() + { + System::Threading::Interlocked::Decrement(this->active_charts); + } + bool Read(Phreeqc * phreeqc_ptr, CParser &parser); + void Punch_user_graph(Phreeqc * phreeqc_ptr); + bool End_timer(); + bool dump(std::ostream & oss, unsigned int indent); +protected: + std::map chart_map; + int current_chart_n_user; + ChartObject * current_chart; + bool u_g_defined; + bool timer; + int active_charts; + +public: + +}; +#endif // MULTICHART +#endif // !defined(CHARTHANDLER_H_INCLUDED) diff --git a/phreeqcpp/ChartObject.cpp b/phreeqcpp/ChartObject.cpp new file mode 100644 index 00000000..9d6513ce --- /dev/null +++ b/phreeqcpp/ChartObject.cpp @@ -0,0 +1,1382 @@ +// ChartObject.cpp: implementation of the ChartObject class. +// +////////////////////////////////////////////////////////////////////// +#ifdef MULTICHART +#include "Phreeqc.h" +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "Utils.h" +#include +#include "ChartObject.h" +#include "Parser.h" +#include +#include +#include +#include "phqalloc.h" + +#include "Form1.h" +using namespace zdg_ui2; + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +ChartObject::ChartObject(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : +cxxNumKeyword(io) +{ + new_ug = false; + FirstCallToUSER_GRAPH = true; + + update_time_chart = 150; + PanelHeight = 510; + PanelWidth = 640; + + Symbol_map["Square"] = 0; + Symbol_map["Diamond"] = 1; + Symbol_map["Triangle"] = 2; + Symbol_map["Circle"] = 3; + Symbol_map["XCross"] = 4; + Symbol_map["Plus"] = 5; + Symbol_map["Star"] = 6; + Symbol_map["TriangleDown"] = 7; + Symbol_map["HDash"] = 8; + Symbol_map["VDash"] = 9; + Symbol_map["None"] = 10; + + Color_vector.push_back("Red"); + Color_vector.push_back("Green"); + Color_vector.push_back("Blue"); + Color_vector.push_back("Orange"); + Color_vector.push_back("Magenta"); +// Color_vector.push_back("Yellow"); + Color_vector.push_back("Black"); + Color_vector.push_back("Cyan"); + Color_vector.push_back("Brown"); + Color_vector.push_back("Lime"); + Color_vector.push_back("Gray"); + + chart_title.clear(); + axis_titles.clear(); + + int i; + for (i = 0; i < 5; i++) + { + axis_scale_x[i] = NA; + axis_scale_y[i] = NA; + axis_scale_y2[i] = NA; + } + + chart_type = 0; + graph_initial_solutions = false; + shifts_as_points = false; + connect_simulations = true; + colnr = 0; + ColumnOffset = 0; + prev_advection_step = 0; + prev_transport_step = 0; + prev_sim_no = 0; + end_timer = false; + done = false; + + curve_added = false; + point_added = false; + + user_graph = new rate; + user_graph->commands.clear(); + user_graph->name = NULL; + user_graph->new_def = 0; + user_graph->linebase = user_graph->loopbase = user_graph->varbase = NULL; + + default_symbol = 0; + default_symbol_csv = 0; + default_color = 0; + default_color_csv = 0; + + graph_x = NA; + graph_y.clear(); + secondary_y.clear(); + + usingResource = 0; + form_started = false; + active = true; + detach = false; + + batch_background = true; + batch_grid = true; + batch = ChO_NO_BATCH; +} + +ChartObject::~ChartObject() +{ + while (0 != System::Threading::Interlocked::CompareExchange(this->usingResource, 7, 0)) + { + System::Threading::Thread::Sleep(5); + } + this->Rate_free(); + delete this->user_graph; + + std::vector::iterator it; + for (it = this->CurvesCSV.begin(); it != CurvesCSV.end(); it++) + { + delete *it; + } + + for (it = this->Curves.begin(); it != Curves.end(); it++) + { + delete *it; + } +} + +bool +ChartObject::Set_axis_scale(CParser & parser) +{ + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + LDBLE *scale_ptr = NULL; + std::vector string_vector; + size_t j = 0; + + // rereads option + parser.copy_token(token, next_char); + + // reads parameters + while ((j < 6) && (parser.copy_token(token, next_char)) != CParser::TT_EMPTY) + { + string_vector.push_back(token); + j++; + } + if (string_vector.size() == 0) + { + error_msg("No axis defined for scales", PHRQ_io::OT_CONTINUE); + return false; + } + std::string str = string_vector[0]; + std::string type; + Utilities::str_tolower(str); + if (str.substr(0,2) == "y2" || str.substr(0,1) == "s") + { + type = "sy"; + scale_ptr = this->axis_scale_y2; + } + else if (str.substr(0,1) == "y") + { + type = "y"; + scale_ptr = this->axis_scale_y; + } + else if (str.substr(0,1) == "x") + { + type = "x"; + scale_ptr = this->axis_scale_x; + } + else + { + std::ostringstream estream; + estream << "Found " << str; + estream << ", but expect axis type \'x\', \'y\', \'y2\'or \'sy\'."; + estream << "\n"; + error_msg(estream.str().c_str(), PHRQ_io::OT_CONTINUE); + return false; + } + + for (j = 1; j < string_vector.size() && j < 5; j++) + { + std::string s = string_vector[j]; + if (s[0] == 'a' || s[0] == 'A') + { + scale_ptr[j - 1] = NA; + } + else if (CParser::token_type(s) == CParser::TT_DIGIT) + { + scale_ptr[j - 1] = atof(s.c_str()); + } + else + { + std::ostringstream estream; + estream << "Found " << s; + estream << ", but expect number or 'a(uto)'."; + estream << "\n"; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + } + if (string_vector.size() == 6) + { + std::string s = string_vector[5]; + if (s[0] == 't' || s[0] == 'T' || s[0] == 'l' || s[0] == 'L') + { + scale_ptr[4] = 10.0; + if (((fabs(scale_ptr[0] - NA) > 1e-3) && scale_ptr[0] <=0) || + ((fabs(scale_ptr[1] - NA) > 1e-3) && scale_ptr[1] <=0)) + { + std::ostringstream estream; + estream << "MIN and MAX must be > 0 for log " << type << "-scale."; + estream << "\n"; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + + } + } + if ((fabs(scale_ptr[0] - NA) > 1e-3) && (fabs(scale_ptr[1] - NA) > 1e-3)) + { + if (scale_ptr[0] > scale_ptr[1]) + { + std::ostringstream estream; + estream << "Maximum must be larger than minimum of axis_scale " << type << "-scale." << "\n"; + estream << "Switching values for MIN and MAX. " << "\n"; + warning_msg(estream.str().c_str()); + LDBLE t; + t = scale_ptr[0]; + scale_ptr[0] = scale_ptr[1]; + scale_ptr[1] = scale_ptr[0]; + return false; + } + } + return true; +} + +bool +ChartObject::Read(CParser & parser) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + bool new_command_lines(false); + // if no definitions in USER_GRAPH data block, deactivate plot + bool no_def(true); + if (this->FirstCallToUSER_GRAPH) + { + this->new_ug = true; + } + else + { + this->ColumnOffset = (int) this->Curves.size(); + this->new_ug = true; + } + this->new_plotxy_curves.clear(); + + // Read number and description + { + this->read_number_description(parser); + } + + opt_save = CParser::OPT_DEFAULT; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + error_msg("Unknown input in USER_GRAPH keyword.", CONTINUE); + error_msg(parser.line().c_str(), CONTINUE); + useLastLine = false; + break; + case 0: /* start */ + opt_save = CParser::OPT_DEFAULT; + break; + case 1: /* end */ + opt_save = CParser::OPT_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + this->new_headings.clear(); + while (parser.copy_token(token, next_char) != CParser::TT_EMPTY) + { + this->new_headings.push_back(token); + } + this->headings_original = this->new_headings; + break; + case 4: /* chart title */ + { + std::string tok; + parser.get_rest_of_line(tok); + std::string::iterator b = tok.begin(); + std::string::iterator e = tok.end(); + CParser::copy_title(this->chart_title, b, e); + } + break; + case 5: /* axis titles */ + { + this->axis_titles.clear(); + std::string l; + parser.get_rest_of_line(l); + std::string tok; + std::string::iterator b = l.begin(); + std::string::iterator e = l.end(); + this->axis_titles.clear(); + while(parser.copy_title(tok, b, e) != CParser::TT_EMPTY) + { + this->axis_titles.push_back(tok); + } + } + while (this->axis_titles.size() < 3) this->axis_titles.push_back(""); + break; + case 6: /* axis scales */ + { + this->Set_axis_scale(parser); + } + break; + case 7: /* initial_solutions */ + this->graph_initial_solutions = parser.get_true_false(next_char, FALSE); + break; + case 8: /* plot_concentration_vs */ + parser.copy_token(token, next_char); + Utilities::str_tolower(token); + if (token[0] == 'x' || token[0] == 'd') + chart_type = 0; + else if (token[0] == 't') + chart_type = 1; + else + { + std::ostringstream estream; + estream << "Found " << token << ", but expect plot type: (\'x\' or \'dist\') for distance, (\'t\') for time."; + error_msg(estream.str().c_str(), CONTINUE); + } + break; + case 9: /* shifts_as_points */ + this->shifts_as_points = parser.get_true_false(next_char, true); + if (this->shifts_as_points) + this->chart_type = 0; + else + this->chart_type = 1; + break; + case 10: /* grid_offset */ + break; + case 11: /* connect_simulations */ + this->connect_simulations = parser.get_true_false(next_char, true); + break; + case 12: /* plot_csv_file */ + case 17: /* plot_tsv_file */ + { + std::string file_name; + parser.get_rest_of_line(file_name); + file_name = trim(file_name); + this->OpenCSVFile(file_name); + this->csv_file_names.push_back(file_name); + } + break; + case 13: /* clear */ + case 14: /* detach */ + this->detach = true; + break; + /* End of modifications */ + case 15: /* active */ + this->active = parser.get_true_false(next_char, true); + if (this->active && this->Get_connect_simulations()) + { + this->ColumnOffset = 0;//this->Curves.size(); + this->new_ug = false; + } + break; + /* End of modifications */ + case 16: /* batch */ + { + this->batch = ChartObject::ChO_BATCH_ONLY; + std::string rest_of_line, lc_rest_of_line; + parser.get_rest_of_line(rest_of_line); + lc_rest_of_line = rest_of_line; + Utilities::str_tolower(lc_rest_of_line); + + std::vector file_types; + file_types.push_back(".emf"); + file_types.push_back(".png"); + file_types.push_back(".jpg"); + file_types.push_back(".gif"); + file_types.push_back(".tiff"); + file_types.push_back(".bmp"); + file_types.push_back(".jpeg"); + + size_t first, last; + size_t i; + for (i = 0; i < file_types.size(); i++) + { + first = lc_rest_of_line.rfind(file_types[i].c_str()); + if (first != std::string::npos) + { + break; + } + } + if (i >= file_types.size()) + { + std::ostringstream estream; + estream << "Batch file name must have suffix emf, png, jpg, jpeg, gif, tiff, or bmp."; + error_msg(estream.str().c_str(), CONTINUE); + break; + } + switch (i) + { + case 0: + this->batch = ChartObject::ChO_EMF; + last = first + 4; + break; + case 1: + this->batch = ChartObject::ChO_PNG; + last = first + 4; + break; + case 2: + this->batch = ChartObject::ChO_JPG; + last = first + 4; + break; + case 3: + this->batch = ChartObject::ChO_GIF; + last = first + 4; + break; + case 4: + this->batch = ChartObject::ChO_TIFF; + last = first + 5; + break; + case 5: + this->batch = ChartObject::ChO_BMP; + last = first + 4; + break; + case 6: + this->batch = ChartObject::ChO_JPG; + last = first + 5; + break; + } + + this->batch_fn = rest_of_line.substr(0, last); + if (last+1 < rest_of_line.size()) + { + token = rest_of_line.substr(last); + token = trim(token); + std::string tf; + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + + CParser::copy_token(tf, b, e); + if (tf.size() > 0) + { + Utilities::str_tolower(tf); + if (tf[0] == 'f') + { + this->batch_background = false; + } + } + CParser::copy_token(tf, b, e); + if (tf.size() > 0) + { + Utilities::str_tolower(tf); + if (tf[0] == 'f') + { + this->batch_grid = false; + } + } + } + } + break; + case CParser::OPT_DEFAULT: // Read Basic commands + { + if (!new_command_lines) + { + this->rate_command_list.clear(); + this->rate_command_list_original.clear(); + new_command_lines = true; + } + this->rate_new_def = true; + /* read command */ + std::string cmd(parser.line()); + this->rate_command_list_original.push_back(cmd); + std::string cmd_lower = cmd; + Utilities::str_tolower(cmd_lower); + if ((cmd_lower.find("graph_y") != std::string::npos) || + (cmd_lower.find("graph_sy") != std::string::npos)) + { + //Number of curves not known here + //Curves are created in Basic cmds + } + if (cmd_lower.find("plot_xy") != std::string::npos) + { + //Curves are created in Basic cmds + CurveObject new_curve = ExtractCurveInfo(cmd); // truncates cmd + // Add to list of new plotxy curves + this->new_plotxy_curves.push_back(new_curve); + } + this->rate_command_list.push_back(cmd); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + no_def = false; + } + + // install new plotxy commands + // disable this user_graph if USER_GRAPH block is empty + //if (new_command_lines || no_def) this->Set_rate_struct(); + if (new_command_lines) this->Set_rate_struct(); + if (no_def) this->Set_active(false); + return true; +} +bool +ChartObject::OpenCSVFile(std::string file_name) +{ + std::string token; + + std::ifstream f_csv(file_name.c_str(), std::ifstream::in); + + if (!f_csv.is_open()) + { + std::ostringstream estream; + estream << "Could not open tsv file for USER_GRAPH " << file_name << "\n Please, give the full path + filename."; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + + CParser parser(f_csv, this->Get_io()); + parser.set_echo_file(CParser::EO_NONE); + + /* Get lines */ + int linenr = 0; + + // temporary storage for new CSV curves + std::vector headings; + std::vector csv_curves; + + // Read file line by line into temporary curves + while (parser.check_line("cvs file", false, true, true, false) != PHRQ_io::LT_EOF) + { + // Headings line + if (linenr == 0) + { + // Skip x in 1st column + parser.get_iss() >> token; + + // Read rest of headings + while (parser.get_iss() >> token) + { + headings.push_back(token); + } + // add curves to temporary csv_curves + size_t i; + for (i = 0; i < headings.size(); i++) + { + CurveObject *c = new CurveObject; + c->Set_id(headings[i]); + c->Set_line_w(0); + std::string sym = ""; + this->Get_legal_symbol_csv(sym); + c->Set_symbol(sym); + csv_curves.push_back(c); + } + + linenr++; + continue; + } + + token = parser.line(); + std::string tok1; + CParser::TOKEN_TYPE tt = CParser::parse_delimited(token, tok1, "\t"); + + // Curve properties lines + if (linenr < 6 && tt != CParser::TT_DIGIT) + { + Utilities::str_tolower(tok1); + std::string tok2; + size_t i=0; + while (token.size() != 0 && i < csv_curves.size()) + { + CParser::parse_delimited(token, tok2, "\t"); + tok2 = trim(tok2); + if (!strncmp(tok1.c_str(), "color", 5)) + { + csv_curves[i]->Set_color(tok2); + } + else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL) + && (strstr(tok1.c_str(), "-si") == NULL)) + { + csv_curves[i]->Set_symbol(tok2); + } + else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8)) + { + if (tok2.size() > 0) + { + csv_curves[i]->Set_symbol_size(atof(tok2.c_str())); + } + } + else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5)) + { + csv_curves[i]->Set_line_w(atof(tok2.c_str())); + } + else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5)) + { + if (tok2.size() > 0) + { + csv_curves[i]->Set_y_axis(atoi(tok2.c_str())); + } + } + i++; + } + linenr++; + continue; + } + + // Curve data + if (linenr < 6) linenr = 6; + if (tt != CParser::TT_DIGIT) + { + linenr++; + continue; + } + + // x value for all curves + LDBLE x_value = atof(tok1.c_str()); + + // y values for curves + std::string tok2; + size_t i=0; + while (token.size() != 0 && i < csv_curves.size()) + { + CParser::parse_delimited(token, tok2, "\t"); + Utilities::squeeze_white(tok2); + if (tok2.size() == 0) + { + //csv_curves[i].Get_x().push_back(NA); + //csv_curves[i].Get_y().push_back(NA); + } + else + { + csv_curves[i]->Get_x().push_back(x_value); + csv_curves[i]->Get_y().push_back(atof(tok2.c_str())); + } + i++; + } + linenr++; + } + + // Append new curves + + std::vector::iterator it = csv_curves.begin(); + for (; it != csv_curves.end(); it++) + { + if ((*it)->Get_x().size() > 0) + { + std::string col = (*it)->Get_color(); + this->Get_color_string_csv(col); + (*it)->Set_color(col); + this->CurvesCSV.push_back(*it); + this->Set_curve_added(true); + } + } + return true; +} + +CurveObject +ChartObject::ExtractCurveInfo(std::string & cmd_line) +{ + /* plot_xy x, tot("Cl"), color = Red, symbol = Circle, symbol_size = 0.0, line_w = 1.0, y_axis = 2 */ + + // Make copy of cmd_line + //int curvenr = (int) this->Curves.size(); + std::string str_line = cmd_line; + + // Massage line + while (Utilities::replace(" ,",",",str_line)); + while (Utilities::replace("\t,",",",str_line)); + while (Utilities::replace(",","#",str_line)); + while (Utilities::replace("#",", ", str_line)); + + int sel; + std::string token, tok1, tok2; + std::string revised_line; + std::string::iterator b = str_line.begin(); + std::string::iterator e = str_line.end(); + // new curve + CurveObject new_curve; + + while (CParser::copy_token(token, b, e) != CParser::TT_EMPTY) + { + sel = -1; + std::string token_save = token; + Utilities::str_tolower(token); + tok1 = token; + if (!strncmp(tok1.c_str(), "color", 5)) + { + sel = 0; + } + else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL) + && (strstr(tok1.c_str(), "-si") == NULL)) + { + sel = 1; + } + else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8)) + { + sel = 2; + } + else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5)) + { + sel = 3; + } + else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5)) + { + sel = 4; + } + + // Append to command line + if (sel < 0) + { + revised_line.append(token_save); + revised_line.append(" "); + } + // Parse plot_xy pair + else + { + // remove preceding comma + std::string comma = revised_line.substr(revised_line.size() - 2); + if (comma == ", ") + { + revised_line = revised_line.substr(0, revised_line.size() - 2); + revised_line.append(" "); + } + + token = token_save; + size_t p1 = token.find("="); + std::string tok2; + // "=" found + if(p1 != std::string::npos) + { + if (p1 != token.size() - 1) + { + // color=Red + tok2 = token.substr(p1 + 1); + } + else + { + // color= Red + CParser::copy_token(tok2, b, e); + } + } + else + { + // no "=" found + CParser::copy_token(tok2, b, e); + p1 = tok2.find("="); + if (tok2 == "=") + { + // color = Red + CParser::copy_token(tok2, b, e); + } + else if (p1 != std::string::npos) + { + // color =Red + tok2 = tok2.substr(p1 + 1); + } + else + { + // color Red + tok2 = tok2; + } + } + // remove any commas + while(Utilities::replace(",","",tok1)); + while(Utilities::replace(",","",tok2)); + + // tok1 is name, tok2 is value + + switch (sel) + { + case 0: + new_curve.Set_color(tok2); + break; + case 1: + new_curve.Set_symbol(tok2); + break; + case 2: + new_curve.Set_symbol_size(atof(tok2.c_str())); + break; + case 3: + new_curve.Set_line_w(atof(tok2.c_str())); + break; + case 4: + new_curve.Set_y_axis(atoi(tok2.c_str())); + break; + } + } + } + + cmd_line = revised_line; + return new_curve; +} +void +ChartObject::Set_rate_struct(void) +{ + + if (rate_command_list.size() == 0) return; + std::list::iterator it = rate_command_list.begin(); + std::ostringstream oss; + for (; it != rate_command_list.end(); it++) + { + oss << *it << "\n"; + } + this->Rate_free(); + this->user_graph->commands = oss.str().c_str(); + this->user_graph->new_def = this->rate_new_def; + this->user_graph->loopbase = NULL; + this->user_graph->varbase = NULL; + this->user_graph->linebase = NULL; + this->user_graph->name = NULL; +} +void +ChartObject::Get_legal_symbol(std::string &sym) +{ + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end()) + { + sym = "Default"; + for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++) + { + if (default_symbol == it->second) + { + sym = it->first; + break; + } + } + default_symbol++; + default_symbol = default_symbol % this->Symbol_map.size(); + } + return; +} +void +ChartObject::Get_legal_symbol_csv(std::string &sym) +{ + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end()) + { + sym = "Default"; + for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++) + { + if (this->default_symbol_csv == it->second) + { + sym = it->first; + break; + } + } + default_symbol_csv++; + default_symbol_csv = default_symbol_csv % this->Symbol_map.size(); + } + return; +} +void +ChartObject::Get_color_string(std::string &color) +{ + if (color.size() == 0) + { + color = Color_vector[this->default_color]; + default_color++; + default_color = default_color % this->Color_vector.size(); + } + return; +} +void +ChartObject::Get_color_string_csv(std::string &color) +{ + if (color.size() == 0) + { + color = Color_vector[this->default_color_csv]; + default_color_csv++; + default_color_csv = default_color_csv % this->Color_vector.size(); + } + return; +} +ZedGraph::SymbolType +ChartObject::Return_SymbolType(const std::string sym) +{ + int i; + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) != this->Symbol_map.end()) + { + i = it->second; + } + else + { + i = default_symbol++; + default_symbol = default_symbol % this->Symbol_map.size(); + } + + switch (i) + { + case 0: + return SymbolType::Square; + break; + case 1: + return SymbolType::Diamond; + break; + case 2: + return SymbolType::Triangle; + break; + case 3: + return SymbolType::Circle; + break; + case 4: + return SymbolType::XCross; + break; + case 5: + return SymbolType::Plus; + break; + case 6: + return SymbolType::Star; + break; + case 7: + return SymbolType::TriangleDown; + break; + case 8: + return SymbolType::HDash; + break; + case 9: + return SymbolType::VDash; + break; + case 10: + return SymbolType::None; + break; + default: + return SymbolType::Default; + break; + } +} +void +ChartObject::SaveCurvesToFile(std::string &file_name) +{ + // reimplemented in Form + // This version not currently used + std::ofstream f_out(file_name.c_str(), std::ifstream::out); + + if (!f_out.is_open()) + { + std::ostringstream estream; + estream << "Could not open csv file for USER_GRAPH " << file_name; + error_msg(estream.str().c_str(), CONTINUE); + return; + } + + // list of curves + std::vector all_curves; + size_t i; + for (i = 0; i < this->CurvesCSV.size(); i++) + { + all_curves.push_back(this->CurvesCSV[i]); + } + for (i = 0; i < this->Curves.size(); i++) + { + all_curves.push_back(Curves[i]); + } + // write headings + size_t max_points = 0; + std::vector::iterator it = all_curves.begin(); + f_out.precision(4); + i = 0; + for ( ; it != all_curves.end(); it++) + { + f_out.width(12); + f_out << "x" << "\t"; + f_out.width(12); + if ((*it)->Get_id().size() > 0) + { + f_out << (*it)->Get_id() << "\t"; + } + else + { + f_out << "y" << "\t";; + } + if ((*it)->Get_x().size() > max_points) + max_points = (*it)->Get_x().size(); + } + f_out << "\n"; + + // write data + size_t i2 = 0; + f_out << std::scientific; + f_out.precision(4); + while (i2 < max_points) + { + for (it = all_curves.begin(); it != all_curves.end(); it++) + { + if (i2 < (*it)->Get_x().size()) + { + f_out.width(12); + f_out << (*it)->Get_x()[i2] << "\t"; + f_out.width(12); + f_out << (*it)->Get_y()[i2] << "\t"; + } + else if (i2 < max_points) + { + f_out.width(13); + f_out << "\t"; + f_out.width(13); + f_out << "\t"; + } + } + f_out << "\n"; + i2++; + } + f_out.close(); + return; +} +// file only used with MULTICHART +bool +ChartObject::start_chart(void) +{ + Application::EnableVisualStyles(); + Application::SetCompatibleTextRenderingDefault(true); + + // needed to send ChartObject pointer to thread + Thread ^t = gcnew Thread( + gcnew ParameterizedThreadStart(Form1::ThreadForm)); + + t->SetApartmentState(ApartmentState::STA); + t->IsBackground = false; + t->Priority = ThreadPriority::Normal; + + ChartObj ^p = gcnew ChartObj(this); + t->Start(p); + this->form_started = true; + + //Thread::Sleep( 1 ); /* this when debugging... */ + //_beginthread(void (Form1::ThreadForm), 0, NULL); + return true; +} + +void +ChartObject::Rate_free(void) +{ + + + if (this->phreeqc_ptr) + { + user_graph->commands.clear(); + } + if (user_graph->linebase != NULL) + { + char cmd[] = "new; quit"; + if (this->phreeqc_ptr) + { + phreeqc_ptr-> basic_run(cmd, user_graph->linebase, user_graph->varbase, user_graph->loopbase); + } + user_graph->linebase = NULL; + user_graph->varbase = NULL; + user_graph->loopbase = NULL; + } +} +void ChartObject::Initialize_graph_pts(void) +{ + graph_x = NA; + graph_y.clear(); + secondary_y.clear(); +} + +void +ChartObject::Finalize_graph_pts(void) +{ + if (graph_x != NA) + { + std::map::iterator it; + for (it = graph_y.begin(); it != graph_y.end(); it++) + { + Curves[it->first]->Get_x().push_back(graph_x); + Curves[it->first]->Get_y().push_back(it->second); + // Mark added curve for first point, might have been invisible in DefineCurves + if (Curves[it->first]->Get_x().size() == 1) + this->Set_curve_added(true); + } + } + if (graph_x != NA) + { + std::map::iterator it; + for (it = secondary_y.begin(); it != secondary_y.end(); it++) + { + Curves[it->first]->Set_y_axis(2); + } + } +} +void +ChartObject::Add_new_series(void) +{ + std::vector Curves; + size_t i; + size_t e = this->Curves.size(); + for (i = this->ColumnOffset; i < e; i++) + { + this->Add_curve(false, + this->Curves[i]->Get_id(), + this->Curves[i]->Get_line_w(), + "", + this->Curves[i]->Get_symbol_size(), + this->Curves[i]->Get_y_axis(), + ""); + } + this->ColumnOffset = (int) e; + this->curve_added = true; +} +void +ChartObject::Add_curve(bool plotxy, std::string id, + LDBLE line_width, + std::string symbol, + LDBLE symbol_size, + int y_axis, + std::string color) +{ + CurveObject *c = new CurveObject; + c->Set_id(id); + c->Set_line_w(line_width); + this->Get_legal_symbol(symbol); + c->Set_symbol(symbol); + c->Set_symbol_size(symbol_size); + if (this->CurvesCSV.size() > this->Curves.size() && !plotxy) + { + c->Set_symbol_size(0.0); + } + c->Set_y_axis(y_axis); + this->Get_color_string(color); + c->Set_color(color); + + this->Curves.push_back(c); +} +void +ChartObject::Set_rate_new_def(bool tf) +{ + this->rate_new_def = tf; + if (this->user_graph != NULL) + { + if (tf) + { + this->user_graph->new_def = 1; + } + else + { + this->user_graph->new_def = 0; + } + } +} +void +ChartObject::dump(std::ostream & oss, unsigned int indent) +{ + size_t i; + oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + oss << indent0 << "USER_GRAPH " << this->n_user << " " << this->description << "\n"; + + // chart title + oss << indent1 << "-chart_title \"" << this->chart_title << "\"" << "\n"; + + // axis titles + if (this->axis_titles.size() > 0) + { + oss << indent1 << "-axis_titles "; + + for (i = 0; i < this->axis_titles.size(); i++) + { + oss << "\"" << axis_titles[i] << "\" "; + } + oss << "\n"; + } + + // axis_scale_x + LDBLE *scale_ptr = this->axis_scale_x; + { + oss << indent1 << "-axis_scale x_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // axis_scale_y + scale_ptr = this->axis_scale_y; + { + oss << indent1 << "-axis_scale y_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // axis_scale_sy + scale_ptr = this->axis_scale_y2; + { + oss << indent1 << "-axis_scale sy_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // chart type + if (this->chart_type == 0) + { + oss << indent1 << "-plot_concentration_vs x" << "\n"; + } + else + { + oss << indent1 << "-plot_concentration_vs t" << "\n"; + } + // graph_initial_solutions + if (this->graph_initial_solutions) + { + oss << indent1 << "-initial_solutions true" << "\n"; + } + else + { + oss << indent1 << "-initial_solutions false" << "\n"; + } + // connect_simulations + if (this->connect_simulations) + { + oss << indent1 << "-connect_simulations true" << "\n"; + } + else + { + oss << indent1 << "-connect_simulations false" << "\n"; + } + // csv files + for (i = 0; i < this->csv_file_names.size(); i++) + { + oss << indent1 << "-plot_tsv_file " << this->csv_file_names[i] << "\n"; + } + + // headings + if (this->headings_original.size() > 0) + { + oss << indent1 << "-headings "; + for (i = 0; i < this->headings_original.size(); i++) + { + oss << this->headings_original[i] << " "; + } + oss << "\n"; + } + + // commands + oss << indent1 << "-start" << "\n"; + std::list::iterator it = rate_command_list_original.begin(); + for (; it != rate_command_list_original.end(); it++) + { + oss << *it << "\n"; + } + oss << indent1 << "-end" << "\n"; + + /* + class rate *user_graph; + // C++ for rate struct + std::string rate_name; + std::list rate_command_list; + bool rate_new_def; + + int default_symbol; + int default_symbol_csv; + int default_color; + int default_color_csv; + + // temporary storage before stored graph_x/y/sy data are stored in curves + // Initialize_graph_pts and Finalize_graph_pts use this storage. + LDBLE graph_x; + std::map graph_y; + std::map secondary_y; + + // temporary plotxy curve definitions before stored in curves + // a plotxy curve is copied to Curves when cmdplotxy is encountered + // this keeps order correct between plotxy and graph_x/y/sy + std::vector new_plotxy_curves; + + // temporary headings until stored during basic_run + std::vector new_headings; + bool active; + bool detach; + bool form_started; + */ +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("start"), // 0 + std::vector< std::string >::value_type("end"), // 1 + std::vector< std::string >::value_type("heading"), // 2 + std::vector< std::string >::value_type("headings"), // 3 + std::vector< std::string >::value_type("chart_title"), // 4 + std::vector< std::string >::value_type("axis_titles"), // 5 + std::vector< std::string >::value_type("axis_scale"), // 6 + std::vector< std::string >::value_type("initial_solutions"), // 7 + std::vector< std::string >::value_type("plot_concentration_vs"), // 8 + std::vector< std::string >::value_type("shifts_as_points"), // 9 + std::vector< std::string >::value_type("grid_offset"), // 10 + std::vector< std::string >::value_type("connect_simulations"), // 11 + std::vector< std::string >::value_type("plot_csv_file"), // 12 + std::vector< std::string >::value_type("clear"), // 13 + std::vector< std::string >::value_type("detach"), // 14 + std::vector< std::string >::value_type("active"), // 15 + std::vector< std::string >::value_type("batch"), // 16 + std::vector< std::string >::value_type("plot_tsv_file") // 17 +}; +const std::vector< std::string > ChartObject::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); + + +#endif // MULTICHART diff --git a/phreeqcpp/ChartObject.h b/phreeqcpp/ChartObject.h new file mode 100644 index 00000000..3402febf --- /dev/null +++ b/phreeqcpp/ChartObject.h @@ -0,0 +1,444 @@ +#if !defined(CHARTOBJECT_H_INCLUDED) +#define CHARTOBJECT_H_INCLUDED +#if defined MULTICHART +#include +#include +#include +#include +#include "CurveObject.h" +#include "NumKeyword.h" + +#include +class Phreeqc; + +class ChartObject:public cxxNumKeyword +{ + + public: + ChartObject(PHRQ_io *io=NULL); + ChartObject(int i, PHRQ_io *io=NULL); + ~ChartObject(); + + enum chart_batch_type + { ChO_NO_BATCH = -1, + ChO_BATCH_ONLY = 0, + ChO_EMF = 1, + ChO_PNG = 2, + ChO_JPG = 3, + ChO_GIF = 4, + ChO_TIFF = 5, + ChO_BMP = 6 + }; + + bool Get_new_ug()const + { + return this->new_ug; + } + void Set_new_ug(bool b) + { + this->new_ug = b; + } + void Set_FirstCallToUSER_GRAPH(bool b) + { + this->FirstCallToUSER_GRAPH = b; + } + bool Get_FirstCallToUSER_GRAPH()const + { + return this->FirstCallToUSER_GRAPH; + } + int Get_update_time_chart()const + { + return (this->update_time_chart); + } + int Get_PanelHeight()const + { + return (this->PanelHeight); + } + int Get_PanelWidth()const + { + return (this->PanelWidth); + } + std::string &Get_chart_title() + { + return this->chart_title; + } + const std::string &Get_chart_title()const + { + return this->chart_title; + } + std::string Get_batch_fn() + { + return this->batch_fn; + } + void Set_batch_fn(std::string fn) + { + this->batch_fn = fn; + } + std::vector &Get_axis_titles() + { + return this->axis_titles; + } + const std::vector &Get_axis_titles()const + { + return this->axis_titles; + } + LDBLE *Get_axis_scale_x() + { + return this->axis_scale_x; + } + const LDBLE *Get_axis_scale_x()const + { + return this->axis_scale_x; + } + LDBLE *Get_axis_scale_y() + { + return this->axis_scale_y; + } + const LDBLE *Get_axis_scale_y()const + { + return this->axis_scale_y; + } + LDBLE *Get_axis_scale_y2() + { + return this->axis_scale_y2; + } + const LDBLE *Get_axis_scale_y2()const + { + return this->axis_scale_y2; + } + int Get_chart_type()const + { + return this->chart_type; + } + bool Get_graph_initial_solutions()const + { + return this->graph_initial_solutions; + } + void Set_graph_initial_solutions(bool val) + { + this->graph_initial_solutions = val; + } + bool Get_connect_simulations()const + { + return this->connect_simulations; + } + void Set_connect_simulations(bool val) + { + this->connect_simulations = val; + } + void Set_colnr(int i) + { + this->colnr = i; + } + int Get_colnr()const + { + return (this->colnr); + } + void Set_ColumnOffset(int i) + { + this->ColumnOffset = i; + } + int Get_ColumnOffset()const + { + return (this->ColumnOffset); + } + void Set_AddSeries(bool b) + { + this->AddSeries = b; + } + bool Get_AddSeries()const + { + return this->AddSeries; + } + std::vector< std::string > Get_csv_file_names()const + { + return this->csv_file_names; + } + void Get_csv_file_names(std::vector< std::string > names) + { + this->csv_file_names = names; + } + void Set_prev_advection_step(int i) + { + this->prev_advection_step = i; + } + int Get_prev_advection_step()const + { + return (this->prev_advection_step); + } + void Set_prev_transport_step(int i) + { + this->prev_transport_step = i; + } + int Get_prev_transport_step()const + { + return (this->prev_transport_step); + } + void Set_prev_sim_no(int i) + { + this->prev_sim_no = i; + } + int Get_prev_sim_no(void)const + { + return this->prev_sim_no; + } + void Set_end_timer(bool b) + { + this->end_timer = b; + } + bool Get_end_timer()const + { + return this->end_timer; + } + void Set_done(bool b) + { + this->done = b; + } + bool Get_done()const + { + return this->done; + } + std::vector &Get_CurvesCSV() + { + return this->CurvesCSV; + } + const std::vector &Get_CurvesCSV()const + { + return this->CurvesCSV; + } + std::vector &Get_Curves() + { + return this->Curves; + } + const std::vector &Get_Curves()const + { + return this->Curves; + } + void Set_curve_added(bool tf) + { + this->curve_added = tf; + } + bool Get_curve_added()const + { + return this->curve_added; + } + void Set_point_added(bool tf) + { + this->point_added = tf; + } + bool Get_point_added()const + { + return this->point_added; + } + class rate *Get_user_graph() + { + return this->user_graph; + } + const class rate *Get_user_graph()const + { + return this->user_graph; + } + std::list &Get_rate_command_list() + { + return this->rate_command_list; + } + const std::list &Get_rate_command_list()const + { + return this->rate_command_list; + } + void Set_rate_new_def(bool tf); + + bool Get_rate_new_def()const + { + return this->rate_new_def; + } + void Set_graph_x(LDBLE d) + { + this->graph_x = d; + } + LDBLE Get_graph_x()const + { + return this->graph_x; + } + std::map &Get_graph_y() + { + return this->graph_y; + } + const std::map &Get_graph_y()const + { + return this->graph_y; + } + std::map &Get_secondary_y() + { + return this->secondary_y; + } + const std::map &Get_secondary_y()const + { + return this->secondary_y; + } + std::vector &Get_new_plotxy_curves() + { + return this->new_plotxy_curves; + } + const std::vector &Get_new_plotxy_curves()const + { + return this->new_plotxy_curves; + } + std::vector &Get_new_headings() + { + return this->new_headings; + } + const std::vector &Get_new_headings()const + { + return this->new_headings; + } + void Set_active(bool tf) + { + this->active = tf; + } + bool Get_active()const + { + return this->active; + } + void Set_detach(bool tf) + { + this->detach = tf; + } + bool Get_detach()const + { + return this->detach; + } + bool Get_form_started()const + { + return this->form_started; + } + void Set_phreeqc(Phreeqc * ptr) + { + this->phreeqc_ptr = ptr; + } + Phreeqc * Get_phreeqc() + { + return this->phreeqc_ptr; + } + const Phreeqc * Get_phreeqc()const + { + return this->phreeqc_ptr; + } + const std::list& Get_rate_command_list_original()const + { + return this->rate_command_list_original; + } + bool Set_axis_scale(std::vector, std::vector types, std::ostringstream &); + bool Set_axis_scale(CParser & parser); + bool Read(CParser & parser); + bool OpenCSVFile(std::string fn); + static CurveObject ExtractCurveInfo(std::string & str_line); + void Set_rate_struct(void); + void PlotXY(std::string x, std::string y); + bool start_chart(void); + ZedGraph::SymbolType Return_SymbolType(std::string); + void SaveCurvesToFile(std::string &); + void Rate_free(void); + void Initialize_graph_pts(void); + void Finalize_graph_pts(void); + void Get_legal_symbol(std::string &sym); + void Get_legal_symbol_csv(std::string &sym); + void Get_color_string(std::string &color); + void Get_color_string_csv(std::string &color); + void Add_new_series(void); + void Add_curve(bool plotxy, std::string id = "", + LDBLE line_width = 1.0, + std::string symbol = "", + LDBLE symbol_size = 6.0, + int y_axis = 1, + std::string color = ""); + void dump(std::ostream & s_oss, unsigned int indent); + + chart_batch_type Get_batch(void) {return this->batch;} + void Set_batch(chart_batch_type bt) {this->batch = bt;} + bool Get_batch_background(void) {return this->batch_background;} + void Set_batch_background(bool tf) {this->batch_background = tf;} + + bool Get_batch_grid(void) {return this->batch_grid;} + void Set_batch_grid(bool tf) {this->batch_grid = tf;} + protected: + + bool new_ug; + bool FirstCallToUSER_GRAPH; + + int update_time_chart; /* milliseconds, maybe read */ + int PanelHeight; + int PanelWidth; + std::map Symbol_map; + std::vector Color_vector; + std::string chart_title; + std::vector axis_titles; + LDBLE axis_scale_x[5]; + LDBLE axis_scale_y[5]; + LDBLE axis_scale_y2[5]; + + int chart_type; + bool graph_initial_solutions; + bool connect_simulations; + int shifts_as_points; + int colnr; + int ColumnOffset; + bool AddSeries; + + int prev_advection_step; + int prev_transport_step; + + int prev_sim_no; + + bool end_timer; + bool done; + + std::vector csv_file_names; + std::vector CurvesCSV; + std::vector Curves; + bool curve_added; + bool point_added; + + class rate *user_graph; + // C++ for rate class + std::string rate_name; + std::list rate_command_list; + std::list rate_command_list_original; + bool rate_new_def; + + int default_symbol; + int default_symbol_csv; + int default_color; + int default_color_csv; + + // temporary storage before stored graph_x/y/sy data are stored in curves + // Initialize_graph_pts and Finalize_graph_pts use this storage. + LDBLE graph_x; + std::map graph_y; + std::map secondary_y; + + // temporary plotxy curve definitions before stored in curves + // a plotxy curve is copied to Curves when cmdplotxy is encountered + // this keeps order correct between plotxy and graph_x/y/sy + std::vector new_plotxy_curves; + + // temporary headings until stored during basic_run + std::vector new_headings; + std::vector headings_original; + bool active; + bool detach; + bool form_started; + class Phreeqc * phreeqc_ptr; + + bool batch_background; + bool batch_grid; + std::string batch_fn; + chart_batch_type batch; + const static std::vector < std::string > vopts; + + public: + int usingResource; + +}; +#endif // MULTICHART +#endif // !defined(CHARTOBJECT_H_INCLUDED) diff --git a/phreeqcpp/CurveObject.cpp b/phreeqcpp/CurveObject.cpp new file mode 100644 index 00000000..91e1e29c --- /dev/null +++ b/phreeqcpp/CurveObject.cpp @@ -0,0 +1,42 @@ +// CurveObject.cpp: implementation of the CurveObject class. +// +////////////////////////////////////////////////////////////////////// +#ifdef MULTICHART +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "CurveObject.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CurveObject::CurveObject() + // + // default constructor for cxxExchComp + // +{ + x.clear(); + y.clear(); + this->id = ""; + this->symbol = ""; + this->color = ""; + this->y_axis = 1; + this->line_w = 1.0; + this->symbol_size = 6.0; +} + + +CurveObject::~CurveObject() +{ +} + +#endif // MULTICHART diff --git a/phreeqcpp/CurveObject.h b/phreeqcpp/CurveObject.h new file mode 100644 index 00000000..d491fd36 --- /dev/null +++ b/phreeqcpp/CurveObject.h @@ -0,0 +1,79 @@ +#if !defined(CURVEOBJECT_H_INCLUDED) +#define CURVEOBJECT_H_INCLUDED +#include +#include +#include "phrqtype.h" +class CurveObject +{ + +public: + CurveObject(); + ~CurveObject(); + void Set_id(std::string s) + { + this->id = s; + } + std::string &Get_id(void) + { + return this->id; + } + void Set_color(std::string s) + { + this->color = s; + } + std::string &Get_color(void) + { + return this->color; + } + void Set_symbol(std::string s) + { + this->symbol = s; + } + std::string &Get_symbol(void) + { + return this->symbol; + } + void Set_symbol_size(LDBLE f) + { + this->symbol_size = f; + } + LDBLE Get_symbol_size(void) + { + return this->symbol_size; + } + void Set_line_w(LDBLE f) + { + this->line_w = f; + } + LDBLE Get_line_w(void) + { + return this->line_w; + } + void Set_y_axis(int f) + { + this->y_axis = f; + } + std::vector & Get_x() + { + return this->x; + } + std::vector & Get_y() + { + return this->y; + } + int Get_y_axis() + { + return this->y_axis; + } + +protected: + std::vector x, y; + std::string id, color, symbol; + int y_axis; + LDBLE line_w, symbol_size; + +public: + +}; + +#endif // !defined(CURVEOBJECT_H_INCLUDED) diff --git a/phreeqcpp/Dictionary.cpp b/phreeqcpp/Dictionary.cpp new file mode 100644 index 00000000..ed5ee933 --- /dev/null +++ b/phreeqcpp/Dictionary.cpp @@ -0,0 +1,41 @@ +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +Dictionary::Dictionary(void) +{ +} + +Dictionary::Dictionary(std::string & words_string) +{ + std::istringstream words_stream(words_string); + char str[256]; + while (words_stream.getline(str,256)) + { + this->Find(str); + } +} +Dictionary::~Dictionary(void) +{ +} + +int +Dictionary::Find(std::string str) +{ + std::map::iterator it = this->dictionary_map.find(str); + if (it != this->dictionary_map.end()) + { + return it->second; + } + int i = this->MapSize(); + this->dictionary_map[str] = i; + this->words.push_back(str); + this->dictionary_oss << str << "\n"; + return i; +} diff --git a/phreeqcpp/Dictionary.h b/phreeqcpp/Dictionary.h new file mode 100644 index 00000000..ffe86fc5 --- /dev/null +++ b/phreeqcpp/Dictionary.h @@ -0,0 +1,28 @@ +#if !defined(DICTIONARY_H_INCLUDED) +#define DICTIONARY_H_INCLUDED +#include +#include +#include +#include +class Phreeqc; +class Dictionary +{ +public: + Dictionary(void); + Dictionary(std::string & words_string); + ~Dictionary(void); + int Find(std::string str); + int MapSize() {return (int) this->dictionary_map.size();} + int OssSize() {return (int) this->dictionary_oss.str().size();} + std::ostringstream &GetDictionaryOss() {return this->dictionary_oss;} + std::vector &GetWords() {return this->words;} + +protected: + std::map dictionary_map; + std::vector words; + std::ostringstream dictionary_oss; + +}; + +#endif // !defined(DICTIONARY_H_INCLUDED) + diff --git a/phreeqcpp/ExchComp.cxx b/phreeqcpp/ExchComp.cxx new file mode 100644 index 00000000..4072e49c --- /dev/null +++ b/phreeqcpp/ExchComp.cxx @@ -0,0 +1,398 @@ +// ExchComp.cxx: implementation of the cxxExchComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "ExchComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxExchComp::cxxExchComp(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : PHRQ_base(io) +{ + totals.type = cxxNameDouble::ND_ELT_MOLES; + la = 0.0; + charge_balance = 0.0; + phase_proportion = 0.0; + formula_z = 0.0; +} +cxxExchComp::~cxxExchComp() +{ +} + +void +cxxExchComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exch_Comp element and attributes + + s_oss << indent0 << "formula=\"" << this->formula << "\"" << "\n"; + s_oss << indent0 << "formula_z=\"" << this-> + formula_z << "\"" << "\n"; + s_oss << indent0 << "la=\"" << this->la << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this-> + charge_balance << "\"" << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "phase_name=\"" << this->phase_name << "\"" << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "rate_name=\"" << this-> + rate_name << "\"" << "\n"; + } + s_oss << indent0 << "phase_proportion=\"" << this-> + phase_proportion << "\"" << "\n"; + + // totals + s_oss << indent0; + s_oss << "totals.dump_xml(s_oss, indent + 1); +} + +void +cxxExchComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exch_Comp element and attributes + //s_oss << indent1 << "# critical values" << "\n"; + + // totals + s_oss << indent0 << "# EXCHANGE_MODIFY candidate identifiers #\n"; + s_oss << indent0; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); + + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + + //s_oss << indent1 << "# Noncritical values" << "\n"; + s_oss << indent0 << "-la " << this->la << "\n"; + + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "-phase_name " << this->phase_name << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "-rate_name " << this->rate_name << "\n"; + } + s_oss << indent0 << "-phase_proportion " << this->phase_proportion << "\n"; + s_oss << indent0 << "-formula_z " << this->formula_z << "\n"; +} + +void +cxxExchComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool la_defined(false); + bool charge_balance_defined(false); + bool formula_z_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // formula + warning_msg("-formula ignored. Defined with -component."); + break; + + case 1: // moles + parser.warning_msg("-moles is an obsolete identifier"); + break; + + case 2: // la + if (!(parser.get_iss() >> this->la)) + { + this->la = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la.", + PHRQ_io::OT_CONTINUE); + } + la_defined = true; + break; + + case 3: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 4: // phase_name + if (!(parser.get_iss() >> str)) + { + this->phase_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for phase_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->phase_name = str; + } + break; + + case 5: // rate_name + if (!(parser.get_iss() >> str)) + { + this->rate_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for rate_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->rate_name = str; + } + break; + + case 6: // formula_z + if (!(parser.get_iss() >> this->formula_z)) + { + this->formula_z = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for formula_z.", + PHRQ_io::OT_CONTINUE); + } + formula_z_defined = true; + break; + + case 7: // phase_proportion + if (!(parser.get_iss() >> this->phase_proportion)) + { + this->phase_proportion = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for phase_proportion.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for ExchComp totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 8; + break; + + case 9: // formula_totals + parser.warning_msg("-formula_totals is an obsolete identifier"); + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (la_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_balance not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (formula_z_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Formula_z not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} +void +cxxExchComp::add(const cxxExchComp & addee, LDBLE extensive) +{ + LDBLE f1, f2; + if (extensive == 0.0) + return; + if (addee.formula.size() == 0) + return; + // this and addee must have same formula + // otherwise generate a new exchcomp with multiply + f1 = 0.5; + f2 = 0.5; + + if (this->formula.size() == 0 && addee.formula.size() == 0) + { + return; + } + assert(this->formula == addee.formula); + assert(this->formula_z == addee.formula_z); + if (this->formula.size() == 0 && addee.formula.size() != 0) + { + this->formula = addee.formula; + } + this->totals.add_extensive(addee.totals, extensive); + this->la = f1 * this->la + f2 * addee.la; + this->charge_balance += addee.charge_balance * extensive; + + //if (this->phase_name != addee.phase_name) + if (Phreeqc::strcmp_nocase(this->phase_name.c_str(), addee.phase_name.c_str()) != 0) + { + std::ostringstream oss; + oss << + "Cannot mix two exchange components with same formula and different related phases, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->phase_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + //if (this->rate_name != addee.rate_name) + if (Phreeqc::strcmp_nocase(this->rate_name.c_str(), addee.rate_name.c_str()) != 0) + { + std::ostringstream oss; + oss << + "Cannot mix two exchange components with same formula and different related kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->rate_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + if ((this->rate_name.size() != 0 && addee.phase_name.size() != 0) || + (this->phase_name.size() != 0 && addee.rate_name.size() != 0)) + { + std::ostringstream oss; + oss << + "Cannot mix exchange components related to phase with exchange components related to kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } +} +void +cxxExchComp::multiply(LDBLE extensive) +{ + this->totals.multiply(extensive); + this->charge_balance *= extensive; + //this->phase_proportion *= extensive; +} + +void +cxxExchComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->formula)); + this->totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->la); + doubles.push_back(this->charge_balance); + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->phase_proportion); + ints.push_back(dictionary.Find(this->rate_name)); + doubles.push_back(this->formula_z); +} + +void +cxxExchComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->formula = dictionary.GetWords()[ints[ii++]]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->la = doubles[dd++]; + this->charge_balance = doubles[dd++]; + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->phase_proportion = doubles[dd++]; + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->formula_z = doubles[dd++]; +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("formula"), // 0 + std::vector< std::string >::value_type("moles"), // 1 + std::vector< std::string >::value_type("la"), // 2 + std::vector< std::string >::value_type("charge_balance"), // 3 + std::vector< std::string >::value_type("phase_name"), // 4 + std::vector< std::string >::value_type("rate_name"), // 5 + std::vector< std::string >::value_type("formula_z"), // 6 + std::vector< std::string >::value_type("phase_proportion"), // 7 + std::vector< std::string >::value_type("totals"), // 8 + std::vector< std::string >::value_type("formula_totals") // 9 +}; +const std::vector< std::string > cxxExchComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/ExchComp.h b/phreeqcpp/ExchComp.h new file mode 100644 index 00000000..6af0718f --- /dev/null +++ b/phreeqcpp/ExchComp.h @@ -0,0 +1,117 @@ +#if !defined(EXCHCOMP_H_INCLUDED) +#define EXCHCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxExchComp: public PHRQ_base +{ + + public: + cxxExchComp(PHRQ_io *io=NULL); + virtual ~cxxExchComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check=true); + + const std::string &Get_formula() const + { + return this->formula; + } + void Set_formula(const char *cstring) + { + if (cstring != NULL) + this->formula = std::string(cstring); + else + this->formula.clear(); + } + LDBLE Get_la() const + { + return this->la; + } + void Set_la(LDBLE d) + { + this->la = d; + } + LDBLE Get_charge_balance() const + { + return this->charge_balance; + } + void Set_charge_balance(LDBLE d) + { + this->charge_balance = d; + } + const std::string &Get_phase_name() const + { + return this->phase_name; + } + void Set_phase_name(const char *cstring) + { + if (cstring != NULL) + this->phase_name = std::string(cstring); + else + this->phase_name.clear(); + } + LDBLE Get_phase_proportion() const + { + return this->phase_proportion; + } + void Set_phase_proportion(LDBLE d) + { + this->phase_proportion = d; + } + const std::string &Get_rate_name() const + { + return this->rate_name; + } + void Set_rate_name(const char *cstring) + { + if (cstring != NULL) + this->rate_name = std::string(cstring); + else + this->rate_name.clear(); + } + LDBLE Get_formula_z() const + { + return this->formula_z; + } + void Set_formula_z(LDBLE d) + { + this->formula_z = d; + } + void Set_totals(cxxNameDouble nd) + { + this->totals = nd; + } + cxxNameDouble & Get_totals() {return (this->totals);} + const cxxNameDouble & Get_totals()const {return (this->totals);} + + void add(const cxxExchComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string formula; + // EXCHANGE_MODIFY candidates + cxxNameDouble totals; + LDBLE la; + LDBLE charge_balance; + std::string phase_name; + LDBLE phase_proportion; + std::string rate_name; + LDBLE formula_z; // charge on formula + const static std::vector < std::string > vopts; + public: + +}; + +#endif // !defined(EXCHCOMP_H_INCLUDED) diff --git a/phreeqcpp/Exchange.cxx b/phreeqcpp/Exchange.cxx new file mode 100644 index 00000000..b009bba0 --- /dev/null +++ b/phreeqcpp/Exchange.cxx @@ -0,0 +1,466 @@ +// Exchange.cxx: implementation of the cxxExchange class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort + +#include "Utils.h" // define first + +#include "Phreeqc.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxExchange::cxxExchange(PHRQ_io *io) + // + // default constructor for cxxExchange + // +: cxxNumKeyword(io) +{ + new_def = false; + solution_equilibria = false; + n_solution = -999; + pitzer_exchange_gammas = true; +} +cxxExchange::cxxExchange(const std::map < int, cxxExchange > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + this->pitzer_exchange_gammas = true; + this->new_def = false; + this->n_solution = -999; + this->solution_equilibria = false; +// +// Mix exchangers +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxExchange *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + this->pitzer_exchange_gammas = entity_ptr->pitzer_exchange_gammas; + } + } +} + + +cxxExchange::~cxxExchange() +{ +} +bool +cxxExchange::Get_related_phases() const +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + if (this->exchange_comps[i].Get_phase_name().size() > 0) + { + return (true); + } + } + return (false); +} + +bool +cxxExchange::Get_related_rate() const +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + if (this->exchange_comps[i].Get_rate_name().size() > 0) + { + return (true); + } + } + return (false); +} + +void +cxxExchange::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exchange element and attributes + s_oss << indent0; + s_oss << " + pitzer_exchange_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "exchange_comps.size(); j++) + { + this->exchange_comps[j].dump_xml(s_oss, indent + 2); + } + + return; +} +void +cxxExchange::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exchange element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "EXCHANGE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# EXCHANGE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-exchange_gammas " << (this->pitzer_exchange_gammas ? 1 : 0) << "\n"; + // exchComps + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + s_oss << indent1; + s_oss << "-component " << this->exchange_comps[i].Get_formula() << "\n"; + this->exchange_comps[i].dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# EXCHANGE_MODIFY candidates with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << 0 << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << 0 << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + + s_oss << indent1 << "# Exchange workspace variables #\n"; + s_oss << indent1; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); + + return; +} +void +cxxExchange::read_raw(CParser & parser, bool check) +{ + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + bool useLastLine(false); + + // Read exchange number and description + this->read_number_description(parser); + this->Set_new_def(false); + + bool pitzer_exchange_gammas_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in EXCH_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // pitzer_exchange_gammas + case 2: // exchange_gammas + if (!(parser.get_iss() >> this->pitzer_exchange_gammas)) + { + this->pitzer_exchange_gammas = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for pitzer_exchange_gammas.", + PHRQ_io::OT_CONTINUE); + } + pitzer_exchange_gammas_defined = true; + break; + case 1: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxExchComp temp_comp(this->io); + temp_comp.Set_formula(str.c_str()); + cxxExchComp *comp_ptr = this->Find_comp(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + if (comp_ptr) + { + for (size_t j = 0; j < this->exchange_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->exchange_comps[j].Get_formula().c_str(), str.c_str()) == 0) + { + this->exchange_comps[j] = temp_comp; + } + } + } + else + { + this->exchange_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + case 3: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for new_def.", + PHRQ_io::OT_CONTINUE); + } + break; + case 4: // solution_equilibria + if (!(parser.get_iss() >> this->solution_equilibria)) + { + this->solution_equilibria = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for solution_equilibria.", + PHRQ_io::OT_CONTINUE); + } + break; + case 5: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + this->n_solution = -999; + parser.incr_input_error(); + parser. + error_msg + ("Expected integer value for n_solution.", + PHRQ_io::OT_CONTINUE); + } + break; + case 6: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Exchange totals.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (pitzer_exchange_gammas_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Pitzer_exchange_gammsa not defined for EXCHANGE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + this->Sort_comps(); +} +void +cxxExchange::add(const cxxExchange & addee, LDBLE extensive) + // + // Add existing exchange to "this" exchange + // +{ + // exchComps + if (extensive == 0.0) + return; + for (size_t i = 0; i < addee.exchange_comps.size(); i++) + { + size_t j; + for (j = 0; j < this->Get_exchange_comps().size(); j++) + if (addee.exchange_comps[i].Get_formula() == this->exchange_comps[j].Get_formula()) + { + this->exchange_comps[j].add(addee.exchange_comps[i], extensive); + break; + } + if (j == this->exchange_comps.size()) + { + cxxExchComp exc = addee.exchange_comps[i]; + exc.multiply(extensive); + this->exchange_comps.push_back(exc); + } + } + this->pitzer_exchange_gammas = addee.pitzer_exchange_gammas; +} +void +cxxExchange::totalize() +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + this->totals.add_extensive(this->exchange_comps[i].Get_totals(), 1.0); + this->totals.add("Charge", this->exchange_comps[i].Get_charge_balance()); + } + return; +} +bool +cxxExchange::Get_pitzer_exchange_gammas() const +{ + return this->pitzer_exchange_gammas; +} +void +cxxExchange::Set_pitzer_exchange_gammas(bool b) +{ + this->pitzer_exchange_gammas = b; +} +const cxxNameDouble & +cxxExchange::Get_totals() const +{ + return totals; +} +cxxExchComp *cxxExchange::Find_comp(std::string s) +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + cxxNameDouble nd(this->exchange_comps[i].Get_totals()); + cxxNameDouble::iterator nd_it; + for (nd_it = nd.begin(); nd_it != nd.end(); nd_it++) + { + if(nd_it->first == s) + { + return (&(this->exchange_comps[i])); + } + } + } + return NULL; +} +void cxxExchange:: +Sort_comps(void) +{ + // sort comps + { + std::map comp_map; + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + comp_map[this->exchange_comps[i].Get_formula()] = this->exchange_comps[i]; + } + this->exchange_comps.clear(); + std::map::iterator it; + for (it = comp_map.begin(); it != comp_map.end(); it++) + { + this->exchange_comps.push_back(it->second); + } + } +} +/* ---------------------------------------------------------------------- */ +void +cxxExchange::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + ints.push_back(this->n_user); + ints.push_back((int) this->exchange_comps.size()); + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + exchange_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back(this->pitzer_exchange_gammas ? 1 : 0); + ints.push_back(this->new_def ? 1 : 0); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back(this->n_solution); + this->totals.Serialize(dictionary, ints, doubles); + +} + +/* ---------------------------------------------------------------------- */ +void +cxxExchange::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + int count = ints[ii++]; + this->exchange_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxExchComp ec(this->io); + ec.Deserialize(dictionary, ints, doubles, ii, dd); + this->exchange_comps.push_back(ec); + } + this->pitzer_exchange_gammas = (ints[ii++] != 0); + this->new_def = (ints[ii++] != 0); + this->solution_equilibria = (ints[ii++] != 0); + this->n_solution = ints[ii++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("pitzer_exchange_gammas"), // 0 + std::vector< std::string >::value_type("component"), // 1 + std::vector< std::string >::value_type("exchange_gammas"), // 2 + std::vector< std::string >::value_type("new_def"), // 3 + std::vector< std::string >::value_type("solution_equilibria"), // 4 + std::vector< std::string >::value_type("n_solution"), // 5 + std::vector< std::string >::value_type("totals") // 6 +}; +const std::vector< std::string > cxxExchange::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Exchange.h b/phreeqcpp/Exchange.h new file mode 100644 index 00000000..f387f2fe --- /dev/null +++ b/phreeqcpp/Exchange.h @@ -0,0 +1,74 @@ +#if !defined(EXCHANGE_H_INCLUDED) +#define EXCHANGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include "NumKeyword.h" +#include "ExchComp.h" +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxMix; + +class cxxExchange:public cxxNumKeyword +{ + +public: + cxxExchange(PHRQ_io *io=NULL); + cxxExchange(const std::map < int, cxxExchange > &exchange_map, + cxxMix & mx, int n_user, PHRQ_io *io=NULL); + ~cxxExchange(); + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + bool Get_related_phases(void) const; + + bool Get_related_rate(void) const; + + bool Get_pitzer_exchange_gammas() const; + void Set_pitzer_exchange_gammas(bool b); + + bool Get_new_def(void) const {return this->new_def;} + void Set_new_def(bool tf) {this->new_def = tf;} + bool Get_solution_equilibria(void) const {return this->solution_equilibria;} + void Set_solution_equilibria(bool tf) {this->solution_equilibria = tf;} + int Get_n_solution(void) const {return this->n_solution;} + void Set_n_solution(int i) {this->n_solution = i;} + cxxExchComp *Find_comp(std::string s); + std::vector & Get_exchange_comps(void) {return this->exchange_comps;} + const std::vector & Get_exchange_comps(void)const {return this->exchange_comps;} + void Set_exchange_comps(std::vector &c) {this->exchange_comps = c;} + + void Sort_comps(void); + void totalize(); + + const cxxNameDouble & Get_totals() const; + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + void add(const cxxExchange & addee, LDBLE extensive); + // not written + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + +protected: + // EXCHANGE_MODIFY candidates + std::vector exchange_comps; + bool pitzer_exchange_gammas; + + // EXCHANGE_MODIFY candidates with new_def=true + bool new_def; + bool solution_equilibria; + int n_solution; + + // exchange workspace variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(EXCHANGE_H_INCLUDED) diff --git a/phreeqcpp/Form1.2005.resX b/phreeqcpp/Form1.2005.resX new file mode 100644 index 00000000..fc36b375 --- /dev/null +++ b/phreeqcpp/Form1.2005.resX @@ -0,0 +1,33 @@ + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP8AMzOaAGgymgA0Mc0AmWVoAGdlmwBjZssAy5kvAP+ZNQDLmmMAzpmVAP/MLQDLzWUA/8xjAP3/ + mAAAAAAAiIiIiIiIiIiIgAAIhIiIhHAAAAB3KXcXcAeQAAeTk5fQDd0ADdoK3b27uwALugm7u7u7AAtz + k9vbuwAAu2u7Xbu7AAva29u5zMzAAMzMzMzu7u4ADu7szOAO7gAO7u7u4ADuAA7u7u7uAAAA7u7u7u7g + AA7u7u7u7u7u7u7u7u4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA + + + \ No newline at end of file diff --git a/phreeqcpp/Form1.h b/phreeqcpp/Form1.h new file mode 100644 index 00000000..6a17674a --- /dev/null +++ b/phreeqcpp/Form1.h @@ -0,0 +1,1151 @@ +#pragma once +#include +#include + +namespace zdg_ui2 { + using namespace System; + //using namespace System::ComponentModel; + using namespace System::Resources; + using namespace System::Windows::Forms; + using namespace System::Drawing; + using namespace System::Drawing::Imaging; + using namespace System::Threading; + using namespace ZedGraph; + //using namespace System::Runtime::InteropServices; + //[DllImport("gdi32.dll")] + ////extern IntPtr CopyEnhMetaFileA(IntPtr hemfSrc, System::Text::StringBuilder* hNULL); + //extern IntPtr CopyEnhMetaFileA(IntPtr hemfSrc, String^ hNULL); +// Form1 is only used with MULTICHART + public ref class ChartObj : public System::Object + { + public: ChartObject* chartobject_ptr; + public: ChartObj(ChartObject* ptr) + { + this->chartobject_ptr = ptr; + } + }; + + public ref class Form1 : public System::Windows::Forms::Form + { + public: long int tickStart; + public: Form1(ChartObject *ptr) + { + this->chartobject_ptr = ptr; + if (Phreeqc* ptr = this->chartobject_ptr->Get_phreeqc()) + { + ptr->Get_chart_handler().Increment_active_charts(); + } + InitializeComponent(); + col_use = 0; + symbol_use = 0; + Y2 = false; + phreeqc_done = false; + Y2show = false; + background = true; + hints = true; + grid = true; + } + static void ThreadForm(Object^ data) + { + ChartObject *ptr = ((ChartObj^)(data))->chartobject_ptr; + Form1 ^myForm = gcnew Form1(ptr); + myForm->ShowDialog(); + myForm->~Form1(); + } + private: bool phreeqc_done; + + private: void SetSize() + { + zg1->Location = Point( 0, 0 ); + // Leave a small margin around the outside of the control + zg1->Size = System::Drawing::Size( ClientRectangle.Width - 0, + ClientRectangle.Height - 0 ); + } + + System::Void MyFormClosingEventHandler( + System::Object^ sender, + System::Windows::Forms::FormClosingEventArgs ^e) + { + ChartObject *chart = this->chartobject_ptr; + if (chart != NULL) + { + chart->Set_done(true); + System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->Get_chart_handler().Decrement_active_charts(); + //chart->Set_phreeqc(NULL); + } + } + this->chartobject_ptr = NULL; + } + + System::Void Form1_Load(System::Object ^sender, System::EventArgs ^e) + { + CreateGraph( zg1 ); + SetSize(); + } + + System::Void Form1_Resize(System::Object ^sender, System::EventArgs ^e) + { + SetSize(); + } + + //static bool LogX, LogY, LogY2; + bool LogX, LogY, LogY2; + private: bool check_neg_log( int i, int i2) + { + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) return false; + std::vector &Curves = chart->Get_Curves(); + if (LogX && chart->Get_axis_scale_x()[4] == 10.0 && + Curves[i]->Get_x()[i2] <= 0) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained x_value <= 0, removing point..."); + } + //axis_scale_x[4] = NA; /* if reverting to linear... */ + //LogX = false; + return true; + } + if (Curves[i]->Get_y()[i2] <= 0 && + (chart->Get_axis_scale_y()[4] == 10.0 || + chart->Get_axis_scale_y2()[4] == 10.0)) + { + if (Curves[i]->Get_y_axis() == 2 && LogY2) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained sy_value <= 0, removing point......"); + } + //axis_scale_y2[4] = NA; + //LogY2 = false; + return true; + } + else if (LogY) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained y_value <= 0, removing point......"); + } + //axis_scale_y[4] = NA; + //LogY = false; + return true; + } + } + return false; + } + + private: PointPairList ^list; + int col_use, symbol_use; + bool Y2, Y2show; + // static cli::array ^ColorList = {"Red", "Green", "Blue", "Orange", "Magenta", "Yellow", "Black", "Cyan", "Brown", "Lime", "Gray" }; + static cli::array ^ColorList = {"Red", "Green", "Blue", "Orange", "Magenta", "Black", "Cyan", "Brown", "Lime", "Gray" }; + bool background, hints, grid; + + ZedGraph::GraphObjList ^GOL_no_hints; + ZedGraph::GraphObjList ^GOL_hints; + + void DefineCurves(GraphPane ^myPane, int init) + { + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) + { + return; + } + std::vector Curves; + size_t i; + for (i = 0; i < chart->Get_CurvesCSV().size(); i++) + { + Curves.push_back(chart->Get_CurvesCSV()[i]); + } + for (i = 0; i < chart->Get_Curves().size(); i++) + { + Curves.push_back(chart->Get_Curves()[i]); + } + + chart->Set_curve_added(false); + + // Set the titles and axis labels + myPane->Title->Text = gcnew String(chart->Get_chart_title().c_str()); + if (chart->Get_axis_titles().size() > 0) + myPane->XAxis->Title->Text = gcnew String(chart->Get_axis_titles()[0].c_str()); + if (chart->Get_axis_titles().size() > 1) + myPane->YAxis->Title->Text = gcnew String(chart->Get_axis_titles()[1].c_str()); + if (chart->Get_axis_titles().size() > 2) + myPane->Y2Axis->Title->Text = gcnew String(chart->Get_axis_titles()[2].c_str()); + + LineItem ^myCurve; + + Color col; + + String ^s_t; + if (chart->Get_axis_scale_x()[4] == 10.0) LogX = true; + else LogX = false; + if (chart->Get_axis_scale_y()[4] == 10.0) LogY = true; + else LogY = false; + if (chart->Get_axis_scale_y2()[4] == 10.0) LogY2 = true; + else LogY2 = false; + + //Rewrite all curves + zg1->GraphPane->CurveList->Clear(); + for (size_t i = 0; i < Curves.size(); i++) + { + // even curves with no data + //if (Curves[i]->Get_x().size() == 0) continue; + list = gcnew PointPairList(); + if (Curves[i]->Get_y_axis() == 2) + { + Y2 = true; + Y2show = true; + } + else + Y2 = false; + for (int i2 = 0; (i2 < (int) Curves[i]->Get_x().size()); i2++) + { + if ((LogX && Curves[i]->Get_x()[i2] <=0) + || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + continue; + else + list->Add( Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + } + + col = Color::FromName(gcnew String(Curves[i]->Get_color().c_str())); + if (!col.IsKnownColor) + { + col = Color::FromName(ColorList[col_use]); + std::string newcol; + ToString(col.ToString(), newcol); + Utilities::replace("Color [","",newcol); + Utilities::replace("]","",newcol); + Curves[i]->Set_color(newcol); + + } + if (++col_use > 6) col_use = 0; + + SymbolType symb = chart->Return_SymbolType + (Curves[i]->Get_symbol()); + + // id + s_t = gcnew String(Curves[i]->Get_id().c_str()); + + + // Add curve to chart + myCurve = myPane->AddCurve( s_t, list, col, symb ); + + + // Curve with no points is invisible + if (Curves[i]->Get_x().size() == 0) + { + myCurve->IsVisible = false; + myCurve->Label->IsVisible = false; + } + + if (Curves[i]->Get_line_w() > 0.0) + myCurve->Line->Width = (float) Curves[i]->Get_line_w(); + else + myCurve->Line->IsVisible = false; + /* hmm... dash/dot don`t display well */ + // myCurve->Line->Style = System::Drawing::Drawing2D::DashStyle::Dot; + myCurve->Symbol->Fill = gcnew Fill( Color::FromName("White") ); + if (Curves[i]->Get_symbol_size() > 0.0) + myCurve->Symbol->Size = (float) Curves[i]->Get_symbol_size(); + else + myCurve->Symbol->IsVisible = false; + myCurve->Symbol->Border->Width = (float) Curves[i]->Get_line_w(); + if (Y2) + myCurve->IsY2Axis = true; + delete list; + } + + if (Y2show) + myPane->Legend->Position = ZedGraph::LegendPos::TopCenter; + else + myPane->Legend->Position = ZedGraph::LegendPos::Right; + myPane->Legend->FontSpec->Size = 12; + myPane->Legend->FontSpec->IsBold = false; + + // Show the x axis grid + myPane->XAxis->MajorGrid->IsVisible = true; + if (fabs(chart->Get_axis_scale_x()[0] - NA) > 1e-3) + myPane->XAxis->Scale->Min = chart->Get_axis_scale_x()[0]; + else + myPane->XAxis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_x()[1] - NA) > 1e-3) + myPane->XAxis->Scale->Max = chart->Get_axis_scale_x()[1]; + else + myPane->XAxis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_x()[2] - NA) > 1e-3) + myPane->XAxis->Scale->MajorStep = chart->Get_axis_scale_x()[2]; + else + myPane->XAxis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_x()[3] - NA) > 1e-3) + { + myPane->XAxis->Scale->MinorStep = chart->Get_axis_scale_x()[3]; + if (chart->Get_axis_scale_x()[3] == 0.0) + // remove minor tics + myPane->XAxis->MinorTic->Size = 0; + } + else + myPane->XAxis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_x()[4] == 10.0) + myPane->XAxis->Type = AxisType::Log; + + // Make the Y axis scale red + // myPane->YAxis->Scale->FontSpec->FontColor = Color::Red; + // myPane->YAxis->Title->FontSpec->FontColor = Color::Red; + // turn off the opposite tics so the Y tics don`t show up on the Y2 axis + if (Y2show) + { + myPane->YAxis->MajorTic->IsOpposite = false; + myPane->YAxis->MinorTic->IsOpposite = false; + } + // Don`t display the Y zero line + myPane->YAxis->MajorGrid->IsZeroLine = false; + // Align the Y axis labels so they are flush to the axis + myPane->YAxis->Scale->Align = AlignP::Inside; + myPane->YAxis->MajorGrid->IsVisible = true; + if (fabs(chart->Get_axis_scale_y()[0] - NA) > 1e-3) + myPane->YAxis->Scale->Min = chart->Get_axis_scale_y()[0]; + else + myPane->YAxis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_y()[1] - NA) > 1e-3) + myPane->YAxis->Scale->Max = chart->Get_axis_scale_y()[1]; + else + myPane->YAxis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_y()[2] - NA) > 1e-3) + myPane->YAxis->Scale->MajorStep = chart->Get_axis_scale_y()[2]; + else + myPane->YAxis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_y()[3] - NA) > 1e-3) + { + myPane->YAxis->Scale->MinorStep = chart->Get_axis_scale_y()[3]; + if (chart->Get_axis_scale_y()[3] == 0.0) + // remove minor tics + myPane->YAxis->MinorTic->Size = 0; + } + else + myPane->YAxis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_y()[4] == 10.0) + myPane->YAxis->Type = AxisType::Log; + + // Enable the Y2 axis display + if (Y2show) + { + myPane->Y2Axis->IsVisible = true; + // Make the Y2 axis scale blue + // myPane->Y2Axis->Scale->FontSpec->FontColor = Color::Blue; + // myPane->Y2Axis->Title->FontSpec->FontColor = Color::Blue; + // turn off the opposite tics so the Y2 tics don`t show up on the Y axis + myPane->Y2Axis->MajorTic->IsOpposite = false; + myPane->Y2Axis->MinorTic->IsOpposite = false; + // Don`t display the Y2 axis grid lines + myPane->Y2Axis->MajorGrid->IsVisible = false; + // Align the Y2 axis labels so they are flush to the axis + myPane->Y2Axis->Scale->Align = AlignP::Inside; + + if (fabs(chart->Get_axis_scale_y2()[0] - NA) > 1e-3) + myPane->Y2Axis->Scale->Min = chart->Get_axis_scale_y2()[0]; + else + myPane->Y2Axis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_y2()[1] - NA) > 1e-3) + myPane->Y2Axis->Scale->Max = chart->Get_axis_scale_y2()[1]; + else + myPane->Y2Axis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_y2()[2] - NA) > 1e-3) + myPane->Y2Axis->Scale->MajorStep = chart->Get_axis_scale_y2()[2]; + else + myPane->Y2Axis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_y2()[3] - NA) > 1e-3) + { + myPane->Y2Axis->Scale->MinorStep = chart->Get_axis_scale_y2()[3]; + if (chart->Get_axis_scale_y2()[3] == 0.0) + // remove minor tics + myPane->Y2Axis->MinorTic->Size = 0; + } + else + myPane->Y2Axis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_y2()[4] == 10.0) + myPane->Y2Axis->Type = AxisType::Log; + } + + myPane->XAxis->MinorTic->IsOutside = false; + myPane->XAxis->MajorTic->IsOutside = false; + myPane->YAxis->MinorTic->IsOutside = false; + myPane->YAxis->MajorTic->IsOutside = false; + myPane->Y2Axis->MinorTic->IsOutside = false; + myPane->Y2Axis->MajorTic->IsOutside = false; + + // Fill the axis background with a gradient + //myPane->Chart->Fill = gcnew Fill( Color::White, Color::LightYellow, 45.0f ); /* FromArgb(255, 255, 224) */ + if (this->background) + { + myPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + myPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + if (this->grid) + { + myPane->XAxis->MajorGrid->IsVisible = true; + myPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + myPane->XAxis->MajorGrid->IsVisible = false; + myPane->YAxis->MajorGrid->IsVisible = false; + } + // normalize pane size... + myPane->BaseDimension = 8.0F; + // increase bottom margin to accommodate text options... + myPane->Margin->Bottom = 15.0F; + + // Make sure auto scale, Refresh + zg1->AxisChange(); + zg1->Refresh(); + + } + + public: void CreateGraph( ZedGraphControl ^z1 ) { + // Get a reference to the GraphPane instance in the ZedGraphControl + GraphPane ^myPane = z1->GraphPane; + + // lock thread + while (0 != System::Threading::Interlocked::CompareExchange(this->chartobject_ptr->usingResource, 2, 0)) + { + System::Threading::Thread::Sleep(5); + } + + try + { + + DefineCurves(myPane, 0); + + // Add text boxes with instructions... + GOL_no_hints = gcnew ZedGraph::GraphObjList(myPane->GraphObjList); + + TextObj ^text; + text = gcnew TextObj( + L" Click right mouse for options... \0", + 0.01f, 0.99f, CoordType::PaneFraction, AlignH::Left, AlignV::Bottom ); + text->FontSpec->StringAlignment = StringAlignment::Near; + text->FontSpec->Size = 10; + text->FontSpec->FontColor = Color::Red; + text->ZOrder = ZOrder::H_BehindAll; + myPane->GraphObjList->Add( text ); + text = gcnew TextObj( + L" Press Alt + F4 to quit", + 0.81f, 0.99f, CoordType::PaneFraction, AlignH::Left, AlignV::Bottom ); + text->FontSpec->StringAlignment = StringAlignment::Near; + text->FontSpec->Size = 10; + text->FontSpec->FontColor = Color::Red; + text->ZOrder = ZOrder::H_BehindAll; + myPane->GraphObjList->Add( text ); + + GOL_hints = gcnew ZedGraph::GraphObjList(myPane->GraphObjList); + if (this->hints) + { + myPane->GraphObjList = GOL_hints; + } + else + { + myPane->GraphObjList = GOL_no_hints; + } + + // Enable scrollbars if needed... + /*z1->IsShowHScrollBar = true; + z1->IsShowVScrollBar = true; + z1->IsAutoScrollRange = true; + z1->IsScrollY2 = true;*/ + + // OPTIONAL: Show tooltips when the mouse hovers over a point + z1->IsShowPointValues = false; + z1->PointValueEvent += gcnew ZedGraphControl::PointValueHandler( this, + &Form1::MyPointValueHandler ); + + // OPTIONAL: Add a custom context menu item + z1->ContextMenuBuilder += gcnew ZedGraphControl::ContextMenuBuilderEventHandler( + this, &Form1::MyContextMenuBuilder ); + + // OPTIONAL: Handle the Zoom Event + z1->ZoomEvent += gcnew ZedGraphControl::ZoomEventHandler( this, + &Form1::MyZoomEvent ); + + // Size the control to fit the window + SetSize(); + + // Tell ZedGraph to calculate the axis ranges + // Note that you MUST call this after enabling IsAutoScrollRange, since AxisChange() sets + // up the proper scrolling parameters + + z1->AxisChange(); + // Make sure the Graph gets redrawn + z1->Invalidate(); + timer1->Interval = this->chartobject_ptr->Get_update_time_chart(); + timer1->Enabled = true; + timer1->Start(); + + tickStart = Environment::TickCount; + } + catch (...) + { + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 2); + throw; + } + + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 2); + } + + /// + /// Display customized tooltips when the mouse hovers over a point + /// + System::String ^MyPointValueHandler( ZedGraphControl ^control, GraphPane ^pane, + CurveItem ^curve, int iPt ) { + // Get the PointPair that is under the mouse + PointPair pt = curve[iPt]; + return curve->Label->Text + " is " + pt.Y.ToString( "e3" ) + " units at X = " + pt.X.ToString( "e3" ); + } + + // Add some explanation to the menu.. + void MyContextMenuBuilder( ZedGraphControl ^control, + System::Windows::Forms::ContextMenuStrip ^menuStrip, + Point mousePt, + ZedGraphControl::ContextMenuObjectState objState ) { + + // removes Copy + menuStrip->Items->RemoveAt(0); + // removes Save Image As + menuStrip->Items->RemoveAt(0); + + ToolStripMenuItem ^item = gcnew ToolStripMenuItem(); + item->Text = L"Zoom: left mouse + drag\nPan: middle mouse + drag"; + menuStrip->Items->Insert(5, item ); + + ToolStripMenuItem ^item3 = gcnew ToolStripMenuItem(); + item3->Text = L"Chart options..."; + item3->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SetChartOptions ); + menuStrip->Items->Insert(0, item3 ); + + //ToolStripMenuItem ^item5 = gcnew ToolStripMenuItem(); + //item5->Text = L"Toggle Hints"; + //item5->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::ToggleHints ); + //menuStrip->Items->Insert(0, item5 ); + + ToolStripMenuItem ^item2 = gcnew ToolStripMenuItem(); + item2->Text = L"Save Data to File..."; + item2->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SaveCurves ); + menuStrip->Items->Insert(0, item2 ); + + ToolStripMenuItem ^item4 = gcnew ToolStripMenuItem(); + item4->Text = L"Save Image As..."; + item4->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SaveImage ); + menuStrip->Items->Insert(0, item4 ); + + } + + void form_error_msg( std::string estring ) + { + if (this->chartobject_ptr != NULL) + { + if (Phreeqc* ptr = this->chartobject_ptr->Get_phreeqc()) + { + ptr->error_msg(estring.c_str(), CONTINUE); + } + } + else + { + std::cerr << "ERROR: " << estring << std::endl; + } + } + + void SaveCurves( System::Object ^sender, System::EventArgs ^e ) + { + SaveFileDialog^ saveFileDialog1 = gcnew SaveFileDialog; + //TCHAR dir[MAX_PATH]; + //::GetCurrentDirectory(MAX_PATH, dir); + //String ^d = gcnew String(dir); + //saveFileDialog1->InitialDirectory = d; + saveFileDialog1->FileName = "curves.u_g"; + saveFileDialog1->Filter = "User graph files (*.u_g)|*.u_g|txt files (*.txt)|*.txt|All files (*.*)|*.*"; + saveFileDialog1->FilterIndex = 1; + saveFileDialog1->RestoreDirectory = true; +#undef OK + if ( saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) + { + std::string file_name; + ToString(saveFileDialog1->FileName, file_name); + std::ofstream f_out(file_name.c_str(), std::ifstream::out); + + if (!f_out.is_open()) + { + std::ostringstream estream; + estream << "Could not open file to save curves for USER_GRAPH " << file_name; + form_error_msg(estream.str()); + return; + } + + // write headings + size_t max_points = 0; + f_out.precision(4); + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + LineItem ^curve; + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + + // Calculate max_points + if ((size_t) ip->Count > max_points) + max_points = ip->Count; + + // write headers + std::string s_std; + ToString(curve->Label->Text, s_std); + f_out.width(12); + f_out << "x" << "\t"; + f_out.width(12); + if (s_std.size() > 0) + { + f_out << s_std << "\t"; + } + else + { + f_out << "y" << "\t"; + } + } + + f_out << std::endl; + + // write data + size_t i2 = 0; + f_out << std::scientific; + f_out.precision(4); + + while (i2 < max_points) + { + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + LineItem ^curve; + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + if (i2 < (size_t) ip->Count) + { + //double x = ip[i]->X; + f_out.width(12); + f_out << ip[(int) i2]->X << "\t"; + f_out.width(12); + f_out << ip[(int) i2]->Y << "\t"; + } + else if (i2 < max_points) + { + f_out.width(13); + f_out << "\t"; + f_out.width(13); + f_out << "\t"; + } + } + f_out << std::endl; + i2++; + } + f_out.close(); + + + } + else + { + // no dialog + std::ostringstream estream; + estream << "Could not open dialog to save curves. "; + form_error_msg(estream.str()); + return; + } + return; + } + + // Respond to a Zoom Event + void MyZoomEvent( ZedGraphControl ^control, ZoomState ^oldState, ZoomState ^newState ) + { + // Here we get notification everytime the user zooms + } + void SetChartOptions( System::Object ^sender, System::EventArgs ^e ) + { + // Create form + Form ^graphOptions = gcnew Form; + graphOptions->Text = "Chart options"; + graphOptions->ShowIcon = false; + graphOptions->BringToFront(); + graphOptions->MinimumSize = System::Drawing::Size(255, 230); + graphOptions->MaximumSize = System::Drawing::Size(255, 230); + graphOptions->Size = System::Drawing::Size(255, 230); + graphOptions->StartPosition = System::Windows::Forms::FormStartPosition::CenterParent; + + // Check box for hints + CheckBox ^cb1 = gcnew CheckBox; + cb1->Appearance = Appearance::Normal; + cb1->ThreeState = false; + cb1->AutoCheck = true; + cb1->Location = System::Drawing::Point(5, 10); + if (this->hints) + { + cb1->CheckState = CheckState::Checked; + } + else + { + cb1->CheckState = CheckState::Unchecked; + } + cb1->Text = "Show hints"; + cb1->Visible = true; + graphOptions->Controls->Add(cb1); + + // Check box for background color + CheckBox ^cb2 = gcnew CheckBox; + cb2->Appearance = Appearance::Normal; + cb2->ThreeState = false; + cb2->AutoCheck = true; + cb2->Location = System::Drawing::Point(5, 60); + + if (this->background) + { + cb2->CheckState = CheckState::Checked; + } + else + { + cb2->CheckState = CheckState::Unchecked; + } + cb2->Text = "Show colored background"; + cb2->AutoSize = true; + cb2->Visible = true; + graphOptions->Controls->Add(cb2); + + // checkbox for grid + CheckBox ^cb3 = gcnew CheckBox; + cb3->Appearance = Appearance::Normal; + cb3->ThreeState = false; + cb3->AutoCheck = true; + cb3->Location = System::Drawing::Point(5, 110); + + if (this->grid) + { + cb3->CheckState = CheckState::Checked; + } + else + { + cb3->CheckState = CheckState::Unchecked; + } + cb3->Text = "Show grid lines"; + cb3->Visible = true; + graphOptions->Controls->Add(cb3); + + // done button for Form + Button^ button1 = gcnew Button; + button1->DialogResult = System::Windows::Forms::DialogResult::OK; + button1->Text = "Done"; + button1->Location = System::Drawing::Point(75, 160); + graphOptions->Controls->Add(button1); + graphOptions->AcceptButton = button1; + + // cancel button for Form + Button^ button2 = gcnew Button; + button2->DialogResult = System::Windows::Forms::DialogResult::Cancel; + button2->Text = "Cancel"; + button2->Location = System::Drawing::Point(155, 160); + graphOptions->Controls->Add(button2); + graphOptions->CancelButton = button2; + + + if (graphOptions->ShowDialog() == System::Windows::Forms::DialogResult::OK) + { + this->hints = (cb1->CheckState == CheckState::Checked); + this->background = (cb2->CheckState == CheckState::Checked); + this->grid = (cb3->CheckState == CheckState::Checked); + } + + if (this->background) + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + if (this->grid) + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = true; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = false; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = false; + } + if (this->hints) + { + zg1->GraphPane->GraphObjList = GOL_hints; + } + else + { + zg1->GraphPane->GraphObjList = GOL_no_hints; + } + zg1->Refresh(); + } + void SaveImage( System::Object ^sender, System::EventArgs ^e ) + { + ZedGraph::GraphObjList ^copy = gcnew ZedGraph::GraphObjList(zg1->GraphPane->GraphObjList); + zg1->GraphPane->GraphObjList = GOL_no_hints; + zg1->SaveAs(); + zg1->GraphPane->GraphObjList = copy; + } + void BatchSaveImage( ) + { + ChartObject *chart = this->chartobject_ptr; + assert(chart->Get_batch() > 0); + // Save GraphObjList + ZedGraph::GraphObjList ^GOL_copy = gcnew ZedGraph::GraphObjList(zg1->GraphPane->GraphObjList); + + // Don`t write red hint boxes + zg1->GraphPane->GraphObjList = GOL_no_hints; + + // Set background + if (chart->Get_batch_background()) + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + // Set grid + if (chart->Get_batch_grid()) + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = true; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = false; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = false; + } + // Save the graph + if (this->zg1) + { + ImageFormat ^format = ImageFormat::Png; + switch (chart->Get_batch()) + { + case 2: + format = ImageFormat::Png; + break; + case 3: + format = ImageFormat::Gif; + break; + case 4: + format = ImageFormat::Jpeg; + break; + case 5: + format = ImageFormat::Tiff; + break; + case 6: + format = ImageFormat::Bmp; + break; + default: + break; + } + switch (chart->Get_batch()) + { + case 1: // emf + { + System::String ^fn = gcnew System::String(chart->Get_batch_fn().c_str()); + System::Drawing::Imaging::Metafile ^metaFile = this->zg1->MasterPane->GetMetafile(); + metaFile->Save(fn); + } + break; + case 2: // bitmaps + case 3: + case 4: + case 5: + case 6: + { + System::String ^fn = gcnew System::String(chart->Get_batch_fn().c_str()); + System::IO::FileStream ^myStream = gcnew System::IO::FileStream(fn, System::IO::FileMode::Create); + zg1->MasterPane->GetImage()->Save( myStream, format); + myStream->Close(); + } + default: + break; + } + } + + } + private: void timer1_Tick(System::Object ^sender, System::EventArgs ^e ) + { + LineItem ^curve; + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) return; + + //lock for thread + while (0 != System::Threading::Interlocked::CompareExchange(chart->usingResource, 3, 0)) + { + System::Threading::Thread::Sleep(5); + } + + try + { + if (this->chartobject_ptr->Get_curve_added()) + { + DefineCurves(zg1->GraphPane, zg1->GraphPane->CurveList->Count); + } + else if (this->chartobject_ptr->Get_point_added()) + { + + // Make list of curves + std::vector Curves; + size_t j; + for (j = 0; j < chart->Get_CurvesCSV().size(); j++) + { + Curves.push_back(chart->Get_CurvesCSV()[j]); + } + for (j = 0; j < chart->Get_Curves().size(); j++) + { + Curves.push_back(chart->Get_Curves()[j]); + } + // Add points to curves ... + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + if ((size_t) ip->Count < Curves[i]->Get_x().size()) + { + if (Curves[i]->Get_y_axis() == 2) + Y2 = true; + else + Y2 = false; + for ( size_t i2 = ip->Count; i2 < Curves[i]->Get_x().size(); i2++ ) + { + if ((LogX && Curves[i]->Get_x()[i2] <=0) + || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + continue; + else + ip->Add(Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + } + } + } + // Add points to curves ... + + //size_t i, k; + //k = 0; + //for (i = 0; i < Curves.size(); i++) + //{ + // if (Curves[i]->Get_x().size() == 0) continue; + // curve = (LineItem ^) zg1->GraphPane->CurveList[k++]; + // // Get the PointPairList + // IPointListEdit ^ip = (IPointListEdit^) curve->Points; + // if ((size_t) ip->Count < Curves[i]->Get_x().size()) + // { + // if (Curves[i]->Get_y_axis() == 2) + // Y2 = true; + // else + // Y2 = false; + // for ( size_t i2 = ip->Count; i2 < Curves[i]->Get_x().size(); i2++ ) + // { + // if ((LogX && Curves[i]->Get_x()[i2] <=0) + // || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + // || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + // continue; + // else + // ip->Add(Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + // } + // } + //} + /* explicitly reset the max in case of log scale, zedgraphs doesn`t do this... */ + if ((fabs(chart->Get_axis_scale_x()[1] - NA) < 1e-3) && zg1->GraphPane->XAxis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + if (Curves[i]->Get_x()[Curves[i]->Get_x().size() - 1] > max) + max = Curves[i]->Get_x()[Curves[i]->Get_x().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->XAxis->Scale->Max = max; + } + if ((fabs(chart->Get_axis_scale_y()[1] - NA) < 1e-3) && zg1->GraphPane->YAxis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + if (curve->IsY2Axis) continue; + if (Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1] > max) + max = Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->YAxis->Scale->Max = max; + } + if ((fabs(chart->Get_axis_scale_y2()[1] - NA) < 1e-3) && zg1->GraphPane->Y2Axis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + if (!curve->IsY2Axis) continue; + if (Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1] > max) + max = Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->Y2Axis->Scale->Max = max; + } + zg1->AxisChange(); + zg1->Refresh(); + } + // + // Following asserts may not be true for Log scales + // negative values are rejected, so chart may have fewer points than phreeqc + // + //for (size_t j = 0; j < chart->Get_CurvesCSV().size(); j++) + //{ + // if (zg1->GraphPane->CurveList[j]->Points->Count != chart->Get_CurvesCSV()[j]->Get_x().size()) + // { + // fprintf(stderr, "graph points = %d\n", zg1->GraphPane->CurveList[j]->Points->Count); + // fprintf(stderr, "phreeqc points = %d\n", chart->Get_CurvesCSV()[j]->Get_x().size()); + // } + // assert(zg1->GraphPane->CurveList[j]->Points->Count == chart->Get_CurvesCSV()[j]->Get_x().size()); + //} + //for (int j = chart->Get_CurvesCSV().size(); j < zg1->GraphPane->CurveList->Count; j++) + //{ + // int k = j - chart->Get_CurvesCSV().size(); + // if (zg1->GraphPane->CurveList[j]->Points->Count != chart->Get_Curves()[k]->Get_x().size()) + // { + // fprintf(stderr, "%d %d graph points = %d\n", j, k, zg1->GraphPane->CurveList[j]->Points->Count); + // fprintf(stderr, "phreeqc points = %d\n", chart->Get_Curves()[k]->Get_x().size()); + // } + // assert(zg1->GraphPane->CurveList[j]->Points->Count == chart->Get_Curves()[k]->Get_x().size()); + //} + chart->Set_point_added(false); + if (chart->Get_end_timer()) + { + timer1->Stop(); + chart->Set_done(true); + phreeqc_done = true; + + int batch = chart->Get_batch(); + if (batch > 0) + { + BatchSaveImage(); + } + + //unlock thread before setting chartobject_ptr to NULL + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + + //this->phreeqc_ptr = NULL; + this->chartobject_ptr = NULL; + if (batch >= 0) + { + this->Close(); + } + return; + } + } + catch(...) + { + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + + throw; + } + + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + //tickStart = Environment::TickCount; + return; + } + + void ToString(System::String^ src, std::string& dest) + { + using namespace System::Runtime::InteropServices; + const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(src)).ToPointer(); + dest = chars; + Marshal::FreeHGlobal(IntPtr((void*)chars)); + } + ~Form1() { + if (this->zg1) delete zg1; + //if (this->timer1) delete timer1); + if (components) { + delete components; + } + } + public: ZedGraph::ZedGraphControl ^zg1; + private: System::Windows::Forms::Timer ^timer1; + private: System::ComponentModel::Container ^components; + ChartObject * chartobject_ptr; + + public: +#pragma region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + void InitializeComponent() + { + //System::Threading::Thread::Sleep(5000); + this->components = (gcnew System::ComponentModel::Container()); + this->zg1 = (gcnew ZedGraph::ZedGraphControl()); + this->timer1 = (gcnew System::Windows::Forms::Timer( this->components )); + this->SuspendLayout(); + // + // zg1 + // + this->zg1->Location = System::Drawing::Point(12, 12); + this->zg1->Name = L"zg1"; + this->zg1->ScrollGrace = 0; + this->zg1->ScrollMaxX = 0; + this->zg1->ScrollMaxY = 0; + this->zg1->ScrollMaxY2 = 0; + this->zg1->ScrollMinX = 0; + this->zg1->ScrollMinY = 0; + this->zg1->ScrollMinY2 = 0; + this->zg1->Size = System::Drawing::Size(this->chartobject_ptr->Get_PanelWidth() - 2 * 12, chartobject_ptr->Get_PanelHeight() - 2 * 12); + this->zg1->TabIndex = 0; + this->timer1->Tick += gcnew System::EventHandler( this, &Form1::timer1_Tick ); + // + // Form1 + // + this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); + this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; + this->AutoValidate = System::Windows::Forms::AutoValidate::EnablePreventFocusChange; + this->ClientSize = System::Drawing::Size(this->chartobject_ptr->Get_PanelWidth(), chartobject_ptr->Get_PanelHeight()); + this->Controls->Add(this->zg1); + this->Name = L"Form1"; + this->StartPosition = System::Windows::Forms::FormStartPosition::WindowsDefaultLocation;//:CenterScreen; + //this->Text = L"PHREEQC chart"; + System::String ^desc = gcnew String(this->chartobject_ptr->Get_description().c_str()); + this->Text = L"Phreeqc USER_GRAPH " + this->chartobject_ptr->Get_n_user() + " " + desc; + this->TopMost = false; + System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(Form1::typeid)); + try + { + //this->Icon = gcnew System::Drawing::Icon("c:\\phreeqc\\phreex.ico"); + this->Icon = (cli::safe_cast(resources->GetObject(L"$this.Icon"))); + } + catch (...) + { + } + + this->FormClosing += gcnew System::Windows::Forms::FormClosingEventHandler(this, &Form1::MyFormClosingEventHandler); + this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load); + this->Resize += gcnew System::EventHandler(this, &Form1::Form1_Resize); + this->ResumeLayout(false); + } +#pragma endregion + }; +} \ No newline at end of file diff --git a/phreeqcpp/Form1.resX b/phreeqcpp/Form1.resX new file mode 100644 index 00000000..8cb449b9 --- /dev/null +++ b/phreeqcpp/Form1.resX @@ -0,0 +1,36 @@ + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP8AMzOaAGgymgA0Mc0AmWVoAGdlmwBjZssAy5kvAP+ZNQDLmmMAzpmVAP/MLQDLzWUA/8xjAP3/ + mAAAAAAAiIiIiIiIiIiIgAAIhIiIhHAAAAB3KXcXcAeQAAeTk5fQDd0ADdoK3b27uwALugm7u7u7AAtz + k9vbuwAAu2u7Xbu7AAva29u5zMzAAMzMzMzu7u4ADu7szOAO7gAO7u7u4ADuAA7u7u7uAAAA7u7u7u7g + AA7u7u7u7u7u7u7u7u4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA + + + \ No newline at end of file diff --git a/phreeqcpp/GasComp.cxx b/phreeqcpp/GasComp.cxx new file mode 100644 index 00000000..427d169b --- /dev/null +++ b/phreeqcpp/GasComp.cxx @@ -0,0 +1,265 @@ +// GasComp.cxx: implementation of the cxxGasComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "GasComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxGasComp::cxxGasComp(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : PHRQ_base(io) +{ + p_read = 0.0; + moles = 0.0; + initial_moles = 0.0; + p = 0.0; + phi = 0.0; + f = 0.0; +} + +cxxGasComp::~cxxGasComp() +{ +} + +void +cxxGasComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + s_oss << indent0 << "# GAS_PHASE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-moles " << this->moles << "\n"; + + s_oss << indent0 << "# GAS_PHASE_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent0 << "-p_read " << this->p_read << "\n"; + + s_oss << indent0 << "# GasComp workspace variables #\n"; + s_oss << indent0 << "-initial_moles " << this->initial_moles << "\n"; + s_oss << indent0 << "-p " << this->p << "\n"; + s_oss << indent0 << "-phi " << this->phi << "\n"; + s_oss << indent0 << "-f " << this->f << "\n"; +} + +bool +cxxGasComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + int errors = parser.get_input_error(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + + bool moles_defined(false); + int opt; + for (;;) + { + opt = parser.get_option(vopts, next_char); + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // phase_name + output_msg("-phase_name is obsolete. Define with -component\n"); + break; + + case 1: // name + output_msg("-name is obsolete. Define with -component\n"); + break; + + case 2: // p_read + if (!(parser.get_iss() >> this->p_read)) + { + this->p_read = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial partial pressure.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 3: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 4: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // p + if (!(parser.get_iss() >> this->p)) + { + this->p = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pressure.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 6: // phi + if (!(parser.get_iss() >> this->phi)) + { + this->phi = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for phi.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 7: // f + if (!(parser.get_iss() >> this->f)) + { + this->f = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for f.", + PHRQ_io::OT_CONTINUE); + } + break; + + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for GasComp input.", + PHRQ_io::OT_CONTINUE); + } + } + return (parser.get_input_error() == errors); +} +void +cxxGasComp::add(const cxxGasComp & addee, LDBLE extensive) +{ + //LDBLE ext1, ext2; + if (extensive == 0.0) + return; + if (addee.phase_name.size() == 0) + return; + + double f1, f2; + double ext1 = this->moles; + double ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + + assert(this->phase_name == addee.phase_name); + + this->p_read = this->p_read*f1 + addee.p_read * f2; + this->moles += addee.moles * extensive; + this->initial_moles += addee.initial_moles * extensive; + this->p = this->p * f1 + addee.p * f2; + this->phi = this->phi * f1 + addee.phi * f2; + this->f = this->f * f1 + addee.f * f2; +} + +void +cxxGasComp::multiply(LDBLE extensive) +{ + this->p_read *= extensive; + this->moles *= extensive; + this->initial_moles *= extensive; + this->p *= 1.0; + this->phi *= 1.0; + this->f *= 1.0; +} +void +cxxGasComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->moles); + doubles.push_back(this->p_read); + doubles.push_back(this->initial_moles); + doubles.push_back(this->p); + doubles.push_back(this->phi); + doubles.push_back(this->f); +} + +void +cxxGasComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->moles = doubles[dd++]; + this->p_read = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->p = doubles[dd++]; + this->phi = doubles[dd++]; + this->f = doubles[dd++]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("phase_name"), // 0 + std::vector< std::string >::value_type("name"), // 1 + std::vector< std::string >::value_type("p_read"), // 2 + std::vector< std::string >::value_type("moles"), // 3 + std::vector< std::string >::value_type("initial_moles"), // 4 + std::vector< std::string >::value_type("p"), // 5 + std::vector< std::string >::value_type("phi"), // 6 + std::vector< std::string >::value_type("f") // 7 +}; +const std::vector< std::string > cxxGasComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/GasComp.h b/phreeqcpp/GasComp.h new file mode 100644 index 00000000..ce44e100 --- /dev/null +++ b/phreeqcpp/GasComp.h @@ -0,0 +1,59 @@ +#if !defined(GASCOMP_H_INCLUDED) +#define GASCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxGasComp: public PHRQ_base +{ + + public: + cxxGasComp(PHRQ_io *io=NULL); + + virtual ~cxxGasComp(); + + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + bool read_raw(CParser & parser, bool check=true); + + std::string Get_phase_name(void) const {return this->phase_name;} + void Set_phase_name(std::string s) {this->phase_name = s;} + LDBLE Get_p_read() const {return this->p_read;} + void Set_p_read(LDBLE t) {this->p_read = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_p() const { return this->p; } + void Set_p(LDBLE t) { this->p = t; } + LDBLE Get_phi() const { return this->phi; } + void Set_phi(LDBLE t) { this->phi = t; } + LDBLE Get_f() const { return this->f; } + void Set_f(LDBLE t) { this->f = t; } + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + + void add(const cxxGasComp & addee, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + std::string phase_name; + // GAS_PHASE_MODIFY candidates + LDBLE moles; + // GAS_PHASE_MODIFY candidates with new_def=true + LDBLE p_read; + // internal workspace + LDBLE initial_moles; + LDBLE p; + LDBLE phi; + LDBLE f; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(GASCOMP_H_INCLUDED) diff --git a/phreeqcpp/GasPhase.cxx b/phreeqcpp/GasPhase.cxx new file mode 100644 index 00000000..d4c651ef --- /dev/null +++ b/phreeqcpp/GasPhase.cxx @@ -0,0 +1,659 @@ +// GasPhase.cxx: implementation of the cxxGasPhase class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "GasPhase.h" +#include "cxxMix.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxGasPhase::cxxGasPhase(PHRQ_io * io) + // + // default constructor for cxxGasPhase + // +: cxxNumKeyword(io) +{ + new_def = false; + solution_equilibria = false; + n_solution = -999; + type = cxxGasPhase::GP_PRESSURE; + total_p = 1.0; + total_moles = 0.0; + volume = 1.0; + v_m = 0; + pr_in = false; + temperature = 298.15; +} +cxxGasPhase::cxxGasPhase(std::map < int, cxxGasPhase > &entity_map, + cxxMix & mx, int l_n_user, PHRQ_io * io) +: cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + total_p = 0; + volume = 0; + v_m = 0; + pr_in = false; + bool first = true; + new_def = false; + solution_equilibria = false; + n_solution = -999; + type = cxxGasPhase::GP_PRESSURE; + total_moles = 0.0; + temperature = 298.15; + +// +// Mix +// + // accumulate in map + std::map comp_map; + std::map::iterator comp_it; + + const std::map < int, LDBLE > & mixcomps = mx.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + this->total_p = 0; + double sum_fractions = 0.0; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + sum_fractions += it->second; + } + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entity_map.find(it->first) != entity_map.end()) + { + const cxxGasPhase *entity_ptr = &(entity_map.find(it->first)->second); + if (first) + { + this->new_def = entity_ptr->new_def; + this->solution_equilibria = entity_ptr->solution_equilibria; + this->n_solution = entity_ptr->n_solution; + this->type = entity_ptr->type; + this->total_moles = entity_ptr->total_moles * it->second; + this->volume = entity_ptr->volume * it->second; + if (sum_fractions > 0.0) + { + this->v_m = entity_ptr->v_m * it->second / sum_fractions; + this->total_p += entity_ptr->total_p * it->second / sum_fractions; + } + else + { + this->v_m = 0.0; + this->total_p = 0.0; + } + this->pr_in = entity_ptr->pr_in; + this->temperature = entity_ptr->temperature; + first = false; + } + else + { + if (this->type != entity_ptr->type) + { + std::ostringstream oss; + oss << "Cannot mix two gas_phases with differing types."; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + + this->total_moles += entity_ptr->total_moles * it->second; + this->volume += entity_ptr->volume * it->second; + if (sum_fractions > 0.0) + { + this->v_m += entity_ptr->v_m * it->second / sum_fractions; + this->total_p += entity_ptr->total_p * it->second / sum_fractions; + } + } + } + cxxGasPhase *gas_phase_ptr = Utilities::Rxn_find(entity_map, it->first); + if (gas_phase_ptr) + { + std::vector add_comps = gas_phase_ptr->Get_gas_comps(); + for (size_t i = 0; i < add_comps.size(); i++) + { + comp_it = comp_map.find(add_comps[i].Get_phase_name()); + if (comp_it != comp_map.end()) + { + comp_it->second.add(add_comps[i], it->second); + } + else + { + cxxGasComp gc(add_comps[i]); + gc.multiply(it->second); + comp_map[add_comps[i].Get_phase_name()] = gc; + } + } + + } + } + + // put map into vector + this->gas_comps.clear(); + std::vector gc; + for (comp_it = comp_map.begin(); comp_it != comp_map.end(); comp_it++) + { + this->gas_comps.push_back(comp_it->second); + } +} +cxxGasPhase::~cxxGasPhase() +{ +} + +void +cxxGasPhase::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // GasPhase element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "GAS_PHASE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# GAS_PHASE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-type " << this->type << "\n"; + s_oss << indent1; + s_oss << "-total_p " << this->total_p << "\n"; + s_oss << indent1; + s_oss << "-volume " << this->volume << "\n"; + // gasPhaseComps + for (size_t i = 0 ; i < this->gas_comps.size(); i++) + { + s_oss << indent1; + s_oss << "-component " << this->gas_comps[i].Get_phase_name() << "\n"; + this->gas_comps[i].dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# GAS_PHASE_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << this->new_def << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << this->solution_equilibria << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + s_oss << indent1; + s_oss << "-temperature " << this->temperature << "\n"; + + s_oss << indent1 << "# GasPhase workspace variables #\n"; + s_oss << indent1; + s_oss << "-total_moles " << this->total_moles << "\n"; + s_oss << indent1; + s_oss << "-v_m " << this->v_m << "\n"; + s_oss << indent1; + s_oss << "-pr_in " << (this->pr_in ? 1 : 0) << "\n"; + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} + +void +cxxGasPhase::read_raw(CParser & parser, bool check) +{ + + int i; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read gas_phase number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + bool type_defined(false); + bool total_p_defined(false); + bool volume_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + CParser::ECHO_OPTION eo = parser.get_echo_file(); + parser.set_echo_file(CParser::EO_NONE); + opt = parser.getOptionFromLastLine(vopts, next_char, true); + parser.set_echo_file(eo); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in GAS_PHASE_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // type + if (!(parser.get_iss() >> i)) + { + this->type = cxxGasPhase::GP_PRESSURE; + parser.incr_input_error(); + parser.error_msg("Expected enum for type.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->type = (cxxGasPhase::GP_TYPE) i; + } + type_defined = true; + useLastLine = false; + break; + + case 1: // total_p + case 5: // pressure + if (!(parser.get_iss() >> this->total_p)) + { + this->total_p = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_p.", + PHRQ_io::OT_CONTINUE); + } + total_p_defined = true; + useLastLine = false; + break; + + case 2: // volume + if (!(parser.get_iss() >> this->volume)) + { + this->volume = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for volume.", + PHRQ_io::OT_CONTINUE); + } + volume_defined = true; + useLastLine = false; + break; + + case 3: // v_m + if (!(parser.get_iss() >> this->v_m)) + { + this->v_m = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for v_m.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 4: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxGasComp temp_comp(io); + temp_comp.Set_phase_name(str); + cxxGasComp * comp_ptr = this->Find_comp(str.c_str()); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->gas_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->gas_comps[j].Get_phase_name().c_str(), str.c_str()) == 0) + { + this->gas_comps[j] = temp_comp; + } + } + } + else + { + this->gas_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + + opt_save = CParser::OPT_DEFAULT; + break; + case 6: // pr_in + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for pr_in.", PHRQ_io::OT_CONTINUE); + } + else + { + this->pr_in = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 7: // new_def + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for new_def.", PHRQ_io::OT_CONTINUE); + } + else + { + this->new_def = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 8: // solution_equilibria + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for solution_equilibria.", PHRQ_io::OT_CONTINUE); + } + else + { + this->solution_equilibria = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 9: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for n_solution.", PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 10: // total_moles + if (!(parser.get_iss() >> this->total_moles)) + { + this->total_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_moles.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 11: // temperature + if (!(parser.get_iss() >> this->temperature)) + { + this->temperature = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 12: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for GasPhase totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 12; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Type not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_p_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total_p not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (volume_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Volume not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxGasPhase::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + class phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(this->gas_comps[i].Get_phase_name().c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, this->gas_comps[i].Get_moles()); + } + else + { + assert(false); + } + } + return; +} +LDBLE cxxGasPhase::Calc_total_moles(void)const +{ + LDBLE tot = 0.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + tot += gas_comps[i].Get_moles(); + } + return tot; +} +void cxxGasPhase::Delete_component(const std::string comp_name) +{ + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name.c_str()) == 0) + { + this->gas_comps.erase(this->gas_comps.begin() + i); // To delete the ith element + break; + } + } +} +void cxxGasPhase::Set_component_moles(const std::string comp_name, const double moles) +{ + if (moles < 0.0) + { + this->Delete_component(comp_name); + } + else + { + cxxGasComp* ptr = this->Find_comp(comp_name.c_str()); + if (ptr != NULL) + { + ptr->Set_moles(moles); + } + else + { + cxxGasComp temp_comp; + temp_comp.Set_phase_name(comp_name); + temp_comp.Set_moles(moles); + this->gas_comps.push_back(temp_comp); + } + } +} +double cxxGasPhase::Get_component_moles(const std::string comp_name) +{ + double moles = -1.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name.c_str()) == 0) + { + moles = this->gas_comps[i].Get_moles(); + break; + } + } + return moles; +} +double cxxGasPhase::Get_component_p(const std::string comp_name) +{ + double p = -1.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name.c_str()) == 0) + { + p = this->gas_comps[i].Get_p(); + break; + } + } + return p; +} +double cxxGasPhase::Get_component_phi(const std::string comp_name) +{ + double phi = -1.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name.c_str()) == 0) + { + phi = this->gas_comps[i].Get_phi(); + break; + } + } + return phi; +} +double cxxGasPhase::Get_component_f(const std::string comp_name) +{ + double f = -1.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name.c_str()) == 0) + { + f = this->gas_comps[i].Get_f(); + break; + } + } + return f; +} + +cxxGasComp * +cxxGasPhase::Find_comp(const char * comp_name) +{ + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name) == 0) + { + return &(this->gas_comps[i]); + } + } + return NULL; +} +void +cxxGasPhase::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + ints.push_back((this->type == cxxGasPhase::GP_PRESSURE) ? 0 : 1); + doubles.push_back(this->total_p); + doubles.push_back(this->volume); + ints.push_back((int) this->gas_comps.size()); + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + this->gas_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back(this->new_def ? 1 : 0); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back(this->n_solution); + doubles.push_back(this->temperature); + doubles.push_back(this->total_moles); + doubles.push_back(this->v_m); + ints.push_back(this->pr_in ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + +} + +void +cxxGasPhase::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->type = (ints[ii++] == 0) ? cxxGasPhase::GP_PRESSURE : cxxGasPhase::GP_VOLUME; + this->total_p = doubles[dd++]; + this->volume = doubles[dd++]; + int count = ints[ii++]; + this->gas_comps.clear(); + for (int i = 0; i < count; i++) + { + cxxGasComp gc; + gc.Deserialize(dictionary, ints, doubles, ii, dd); + this->gas_comps.push_back(gc); + } + this->new_def = (ints[ii++] != 0) ? 1 : 0; + this->solution_equilibria = (ints[ii++] != 0) ? 1 : 0; + this->n_solution = ints[ii++]; + this->temperature = doubles[dd++]; + this->total_moles = doubles[dd++]; + this->v_m = doubles[dd++]; + this->pr_in = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("type"), //0 + std::vector< std::string >::value_type("total_p"), //1 + std::vector< std::string >::value_type("volume"), //2 + std::vector< std::string >::value_type("v_m"), //3 + std::vector< std::string >::value_type("component"), //4 + std::vector< std::string >::value_type("pressure"), //5 + std::vector< std::string >::value_type("pr_in"), //6 + std::vector< std::string >::value_type("new_def"), //7 + std::vector< std::string >::value_type("solution_equilibria"), //8 + std::vector< std::string >::value_type("n_solution"), //9 + std::vector< std::string >::value_type("total_moles"), //10 + std::vector< std::string >::value_type("temperature"), //11 + std::vector< std::string >::value_type("totals") //12 +}; +const std::vector< std::string > cxxGasPhase::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/GasPhase.h b/phreeqcpp/GasPhase.h new file mode 100644 index 00000000..1798feb1 --- /dev/null +++ b/phreeqcpp/GasPhase.h @@ -0,0 +1,103 @@ +#if !defined(GASPHASE_H_INCLUDED) +#define GASPHASE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NumKeyword.h" +#include "NameDouble.h" +#include "GasComp.h" +class cxxMix; + +class cxxGasPhase:public cxxNumKeyword +{ + + public: + cxxGasPhase(PHRQ_io * io=NULL); + cxxGasPhase(std::map < int, cxxGasPhase > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io=NULL); + ~cxxGasPhase(); + + enum GP_TYPE + { + GP_PRESSURE = 0, + GP_VOLUME = 1, + GP_UNKNOWN = 2 + }; + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + void totalize(Phreeqc * phreeqc_ptr); + + const cxxNameDouble & Get_totals() const + { + return this->totals; + }; + std::vector & Get_gas_comps(void) {return gas_comps;}; + const std::vector & Get_gas_comps(void)const {return gas_comps;}; + void Set_gas_comps(const std::vector v) {gas_comps = v;}; + + GP_TYPE Get_type(void) const {return type;}; + void Set_type(GP_TYPE t) {type = t;}; + LDBLE Get_total_p(void) const {return total_p;}; + void Set_total_p(LDBLE t) {total_p = t;}; + LDBLE Get_volume(void) const {return volume;}; + void Set_volume(LDBLE v) {volume = v;}; + LDBLE Get_v_m(void) const {return v_m;}; + void Set_v_m(LDBLE v) {v_m = v;}; + bool Get_pr_in(void) const {return pr_in;}; + void Set_pr_in(bool tf) {pr_in = tf;}; + cxxNameDouble & Get_totals(void) {return totals;}; + + bool Get_new_def(void) const {return this->new_def;}; + void Set_new_def(bool tf) {this->new_def = tf;}; + bool Get_solution_equilibria(void) const {return this->solution_equilibria;}; + void Set_solution_equilibria(bool tf) {this->solution_equilibria = tf;}; + int Get_n_solution(void) const {return this->n_solution;}; + void Set_n_solution(int i) {this->n_solution = i;}; + LDBLE Get_total_moles(void)const {return total_moles;}; + void Set_total_moles(LDBLE t) {total_moles = t;}; + LDBLE Get_temperature(void)const {return temperature;}; + void Set_temperature(LDBLE t) {temperature = t;}; + LDBLE Calc_total_moles(void)const; + cxxGasComp *Find_comp(const char * comp_name); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + void Delete_component(const std::string comp_name); + void Set_component_moles(const std::string comp_name, const double moles); + double Get_component_moles(const std::string comp_name); + double Get_component_p(const std::string comp_name); + double Get_component_phi(const std::string comp_name); + double Get_component_f(const std::string comp_name); +protected: + void add(const cxxGasPhase & addee, LDBLE extensive); + +protected: + // candidate variables for GAS_PHASE_MODIFY + GP_TYPE type; + LDBLE total_p; + LDBLE volume; + std::vector gas_comps; + + // GAS_PHASE_MODIFY with new_def=true variables + bool new_def; + bool solution_equilibria; + int n_solution; + LDBLE temperature; + + // internal variables + LDBLE total_moles; + LDBLE v_m; + bool pr_in; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(GASPHASE_H_INCLUDED) diff --git a/phreeqcpp/ISolution.cxx b/phreeqcpp/ISolution.cxx new file mode 100644 index 00000000..a7728789 --- /dev/null +++ b/phreeqcpp/ISolution.cxx @@ -0,0 +1,40 @@ +// ISolution.cxx: implementation of the cxxSolutionxx class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "ISolution.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxISolution::cxxISolution(PHRQ_io *io) +: +units("mMol/kgw") +{ + default_pe = "pe"; + CReaction temp_pe_reactions; + pe_reactions[default_pe] = temp_pe_reactions; + this->calc_density = false; + +} +cxxISolution::~cxxISolution() +{ +} diff --git a/phreeqcpp/ISolution.h b/phreeqcpp/ISolution.h new file mode 100644 index 00000000..10fb7160 --- /dev/null +++ b/phreeqcpp/ISolution.h @@ -0,0 +1,53 @@ +#if !defined(ISOLUTION_H_INCLUDED) +#define ISOLUTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include // std::set +#include +#include +#include + +#include "ISolutionComp.h" +#include "PHRQ_base.h" +#include "NameDouble.h" +#include "global_structures.h" +class cxxISolution: public PHRQ_base +{ + + public: + cxxISolution(PHRQ_io *io=NULL); + cxxISolution(const cxxISolution *is); + virtual ~cxxISolution(); + std::string Get_units() const {return units;} + void Set_units(std::string l_units) {units = l_units;} + void Set_units(const char * l_units) + { + if (l_units != NULL) + this->units = std::string(l_units); + else + this->units.clear(); + } + const char * Get_default_pe() const {return default_pe;} + void Set_default_pe(const char * pe) {default_pe = pe;} + bool Get_calc_density(void) {return this->calc_density;} + void Set_calc_density(bool calc) {this->calc_density = calc;} + std::map < std::string, cxxISolutionComp > &Get_comps(void) {return this->comps;} + const std::map < std::string, cxxISolutionComp > &Get_comps(void)const {return this->comps;} + void Set_comps(std::map < std::string, cxxISolutionComp > &c) {this->comps = c;} + std::map& Get_pe_reactions(void) { return this->pe_reactions; } + void Set_pe_reactions(std::map < std::string, CReaction >& pe) { this->pe_reactions = pe; } + + protected: + friend class cxxISolutionComp; // for this->pe access + std::string units; + bool calc_density; + std::map < std::string, cxxISolutionComp > comps; + std::map pe_reactions; + const char * default_pe; +}; + +#endif // !defined(ISOLUTION_H_INCLUDED) diff --git a/phreeqcpp/ISolutionComp.cxx b/phreeqcpp/ISolutionComp.cxx new file mode 100644 index 00000000..d9fd455f --- /dev/null +++ b/phreeqcpp/ISolutionComp.cxx @@ -0,0 +1,202 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include + +#include "Utils.h" +#include "ISolutionComp.h" +#include "Parser.h" +#include "Solution.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +cxxISolutionComp::cxxISolutionComp(PHRQ_io *io): +PHRQ_base(io), +moles(0.0), +input_conc(0.0), +phase_si(0.0), +gfw(0.0) +{ +} +cxxISolutionComp::~cxxISolutionComp(void) +{ +} + +/* ---------------------------------------------------------------------- */ +CParser::STATUS_TYPE cxxISolutionComp:: +read(const char *line_in, cxxSolution *solution_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Remove space between "kg" and "solution" or "water" in units + */ + std::string line = line_in; + Utilities::replace("Kg", "kg", line); + Utilities::replace("KG", "kg", line); + while (Utilities::replace("kg ", "kg", line) == TRUE); +/* + * Read master species list for mass balance equation + */ + std::string master_list; + std::string token; + std::string::iterator b = line.begin(); + std::string::iterator e = line.end(); + { + int j; + while (((j = CParser::copy_token(token, b, e)) == CParser::TT_UPPER) || + (token[0] == '[') || + (Utilities::strcmp_nocase(token.c_str(), "ph") == 0) || + (Utilities::strcmp_nocase(token.c_str(), "pe") == 0)) + { + Utilities::replace("(+", "(", token); + if (master_list.size() > 0) + master_list.append(" "); + master_list.append(token); + } + } + if (master_list.size() == 0) + { + error_msg + ("No element or master species given for concentration input.", + PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + this->Set_description(master_list.c_str()); +/* + * Determine if reading alkalinity, allow equivalents for units + */ + bool alk; + Utilities::str_tolower(master_list); + if (strstr(master_list.c_str(), "alk") == master_list.c_str()) + { + alk = true; + } + else + { + alk = false; + } +/* + * Read concentration + */ + { + LDBLE dummy; + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + if (j == 0) + { + std::ostringstream errstr; + errstr << "Concentration data error for " << master_list << " in solution input."; + error_msg(errstr.str().c_str(), PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + else + { + this->Set_input_conc(dummy); + } + if ((j = CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } +/* + * Read optional data + */ + std::string token1 = token; + +/* + * Check for units info + */ + CParser parser(this->io); + if (solution_ptr->Get_initial_data() == NULL) + { + error_msg("Initial_data instance not defined in cxxISolutionComp::read", 1); + } + if (parser.check_units(token1, alk, false, solution_ptr->Get_initial_data()->Get_units().c_str(), false) == CParser::PARSER_OK) + { + if (parser.check_units(token1, alk, false, solution_ptr->Get_initial_data()->Get_units().c_str(), true) == CParser::PARSER_OK) + { + this->units = token1; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else + { + return (CParser::PARSER_ERROR); + } + } +/* + * Check for "as" followed by formula to be used for gfw + */ + token1 = token; + Utilities::str_tolower(token1); + if (strcmp(token1.c_str(), "as") == 0) + { + CParser::copy_token(token, b, e); + this->as = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); +/* + * Check for "gfw" followed by gram formula weight + */ + } + else if (strcmp(token1.c_str(), "gfw") == 0 || strcmp(token1.c_str(), "gfm") == 0) + { + if (CParser::copy_token(token, b, e) != DIGIT) + { + error_msg("Expecting gram formula weight.", PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, &this->gfw); + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + } +/* + * Check for redox couple for pe + */ + if (Utilities::strcmp_nocase(token.c_str(), "pe") == 0) + { + this->pe_reaction = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else if (strstr(token.c_str(), "/") != NULL) + { + if (parser.parse_couple(token) == CParser::PARSER_OK) + { + this->pe_reaction = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else + { + return (CParser::PARSER_ERROR); + } + } +/* + * Must have phase + */ + this->equation_name = token; + if (CParser::copy_token(token, b, e) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); +/* + * Check for saturation index + */ + { + int j = sscanf(token.c_str(), SCANFORMAT, + &(this->phase_si)); + if (j != 1) + { + error_msg("Expected saturation index.", PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + } + return (CParser::PARSER_OK); + +} diff --git a/phreeqcpp/ISolutionComp.h b/phreeqcpp/ISolutionComp.h new file mode 100644 index 00000000..ef88e620 --- /dev/null +++ b/phreeqcpp/ISolutionComp.h @@ -0,0 +1,138 @@ +#if !defined(ISOLUTIONCOMP_H_INCLUDED) +#define ISOLUTIONCOMP_H_INCLUDED + +#include // ::strcmp +#include +#include // std::map +#include +#include +#include "phrqtype.h" // LDBLE +#include "Parser.h" // CParser +#include "PHRQ_base.h" // PHRQ_base + +// forward declarations +class cxxSolution; +class cxxISolution; // reqd for read and dump_xml +class PHRQ_io; + +class cxxISolutionComp: public PHRQ_base +{ + public: + cxxISolutionComp(PHRQ_io *io=NULL); + virtual ~cxxISolutionComp(void); + + public: + + CParser::STATUS_TYPE read(const char *line, cxxSolution *solution_ptr); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + const std::string &Get_description() const + { + return this->description; + } + void Set_description(const char *l_description) + { + if (l_description != NULL) + this->description = std::string(l_description); + else + this->description.clear(); + } + + LDBLE Get_moles() const + { + return this->moles; + } + void Set_moles(LDBLE l_moles) + { + this->moles = l_moles; + } + + LDBLE Get_input_conc() const + { + return this->input_conc; + } + void Set_input_conc(LDBLE l_input_conc) + { + this->input_conc = l_input_conc; + } + + std::string Get_units()const + { + return this->units; + } + void Set_units(const char *l_units) + { + if (l_units != NULL) + this->units = std::string(l_units); + else + this->units.clear(); + } + + const std::string &Get_equation_name() const + { + return this->equation_name; + } + void Set_equation_name(const char *l_equation_name) + { + if (l_equation_name != NULL) + this->equation_name = std::string(l_equation_name); + else + this->equation_name.clear(); + + } + + LDBLE Get_phase_si() const + { + return this->phase_si; + } + void Set_phase_si(int l_phase_si) + { + this->phase_si = l_phase_si; + } + std::string Get_pe_reaction() const + { + return this->pe_reaction; + } + void Set_pe_reaction(const std::string & pe_r) + { + this->pe_reaction = pe_r; + } + const std::string &Get_as() const + { + return this->as; + } + void Set_as(const char *l_as) + { + if (l_as != NULL) + this->as = std::string(l_as); + else + this->as.clear(); + } + LDBLE Get_gfw() const + { + return this->gfw; + }; + void Set_gfw(LDBLE l_gfw) + { + this->gfw = l_gfw; + } + + bool operator<(const cxxISolutionComp & conc) const + { + return ::strcmp(this->description.c_str(), conc.description.c_str()) < 0; + } + + protected: + std::string description; + LDBLE moles; + LDBLE input_conc; + std::string units; + std::string equation_name; + LDBLE phase_si; + std::string pe_reaction; + std::string as; + LDBLE gfw; +}; + +#endif // ISOLUTIONCOMP_H_INCLUDED diff --git a/phreeqcpp/KineticsComp.cxx b/phreeqcpp/KineticsComp.cxx new file mode 100644 index 00000000..b4d29f17 --- /dev/null +++ b/phreeqcpp/KineticsComp.cxx @@ -0,0 +1,318 @@ +// KineticsComp.cxx: implementation of the cxxKineticsComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "KineticsComp.h" +//#include "Dictionary.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxKineticsComp::cxxKineticsComp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxKineticsComp + // +{ + tol = 1e-8; + m = -1; + m0 = -1; + moles = 0.0; + initial_moles = 0; + namecoef.type = cxxNameDouble::ND_NAME_COEF; +} +cxxKineticsComp::~cxxKineticsComp() +{ +} + +void +cxxKineticsComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics_Comp element and attributes + s_oss << indent1 << "# KINETICS_MODIFY candidate identifiers #\n"; + s_oss << indent1 << "-tol " << this->tol << "\n"; + s_oss << indent1 << "-m " << this->m << "\n"; + s_oss << indent1 << "-m0 " << this->m0 << "\n"; + + // namecoef + s_oss << indent1; + s_oss << "-namecoef" << "\n"; + this->namecoef.dump_raw(s_oss, indent + 2); + + // d_params + s_oss << indent1; + s_oss << "-d_params" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = d_params.begin(); + it != d_params.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1 << "# KineticsComp workspace variables #\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; +} + +void +cxxKineticsComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + std::vector < LDBLE > temp_d_params; + opt_save = CParser::OPT_ERROR; + bool tol_defined(false); + bool m_defined(false); + bool m0_defined(false); + bool d_params_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Kinetics for more processing + break; + + case 0: // rate_name not used + parser.warning_msg("Rate_name ignored. Define in -comp."); + break; + + case 1: // tol + if (!(parser.get_iss() >> this->tol)) + { + this->tol = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for tol.", + PHRQ_io::OT_CONTINUE); + } + tol_defined = true; + break; + + case 2: // m + if (!(parser.get_iss() >> this->m)) + { + this->m = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for m.", + PHRQ_io::OT_CONTINUE); + } + m_defined = true; + break; + + case 3: // m0 + if (!(parser.get_iss() >> this->m0)) + { + this->m0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for m0.", + PHRQ_io::OT_CONTINUE); + } + m0_defined = true; + break; + + + case 4: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // namecoef + if (this->namecoef.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for namecoef.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 5; + break; + + case 6: // d_params + while (parser.copy_token(token, next_char) == CParser::TT_DIGIT) + { + double dd; + (void)sscanf(token.c_str(), "%lf", &dd); + temp_d_params.push_back((LDBLE) dd); + d_params_defined = true; + } + opt_save = 6; + break; + case 7: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + + if (d_params_defined) + { + this->d_params = temp_d_params; + } + if (check) + { + // members that must be defined + if (tol_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Tol not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + if (m_defined == false) + { + parser.incr_input_error(); + parser.error_msg("M not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + if (m0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("M0 not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxKineticsComp::add(const cxxKineticsComp & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee.rate_name.size() == 0) + return; + // this and addee must have same name + // otherwise generate a new KineticsComp with multiply + if (this->rate_name.size() == 0 && addee.rate_name.size() == 0) + { + return; + } + assert(this->rate_name == addee.rate_name); + this->m += addee.m * extensive; + this->m0 += addee.m0 * extensive; + this->moles += addee.moles * extensive; +} + +void +cxxKineticsComp::multiply(LDBLE extensive) +{ + this->m *= extensive; + this->m0 *= extensive; + this->moles *= extensive; +} +void +cxxKineticsComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->rate_name)); + this->namecoef.Serialize(dictionary, ints, doubles); + doubles.push_back(this->tol); + doubles.push_back(this->m); + doubles.push_back(this->m0); + ints.push_back((int) this->d_params.size()); + for (size_t i = 0; i < this->d_params.size(); i++) + { + doubles.push_back(d_params[i]); + } + doubles.push_back(this->moles); + doubles.push_back(this->initial_moles); + this->moles_of_reaction.Serialize(dictionary, ints, doubles); +} +void +cxxKineticsComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->namecoef.Deserialize(dictionary, ints, doubles, ii, dd); + this->tol = doubles[dd++]; + this->m = doubles[dd++]; + this->m0 = doubles[dd++]; + int n = ints[ii++]; + this->d_params.clear(); + for (int j = 0; j < n; j++) + { + this->d_params.push_back(doubles[dd++]); + } + this->moles = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->moles_of_reaction.Deserialize(dictionary, ints, doubles, ii, dd); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("rate_name_not_used"), // 0 + std::vector< std::string >::value_type("tol"), // 1 + std::vector< std::string >::value_type("m"), // 2 + std::vector< std::string >::value_type("m0"), // 3 + std::vector< std::string >::value_type("moles"), // 4 + std::vector< std::string >::value_type("namecoef"), // 5 + std::vector< std::string >::value_type("d_params"), // 6 + std::vector< std::string >::value_type("initial_moles") // 7 +}; +const std::vector< std::string > cxxKineticsComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/KineticsComp.h b/phreeqcpp/KineticsComp.h new file mode 100644 index 00000000..eb3d954f --- /dev/null +++ b/phreeqcpp/KineticsComp.h @@ -0,0 +1,81 @@ +#if !defined(KINETICSCOMP_H_INCLUDED) +#define KINETICSCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxKineticsComp: public PHRQ_base +{ + +public: + cxxKineticsComp(PHRQ_io *io=NULL); + cxxKineticsComp(struct kinetics_comp *, PHRQ_io *io=NULL); + virtual ~cxxKineticsComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_rate_name() const + { + return this->rate_name; + } + void Set_rate_name(const char * s) + { + if (s != NULL) + this->rate_name = std::string(s); + else + this->rate_name.clear(); + } + + cxxNameDouble &Get_namecoef(void) {return namecoef;} + const cxxNameDouble &Get_namecoef(void)const {return namecoef;} + void Set_namecoef(const cxxNameDouble nd) {namecoef = nd;} + LDBLE Get_tol(void) const {return tol;} + void Set_tol(LDBLE t) {tol = t;} + LDBLE Get_m(void) const {return m;} + void Set_m(LDBLE t) {m = t;} + LDBLE Get_m0(void) const {return m0;} + void Set_m0(LDBLE t) {m0 = t;} + LDBLE Get_moles(void) const {return moles;} + void Set_moles(LDBLE t) {moles = t;} + LDBLE Get_initial_moles(void) const {return initial_moles;} + void Set_initial_moles(LDBLE t) {initial_moles = t;} + std::vector < LDBLE > &Get_d_params(void) {return d_params;}; + const std::vector < LDBLE > &Get_d_params(void)const {return d_params;}; + std::vector < std::string > &Get_c_params(void) {return c_params;}; + cxxNameDouble &Get_moles_of_reaction(void) {return moles_of_reaction;} + const cxxNameDouble &Get_moles_of_reaction(void)const {return moles_of_reaction;} + void Set_moles_of_reaction(const cxxNameDouble nd) {moles_of_reaction = nd;} + + void add(const cxxKineticsComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + std::string rate_name; + // KINETICS_MODIFY candidates + cxxNameDouble namecoef; //stoichiometry of reaction + LDBLE tol; + LDBLE m; + LDBLE m0; + std::vector< LDBLE > d_params; + std::vector< std::string > c_params; // Not used + // kinetics workspace variables + LDBLE moles; + LDBLE initial_moles; + cxxNameDouble moles_of_reaction; + const static std::vector < std::string > vopts; + public: + +}; + +#endif // !defined(KINETICSCOMP_H_INCLUDED) diff --git a/phreeqcpp/Makefile b/phreeqcpp/Makefile new file mode 100644 index 00000000..f1a978bb --- /dev/null +++ b/phreeqcpp/Makefile @@ -0,0 +1,639 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# src/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +pkgdatadir = $(datadir)/phreeqc +pkgincludedir = $(includedir)/phreeqc +pkglibdir = $(libdir)/phreeqc +pkglibexecdir = $(libexecdir)/phreeqc +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = phreeqc$(EXEEXT) +am__append_1 = \ + cl1mp.cpp + +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__phreeqc_SOURCES_DIST = class_main.cpp cxxKinetics.cxx \ + cxxKinetics.h cxxMix.cxx cxxMix.h dumper.cpp dumper.h \ + Exchange.cxx Exchange.h ExchComp.cxx ExchComp.h GasComp.cxx \ + GasComp.h GasPhase.cxx GasPhase.h ISolution.cxx ISolution.h \ + ISolutionComp.cxx ISolutionComp.h Keywords.cpp Keywords.h \ + KineticsComp.cxx KineticsComp.h NameDouble.cxx NameDouble.h \ + NumKeyword.cxx NumKeyword.h Parser.cxx Parser.h PBasic.cpp \ + PBasic.h Phreeqc.cpp Phreeqc.h PHRQ_base.cxx PHRQ_base.h \ + PHRQ_io.cpp PHRQ_io.h PPassemblage.cxx PPassemblage.h \ + PPassemblageComp.cxx PPassemblageComp.h Pressure.cxx \ + Pressure.h Reaction.cxx Reaction.h ReadClass.cxx runner.cpp \ + runner.h SelectedOutput.cpp SelectedOutput.h Solution.cxx \ + Solution.h SolutionIsotope.cxx SolutionIsotope.h \ + SSassemblage.cxx SSassemblage.h SS.cxx SS.h SScomp.cxx \ + SScomp.h StorageBin.cxx StorageBin.h StorageBinList.cpp \ + StorageBinList.h Surface.cxx Surface.h SurfaceCharge.cxx \ + SurfaceCharge.h SurfaceComp.cxx SurfaceComp.h System.cxx \ + System.h Temperature.cxx Temperature.h Use.cpp Use.h \ + UserPunch.cpp UserPunch.h Utils.cxx Utils.h advection.cpp \ + basicsubs.cpp cl1.cpp cvdense.cpp cvdense.h cvode.cpp cvode.h \ + dense.cpp dense.h dw.cpp gases.cpp global_structures.h \ + input.cpp integrate.cpp inverse.cpp isotopes.cpp kinetics.cpp \ + mainsubs.cpp model.cpp NA.h nvector.cpp nvector.h \ + nvector_serial.cpp nvector_serial.h parse.cpp phqalloc.cpp \ + phqalloc.h PHRQ_io_output.cpp phrqtype.h pitzer.cpp \ + pitzer_structures.cpp prep.cpp print.cpp read.cpp readtr.cpp \ + sit.cpp smalldense.cpp smalldense.h spread.cpp step.cpp \ + structures.cpp sundialsmath.cpp sundialsmath.h sundialstypes.h \ + tally.cpp tidy.cpp transport.cpp utilities.cpp cl1mp.cpp +am__objects_1 = cl1mp.$(OBJEXT) +am_phreeqc_OBJECTS = class_main.$(OBJEXT) cxxKinetics.$(OBJEXT) \ + cxxMix.$(OBJEXT) dumper.$(OBJEXT) Exchange.$(OBJEXT) \ + ExchComp.$(OBJEXT) GasComp.$(OBJEXT) GasPhase.$(OBJEXT) \ + ISolution.$(OBJEXT) ISolutionComp.$(OBJEXT) Keywords.$(OBJEXT) \ + KineticsComp.$(OBJEXT) NameDouble.$(OBJEXT) \ + NumKeyword.$(OBJEXT) Parser.$(OBJEXT) PBasic.$(OBJEXT) \ + Phreeqc.$(OBJEXT) PHRQ_base.$(OBJEXT) PHRQ_io.$(OBJEXT) \ + PPassemblage.$(OBJEXT) PPassemblageComp.$(OBJEXT) \ + Pressure.$(OBJEXT) Reaction.$(OBJEXT) ReadClass.$(OBJEXT) \ + runner.$(OBJEXT) SelectedOutput.$(OBJEXT) Solution.$(OBJEXT) \ + SolutionIsotope.$(OBJEXT) SSassemblage.$(OBJEXT) SS.$(OBJEXT) \ + SScomp.$(OBJEXT) StorageBin.$(OBJEXT) StorageBinList.$(OBJEXT) \ + Surface.$(OBJEXT) SurfaceCharge.$(OBJEXT) \ + SurfaceComp.$(OBJEXT) System.$(OBJEXT) Temperature.$(OBJEXT) \ + Use.$(OBJEXT) UserPunch.$(OBJEXT) Utils.$(OBJEXT) \ + advection.$(OBJEXT) basicsubs.$(OBJEXT) cl1.$(OBJEXT) \ + cvdense.$(OBJEXT) cvode.$(OBJEXT) dense.$(OBJEXT) dw.$(OBJEXT) \ + gases.$(OBJEXT) input.$(OBJEXT) integrate.$(OBJEXT) \ + inverse.$(OBJEXT) isotopes.$(OBJEXT) kinetics.$(OBJEXT) \ + mainsubs.$(OBJEXT) model.$(OBJEXT) nvector.$(OBJEXT) \ + nvector_serial.$(OBJEXT) parse.$(OBJEXT) phqalloc.$(OBJEXT) \ + PHRQ_io_output.$(OBJEXT) pitzer.$(OBJEXT) \ + pitzer_structures.$(OBJEXT) prep.$(OBJEXT) print.$(OBJEXT) \ + read.$(OBJEXT) readtr.$(OBJEXT) sit.$(OBJEXT) \ + smalldense.$(OBJEXT) spread.$(OBJEXT) step.$(OBJEXT) \ + structures.$(OBJEXT) sundialsmath.$(OBJEXT) tally.$(OBJEXT) \ + tidy.$(OBJEXT) transport.$(OBJEXT) utilities.$(OBJEXT) \ + $(am__objects_1) +phreeqc_OBJECTS = $(am_phreeqc_OBJECTS) +phreeqc_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +phreeqc_DEPENDENCIES = $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(phreeqc_SOURCES) +DIST_SOURCES = $(am__phreeqc_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run aclocal-1.11 +AMTAR = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run tar +AUTOCONF = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run autoconf +AUTOHEADER = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run autoheader +AUTOMAKE = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run automake-1.11 +AWK = gawk +CPPFLAGS = +CXX = g++ +CXXCPP = g++ -E +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +DEFS = -DPACKAGE_NAME=\"phreeqc\" -DPACKAGE_TARNAME=\"phreeqc\" -DPACKAGE_VERSION=\"3.0.0-7109\" -DPACKAGE_STRING=\"phreeqc\ 3.0.0-7109\" -DPACKAGE_BUGREPORT=\"dlpark@usgs.gov\" -DPACKAGE=\"phreeqc\" -DVERSION=\"3.0.0-7109\" -DNDEBUG=1 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FLOAT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STDDEF_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_STDLIB_H=1 -DHAVE_REALLOC=1 -DHAVE_MEMMOVE=1 -DHAVE_MEMSET=1 -DHAVE_STRCHR=1 -DHAVE_STRCSPN=1 -DHAVE_STRTOL=1 -DHAVE_FLOOR=1 -DHAVE_POW=1 -DHAVE_SQRT=1 -DHAVE_ISFINITE=/\*\*/ -DHAVE_FINITE=/\*\*/ -DHAVE_ISNAN=1 -DINVERSE_CL1MP=1 +DEPDIR = .deps +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBGMP = -lgmp +LIBOBJS = +LIBS = +LTLIBOBJS = +MAKEINFO = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = phreeqc +PACKAGE_BUGREPORT = dlpark@usgs.gov +PACKAGE_NAME = phreeqc +PACKAGE_STRING = phreeqc 3.0.0-7109 +PACKAGE_TARNAME = phreeqc +PACKAGE_VERSION = 3.0.0-7109 +PATH_SEPARATOR = : +POW_LIB = +SET_MAKE = +SHELL = /bin/sh +STRIP = +VERSION = 3.0.0-7109 +abs_builddir = /raid/home/dlpark/programs/phreeqc3-trunk/src +abs_srcdir = /raid/home/dlpark/programs/phreeqc3-trunk/src +abs_top_builddir = /raid/home/dlpark/programs/phreeqc3-trunk +abs_top_srcdir = /raid/home/dlpark/programs/phreeqc3-trunk +ac_ct_CXX = g++ +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_build_prefix = ../ +top_builddir = .. +top_srcdir = .. +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/phreeqc + +# sources for phreeqc +phreeqc_SOURCES = class_main.cpp cxxKinetics.cxx cxxKinetics.h \ + cxxMix.cxx cxxMix.h dumper.cpp dumper.h Exchange.cxx \ + Exchange.h ExchComp.cxx ExchComp.h GasComp.cxx GasComp.h \ + GasPhase.cxx GasPhase.h ISolution.cxx ISolution.h \ + ISolutionComp.cxx ISolutionComp.h Keywords.cpp Keywords.h \ + KineticsComp.cxx KineticsComp.h NameDouble.cxx NameDouble.h \ + NumKeyword.cxx NumKeyword.h Parser.cxx Parser.h PBasic.cpp \ + PBasic.h Phreeqc.cpp Phreeqc.h PHRQ_base.cxx PHRQ_base.h \ + PHRQ_io.cpp PHRQ_io.h PPassemblage.cxx PPassemblage.h \ + PPassemblageComp.cxx PPassemblageComp.h Pressure.cxx \ + Pressure.h Reaction.cxx Reaction.h ReadClass.cxx runner.cpp \ + runner.h SelectedOutput.cpp SelectedOutput.h Solution.cxx \ + Solution.h SolutionIsotope.cxx SolutionIsotope.h \ + SSassemblage.cxx SSassemblage.h SS.cxx SS.h SScomp.cxx \ + SScomp.h StorageBin.cxx StorageBin.h StorageBinList.cpp \ + StorageBinList.h Surface.cxx Surface.h SurfaceCharge.cxx \ + SurfaceCharge.h SurfaceComp.cxx SurfaceComp.h System.cxx \ + System.h Temperature.cxx Temperature.h Use.cpp Use.h \ + UserPunch.cpp UserPunch.h Utils.cxx Utils.h advection.cpp \ + basicsubs.cpp cl1.cpp cvdense.cpp cvdense.h cvode.cpp cvode.h \ + dense.cpp dense.h dw.cpp gases.cpp global_structures.h \ + input.cpp integrate.cpp inverse.cpp isotopes.cpp kinetics.cpp \ + mainsubs.cpp model.cpp NA.h nvector.cpp nvector.h \ + nvector_serial.cpp nvector_serial.h parse.cpp phqalloc.cpp \ + phqalloc.h PHRQ_io_output.cpp phrqtype.h pitzer.cpp \ + pitzer_structures.cpp prep.cpp print.cpp read.cpp readtr.cpp \ + sit.cpp smalldense.cpp smalldense.h spread.cpp step.cpp \ + structures.cpp sundialsmath.cpp sundialsmath.h sundialstypes.h \ + tally.cpp tidy.cpp transport.cpp utilities.cpp $(am__append_1) +LDADD = $(LIBGMP) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .cxx .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +phreeqc$(EXEEXT): $(phreeqc_OBJECTS) $(phreeqc_DEPENDENCIES) + @rm -f phreeqc$(EXEEXT) + $(CXXLINK) $(phreeqc_OBJECTS) $(phreeqc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/ExchComp.Po +include ./$(DEPDIR)/Exchange.Po +include ./$(DEPDIR)/GasComp.Po +include ./$(DEPDIR)/GasPhase.Po +include ./$(DEPDIR)/ISolution.Po +include ./$(DEPDIR)/ISolutionComp.Po +include ./$(DEPDIR)/Keywords.Po +include ./$(DEPDIR)/KineticsComp.Po +include ./$(DEPDIR)/NameDouble.Po +include ./$(DEPDIR)/NumKeyword.Po +include ./$(DEPDIR)/PBasic.Po +include ./$(DEPDIR)/PHRQ_base.Po +include ./$(DEPDIR)/PHRQ_io.Po +include ./$(DEPDIR)/PHRQ_io_output.Po +include ./$(DEPDIR)/PPassemblage.Po +include ./$(DEPDIR)/PPassemblageComp.Po +include ./$(DEPDIR)/Parser.Po +include ./$(DEPDIR)/Phreeqc.Po +include ./$(DEPDIR)/Pressure.Po +include ./$(DEPDIR)/Reaction.Po +include ./$(DEPDIR)/ReadClass.Po +include ./$(DEPDIR)/SS.Po +include ./$(DEPDIR)/SSassemblage.Po +include ./$(DEPDIR)/SScomp.Po +include ./$(DEPDIR)/SelectedOutput.Po +include ./$(DEPDIR)/Solution.Po +include ./$(DEPDIR)/SolutionIsotope.Po +include ./$(DEPDIR)/StorageBin.Po +include ./$(DEPDIR)/StorageBinList.Po +include ./$(DEPDIR)/Surface.Po +include ./$(DEPDIR)/SurfaceCharge.Po +include ./$(DEPDIR)/SurfaceComp.Po +include ./$(DEPDIR)/System.Po +include ./$(DEPDIR)/Temperature.Po +include ./$(DEPDIR)/Use.Po +include ./$(DEPDIR)/UserPunch.Po +include ./$(DEPDIR)/Utils.Po +include ./$(DEPDIR)/advection.Po +include ./$(DEPDIR)/basicsubs.Po +include ./$(DEPDIR)/cl1.Po +include ./$(DEPDIR)/cl1mp.Po +include ./$(DEPDIR)/class_main.Po +include ./$(DEPDIR)/cvdense.Po +include ./$(DEPDIR)/cvode.Po +include ./$(DEPDIR)/cxxKinetics.Po +include ./$(DEPDIR)/cxxMix.Po +include ./$(DEPDIR)/dense.Po +include ./$(DEPDIR)/dumper.Po +include ./$(DEPDIR)/dw.Po +include ./$(DEPDIR)/gases.Po +include ./$(DEPDIR)/input.Po +include ./$(DEPDIR)/integrate.Po +include ./$(DEPDIR)/inverse.Po +include ./$(DEPDIR)/isotopes.Po +include ./$(DEPDIR)/kinetics.Po +include ./$(DEPDIR)/mainsubs.Po +include ./$(DEPDIR)/model.Po +include ./$(DEPDIR)/nvector.Po +include ./$(DEPDIR)/nvector_serial.Po +include ./$(DEPDIR)/parse.Po +include ./$(DEPDIR)/phqalloc.Po +include ./$(DEPDIR)/pitzer.Po +include ./$(DEPDIR)/pitzer_structures.Po +include ./$(DEPDIR)/prep.Po +include ./$(DEPDIR)/print.Po +include ./$(DEPDIR)/read.Po +include ./$(DEPDIR)/readtr.Po +include ./$(DEPDIR)/runner.Po +include ./$(DEPDIR)/sit.Po +include ./$(DEPDIR)/smalldense.Po +include ./$(DEPDIR)/spread.Po +include ./$(DEPDIR)/step.Po +include ./$(DEPDIR)/structures.Po +include ./$(DEPDIR)/sundialsmath.Po +include ./$(DEPDIR)/tally.Po +include ./$(DEPDIR)/tidy.Po +include ./$(DEPDIR)/transport.Po +include ./$(DEPDIR)/utilities.Po + +.cpp.o: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cxx.o: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/phreeqcpp/Makefile.am b/phreeqcpp/Makefile.am new file mode 100644 index 00000000..a568ea8c --- /dev/null +++ b/phreeqcpp/Makefile.am @@ -0,0 +1,160 @@ +EXTRA_DIST=\ + ChartHandler.cpp\ + ChartHandler.h\ + ChartObject.cpp\ + ChartObject.h\ + CurveObject.cpp\ + CurveObject.h\ + Form1.h\ + Form1.2005.resX\ + Form1.resX\ + ZedGraph.dll + +AM_CPPFLAGS=-I$(top_srcdir)/src -I$(top_srcdir)/src/common -I$(top_srcdir)/src/PhreeqcKeywords + +bin_PROGRAMS = phreeqc + +# sources for phreeqc +phreeqc_SOURCES=\ + advection.cpp\ + basicsubs.cpp\ + cl1.cpp\ + class_main.cpp\ + common/Parser.cxx\ + common/Parser.h\ + common/PHRQ_base.cxx\ + common/PHRQ_base.h\ + common/PHRQ_exports.h\ + common/PHRQ_io.cpp\ + common/PHRQ_io.h\ + common/phrqtype.h\ + common/Utils.cxx\ + common/Utils.h\ + cvdense.cpp\ + cvdense.h\ + cvode.cpp\ + cvode.h\ + cxxKinetics.cxx\ + cxxKinetics.h\ + cxxMix.cxx\ + cxxMix.h\ + dense.cpp\ + dense.h\ + Dictionary.cpp\ + Dictionary.h\ + dumper.cpp\ + dumper.h\ + Exchange.cxx\ + Exchange.h\ + ExchComp.cxx\ + ExchComp.h\ + GasComp.cxx\ + GasComp.h\ + gases.cpp\ + GasPhase.cxx\ + GasPhase.h\ + global_structures.h\ + input.cpp\ + integrate.cpp\ + inverse.cpp\ + ISolution.cxx\ + ISolution.h\ + ISolutionComp.cxx\ + ISolutionComp.h\ + isotopes.cpp\ + kinetics.cpp\ + KineticsComp.cxx\ + KineticsComp.h\ + mainsubs.cpp\ + model.cpp\ + NA.h\ + NameDouble.cxx\ + NameDouble.h\ + NumKeyword.cxx\ + NumKeyword.h\ + nvector.cpp\ + nvector.h\ + nvector_serial.cpp\ + nvector_serial.h\ + parse.cpp\ + PBasic.cpp\ + PBasic.h\ + phqalloc.cpp\ + phqalloc.h\ + Phreeqc.cpp\ + Phreeqc.h\ + PhreeqcKeywords/Keywords.cpp\ + PhreeqcKeywords/Keywords.h\ + PHRQ_io_output.cpp\ + pitzer.cpp\ + pitzer_structures.cpp\ + PPassemblage.cxx\ + PPassemblage.h\ + PPassemblageComp.cxx\ + PPassemblageComp.h\ + prep.cpp\ + Pressure.cxx\ + Pressure.h\ + print.cpp\ + Reaction.cxx\ + Reaction.h\ + read.cpp\ + ReadClass.cxx\ + readtr.cpp\ + runner.cpp\ + runner.h\ + SelectedOutput.cpp\ + SelectedOutput.h\ + Serializer.cxx\ + Serializer.h\ + sit.cpp\ + smalldense.cpp\ + smalldense.h\ + Solution.cxx\ + Solution.h\ + SolutionIsotope.cxx\ + SolutionIsotope.h\ + spread.cpp\ + SS.cxx\ + SS.h\ + SSassemblage.cxx\ + SSassemblage.h\ + SScomp.cxx\ + SScomp.h\ + step.cpp\ + StorageBin.cxx\ + StorageBin.h\ + StorageBinList.cpp\ + StorageBinList.h\ + structures.cpp\ + sundialsmath.cpp\ + sundialsmath.h\ + sundialstypes.h\ + Surface.cxx\ + Surface.h\ + SurfaceCharge.cxx\ + SurfaceCharge.h\ + SurfaceComp.cxx\ + SurfaceComp.h\ + System.cxx\ + System.h\ + tally.cpp\ + Temperature.cxx\ + Temperature.h\ + tidy.cpp\ + transport.cpp\ + Use.cpp\ + Use.h\ + UserPunch.cpp\ + UserPunch.h\ + utilities.cpp + +if BUILD_GMP + +phreeqc_SOURCES+=\ + cl1mp.cpp + +endif + + +LDADD = $(LIBGMP) diff --git a/phreeqcpp/Makefile.old b/phreeqcpp/Makefile.old new file mode 100644 index 00000000..c0b66adb --- /dev/null +++ b/phreeqcpp/Makefile.old @@ -0,0 +1,986 @@ +# Makefile for PHREEQCPP +# +# Generates object files and executables for 2 versions of PHREEQCPP +# Release +# Debug +# +# Serial verisons: Release Debug +# +# Makefile sets CFG variable, cd's to appropriate directory, runs Makefile recursively +# Recursive make sets appropriate compiler, objects, options, libraries, and compiles PHREEQC +# + +PROGRAM = phreeqc + +CFG1 :=`uname` +CFG :=$(shell echo $(CFG1) | sed "s/Linux.*/Linux/") +ifeq ($(CFG), Linux) + SPOOL=>& + SPOOL2= + CONCAT=; +else + SPOOL=> + SPOOL2=2>&1 + CONCAT=& +endif +all: class_release class_debug + +Debug: class_debug +debug: class_debug +Class_debug: class_debug + +Release: class_release +release: class_release +Class_release: class_release + +Debug_64: class_debug_64 +debug_64: class_debug_64 +Class_debug_64: class_debug_64 + +Release_64: class_release_64 +release_64: class_release_64 +Class_release_64: class_release_64 + + +CLASS_DEBUG_DIR = Class_debug +CLASS_DIR = Class_release +CLASS_DEBUG_64_DIR = Class_debug_64 +CLASS_64_DIR = Class_release_64 +MAKEFILE = Makefile.old + +# ----------------------------------------------------------------------------- +# fixes shared object lookup error(SIGFPE floating point exception) +HASH_STYLE=$(call ld-option, -Wl$(comma)--hash-style=sysv) + + +######################### +#### Serial Versions #### +######################### +.PHONY : Class_debug +class_debug: + mkdir -p $(CLASS_DEBUG_DIR) + cd $(CLASS_DEBUG_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_DEBUG $(PROGRAM) + +.PHONY : Class_release +class_release: + mkdir -p $(CLASS_DIR) + cd $(CLASS_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_RELEASE $(PROGRAM) +.PHONY : Class_debug_64 +class_debug_64: + mkdir -p $(CLASS_DEBUG_64_DIR) + cd $(CLASS_DEBUG_64_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_DEBUG_64 $(PROGRAM) + +.PHONY : Class_release_64 +class_release_64: + mkdir -p $(CLASS_64_DIR) + cd $(CLASS_64_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_RELEASE_64 $(PROGRAM) + +# Recursive make begins here +# +# ============================================================================= +# Significant suffixes [assuming Fortran 90 (.f90) source code]: +# Significant suffixes [assuming Fortran 90 (.F90) source code, needs to be preprocessed ]: +# ============================================================================= + +#SRC = ../phreeqc +.SUFFIXES : .o .c .cxx .cpp + +# compilers + +# ----------------------------------------------------------------------------- +.c.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +.cxx.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +.cpp.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +# ----------------------------------------------------------------------------- +#hdf options +#HDF5_ROOT=$(HOME)/../../usr +#HDF5_INCLUDES=-I$(HDF5_ROOT)/src +#HDF5_LIBS=${HDF5_ROOT}/lib/libhdf5.a -lz -lpthread + +# ----------------------------------------------------------------------------- +# #define compile options + +# ----------------------------------------------------------------------------- +# #define gmp for inverse modeling +# comment the following lines to remove multiprecision option + +# ----------------------------------------------------------------------------- +#efence for debugging +EFENCE_LIB=-L$(HOME)/packages/efence + +# ----------------------------------------------------------------------------- +# 4 Versions +# ----------------------------------------------------------------------------- +ifeq ($(CFG), CLASS_DEBUG) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o +# CL1MP_LIB=-lgmp + CL1MP_LIB=/z/parkplace/usr/lib/libgmp.a + endif + DEFINES = -DUSE_PHRQ_ALLOC $(DEFINE_INVERSE_CL1MP) # -DPHREEQC2 + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -g $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_RELEASE) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o +# CL1MP_LIB=-lgmp + CL1MP_LIB=/z/parkplace/usr/lib/libgmp.a + endif + DEFINES = -DNDEBUG $(DEFINE_INVERSE_CL1MP) # -DPHREEQC2 + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -pedantic -O3 $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_DEBUG_64) +# INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o + CL1MP_LIB=libgmp.a + endif + DEFINES = -DUSE_PHRQ_ALLOC $(DEFINE_INVERSE_CL1MP) -DTEST_COPY_OPERATOR + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -g $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_RELEASE_64) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o + CL1MP_LIB=/usr/lib64/libgmp.a + endif + DEFINES = -DNDEBUG $(DEFINE_INVERSE_CL1MP) # -DUSE_PHRQ_ALLOC # -DR_SO + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + PROFILE = + CXXFLAGS = $(PROFILE) -Wall -pedantic -O3 $(DEFINES) $(INCLUDES) # -g +# CXXFLAGS = -fprofile-arcs -ftest-coverage $(DEFINES) $(INCLUDES) # -g + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = $(PROFILE) -lm ${CL1MP_LIB} ${HASH_STYLE} # -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -lgcov +endif + +# ----------------------------------------------------------------------------- +# + +COMMON_COBJS = \ + advection.o \ + basicsubs.o \ + cl1.o \ + cvdense.o \ + cvode.o \ + dense.o \ + gases.o \ + input.o \ + integrate.o \ + inverse.o \ + isotopes.o \ + kinetics.o \ + mainsubs.o \ + model.o \ + nvector.o \ + nvector_serial.o \ + parse.o \ + PHRQ_io_output.o \ + phqalloc.o \ + pitzer.o \ + pitzer_structures.o \ + prep.o \ + print.o \ + read.o \ + readtr.o \ + sit.o \ + smalldense.o \ + spread.o \ + step.o \ + structures.o \ + sundialsmath.o \ + tally.o \ + tidy.o \ + transport.o \ + utilities.o + +MAIN_FILE = main.o + +CLASS_FILES = \ + class_main.o \ + Phreeqc.o + + +COMMON_CXXOBJS = \ + dumper.o \ + Dictionary.o \ + Exchange.o \ + ExchComp.o \ + GasPhase.o \ + GasComp.o \ + ISolution.o \ + ISolutionComp.o \ + Keywords.o \ + KineticsComp.o \ + cxxKinetics.o \ + cxxMix.o \ + NameDouble.o \ + NumKeyword.o \ + Parser.o \ + PBasic.o \ + PHRQ_base.o \ + PHRQ_io.o \ + PPassemblageComp.o \ + PPassemblage.o \ + Pressure.o \ + Reaction.o \ + ReadClass.o \ + runner.o \ + SelectedOutput.o \ + Serializer.o \ + Solution.o \ + SolutionIsotope.o \ + SSassemblage.o \ + SScomp.o \ + SS.o \ + StorageBin.o \ + StorageBinList.o \ + Surface.o \ + SurfaceCharge.o \ + SurfaceComp.o \ + System.o \ + Temperature.o \ + Use.o \ + UserPunch.o \ + Utils.o + +# ----------------------------------------------------------------------------- +# Assign dependents to target on dependency line & link options on command +# line. Command line is initiated with a tab. ($@ is an internal macro for +# the current target.) + +${PROGRAM} : ${OBJECT_FILES} +# ${CXX} -p -o $@ ${OBJECT_FILES} ${LD_FLAGS} + ${CXX} -o $@ ${OBJECT_FILES} ${LD_FLAGS} + @echo; echo Done making for phreeqcpp +# ----------------------------------------------------------------------------- +# Module dependency list +# ----------------------------------------------------------------------------- +# +# CXX files +# + +cxxKinetics.o: ../cxxKinetics.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../cxxKinetics.h ../KineticsComp.h \ + ../phqalloc.h ../Dictionary.h +cxxMix.o: ../cxxMix.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../phqalloc.h +Exchange.o: ../Exchange.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Exchange.h ../ExchComp.h ../phqalloc.h +ExchComp.o: ../ExchComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../ExchComp.h ../phqalloc.h \ + ../Dictionary.h +GasComp.o: ../GasComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasComp.h ../phqalloc.h \ + ../Dictionary.h +GasPhase.o: ../GasPhase.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasPhase.h ../GasComp.h ../phqalloc.h +ISolutionComp.o: ../ISolutionComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../ISolutionComp.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/PHRQ_base.h ../Solution.h ../NumKeyword.h \ + ../SolutionIsotope.h ../NameDouble.h ../common/PHRQ_io.h ../ISolution.h \ + ../global_structures.h ../Surface.h ../SurfaceComp.h ../SurfaceCharge.h \ + ../NA.h ../phqalloc.h +ISolution.o: ../ISolution.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../ISolution.h ../ISolutionComp.h \ + ../phqalloc.h +KineticsComp.o: ../KineticsComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../KineticsComp.h ../phqalloc.h \ + ../Dictionary.h +NameDouble.o: ../NameDouble.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Dictionary.h ../phqalloc.h \ + ../ISolutionComp.h +NumKeyword.o: ../NumKeyword.cxx ../NumKeyword.h ../common/PHRQ_base.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/Utils.h ../common/phrqtype.h +PPassemblageComp.o: ../PPassemblageComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../PPassemblageComp.h ../Dictionary.h \ + ../phqalloc.h +PPassemblage.o: ../PPassemblage.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../PPassemblage.h ../PPassemblageComp.h \ + ../phqalloc.h +Pressure.o: ../Pressure.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../phqalloc.h +Reaction.o: ../Reaction.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Reaction.h ../phqalloc.h +ReadClass.o: ../ReadClass.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Exchange.h ../ExchComp.h \ + ../PPassemblage.h ../PPassemblageComp.h ../cxxKinetics.h \ + ../KineticsComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../GasPhase.h \ + ../GasComp.h ../Reaction.h ../Temperature.h ../phqalloc.h +Serializer.o: ../Serializer.cxx ../Serializer.h ../common/PHRQ_base.h \ + ../Dictionary.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../common/Utils.h ../common/phrqtype.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h \ + ../Exchange.h ../ExchComp.h ../Temperature.h ../GasPhase.h ../GasComp.h \ + ../cxxKinetics.h ../KineticsComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h +Solution.o: ../Solution.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../phqalloc.h ../Dictionary.h +SolutionIsotope.o: ../SolutionIsotope.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SolutionIsotope.h ../phqalloc.h \ + ../Dictionary.h +SSassemblage.o: ../SSassemblage.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../phqalloc.h ../Dictionary.h +SScomp.o: ../SScomp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SScomp.h ../phqalloc.h ../Dictionary.h +SS.o: ../SS.cxx ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../common/Utils.h ../common/phrqtype.h \ + ../SS.h ../SScomp.h ../Dictionary.h ../phqalloc.h +StorageBin.o: ../StorageBin.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../StorageBin.h ../System.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../cxxKinetics.h ../KineticsComp.h \ + ../PPassemblage.h ../PPassemblageComp.h ../Reaction.h ../Temperature.h \ + ../phqalloc.h +SurfaceCharge.o: ../SurfaceCharge.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Dictionary.h +SurfaceComp.o: ../SurfaceComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Dictionary.h +Surface.o: ../Surface.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +System.o: ../System.cxx ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../System.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../cxxKinetics.h ../KineticsComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Reaction.h ../Temperature.h +Temperature.o: ../Temperature.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../Temperature.h ../phqalloc.h +advection.o: ../advection.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../cxxKinetics.h \ + ../KineticsComp.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h +basicsubs.o: ../basicsubs.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../PBasic.h ../Exchange.h \ + ../ExchComp.h ../GasPhase.h ../GasComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../cxxKinetics.h ../KineticsComp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h +ChartHandler.o: ../ChartHandler.cpp +ChartObject.o: ../ChartObject.cpp +cl1.o: ../cl1.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +cl1mp.o: ../cl1mp.cpp +class_main.o: ../class_main.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../cxxKinetics.h ../KineticsComp.h +CurveObject.o: ../CurveObject.cpp +cvdense.o: ../cvdense.cpp ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../common/phrqtype.h ../nvector.h ../dense.h ../smalldense.h \ + ../sundialsmath.h ../Phreeqc.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +cvode.o: ../cvode.cpp ../nvector_serial.h ../nvector.h ../sundialstypes.h \ + ../common/phrqtype.h ../cvode.h ../sundialsmath.h ../Phreeqc.h \ + ../cvdense.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +dense.o: ../dense.cpp ../sundialstypes.h ../common/phrqtype.h \ + ../sundialsmath.h ../dense.h ../smalldense.h +Dictionary.o: ../Dictionary.cpp ../Dictionary.h +dumper.o: ../dumper.cpp ../dumper.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../common/Parser.h ../common/PHRQ_base.h \ + ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h ../common/PHRQ_io.h +gases.o: ../gases.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasPhase.h ../GasComp.h +input.o: ../input.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +integrate.o: ../integrate.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +inverse.o: ../inverse.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../common/Utils.h \ + ../common/phrqtype.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h +isotopes.o: ../isotopes.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +kinetics.o: ../kinetics.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../StorageBin.h ../System.h \ + ../Reaction.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../Temperature.h \ + ../nvector_serial.h +mainsubs.o: ../mainsubs.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../PBasic.h \ + ../Temperature.h ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h \ + ../Reaction.h ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h \ + ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +model.o: ../model.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h +nvector.o: ../nvector.cpp ../nvector.h ../sundialstypes.h \ + ../common/phrqtype.h +nvector_serial.o: ../nvector_serial.cpp ../nvector_serial.h ../nvector.h \ + ../sundialstypes.h ../common/phrqtype.h ../sundialsmath.h +parse.o: ../parse.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +PBasic.o: ../PBasic.cpp ../PBasic.h ../common/phrqtype.h \ + ../common/PHRQ_base.h ../global_structures.h ../Surface.h \ + ../NumKeyword.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../NA.h ../Phreeqc.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h ../SelectedOutput.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +phqalloc.o: ../phqalloc.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h +Phreeqc.o: ../Phreeqc.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Reaction.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h \ + ../KineticsComp.h ../phqalloc.h ../PBasic.h ../Temperature.h +PHRQ_io_output.o: ../PHRQ_io_output.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h +pitzer.o: ../pitzer.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +pitzer_structures.o: ../pitzer_structures.cpp ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +prep.o: ../prep.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../cxxKinetics.h ../KineticsComp.h +print.o: ../print.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +read.o: ../read.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +readtr.o: ../readtr.cpp ../StorageBin.h ../System.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/phrqtype.h ../common/PHRQ_base.h \ + ../common/PHRQ_io.h ../SS.h ../SScomp.h ../Phreeqc.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../SurfaceCharge.h ../global_structures.h \ + ../NA.h ../phqalloc.h ../common/Utils.h ../common/phrqtype.h +runner.o: ../runner.cpp ../runner.h ../common/phrqtype.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../common/Parser.h \ + ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h \ + ../NA.h ../common/Utils.h ../common/phrqtype.h +SelectedOutput.o: ../SelectedOutput.cpp ../SelectedOutput.h \ + ../NumKeyword.h ../common/PHRQ_base.h +sit.o: ../sit.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +smalldense.o: ../smalldense.cpp ../smalldense.h ../sundialstypes.h \ + ../common/phrqtype.h ../sundialsmath.h +spread.o: ../spread.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../common/Utils.h \ + ../common/phrqtype.h +step.o: ../step.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../common/Utils.h \ + ../common/phrqtype.h ../StorageBin.h ../System.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../Temperature.h ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h \ + ../Reaction.h ../cxxKinetics.h ../KineticsComp.h +StorageBinList.o: ../StorageBinList.cpp ../StorageBinList.h \ + ../common/PHRQ_base.h ../common/Parser.h ../common/PHRQ_base.h \ + ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h +structures.o: ../structures.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../StorageBin.h \ + ../System.h +sundialsmath.o: ../sundialsmath.cpp ../sundialsmath.h ../sundialstypes.h \ + ../common/phrqtype.h +tally.o: ../tally.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +tidy.o: ../tidy.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +transport.o: ../transport.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +Use.o: ../Use.cpp ../Use.h +UserPunch.o: ../UserPunch.cpp ../UserPunch.h ../NumKeyword.h \ + ../common/PHRQ_base.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h +utilities.o: ../utilities.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +Keywords.o: ../PhreeqcKeywords/Keywords.cpp ../PhreeqcKeywords/Keywords.h +Parser.o: ../common/Parser.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h +PHRQ_base.o: ../common/PHRQ_base.cxx ../common/PHRQ_base.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h +Utils.o: ../common/Utils.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h + +# ----------------------------------------------------------------------------- +clean: + rm -rf Class_release Class_debug Class_release_64 Class_debug_64 + +dependencies: + mkdir -p $(CLASS_DEBUG_DIR) + cd $(CLASS_DEBUG_DIR); gcc -MM -I.. -I../PhreeqcKeywords -I../common ../*.cxx ../*.cpp ../PhreeqcKeywords/*.cpp ../common/*.cxx + +tester: +# cd ../mytest; make clean; make -k -j 1 $(SPOOL) make.out $(SPOOL2); make diff $(SPOOL) diff.out $(SPOOL2) + cd ../mytest $(CONCAT) make clean $(CONCAT) make -k -j 1 $(SPOOL) make.out $(SPOOL2) $(CONCAT) make diff $(SPOOL) diff.out $(SPOOL2) + cd ../examples $(CONCAT) make -f Makefile.old clean $(CONCAT) make -f Makefile.old -k -j 1 $(SPOOL) make.out $(SPOOL2) $(CONCAT) make -f Makefile.old diff $(SPOOL) diff.out $(SPOOL2) +# svn status -q ../mytest +# svn status -q ../examples + +#ld-option +# Usage: ldflags += $(call ld-option, -Wl$(comma)--hash-style=sysv) +comma=, +ld-option = $(shell if $(CC) $(1) \ + -nostdlib -o /dev/null -xc /dev/null \ + > /dev/null 2>&1 ; then echo "$(1)" ; else echo "$(2)"; fi) + +# ============================================================================= +# End of makefile. +# ============================================================================= diff --git a/phreeqcpp/NA.h b/phreeqcpp/NA.h new file mode 100644 index 00000000..3222c6cb --- /dev/null +++ b/phreeqcpp/NA.h @@ -0,0 +1 @@ +#define NA -98.7654321 diff --git a/phreeqcpp/NameDouble.cxx b/phreeqcpp/NameDouble.cxx new file mode 100644 index 00000000..3dcf9607 --- /dev/null +++ b/phreeqcpp/NameDouble.cxx @@ -0,0 +1,537 @@ +// NameDouble.cxx: implementation of the cxxNameDouble class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include // std::sort +#include // std::cout std::cerr + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "NameDouble.h" +#include "Dictionary.h" +//#include "Dictionary.h" +#include "phqalloc.h" +#include "ISolutionComp.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxNameDouble::cxxNameDouble() + // + // default constructor for cxxNameDouble + // +{ + this->type = ND_ELT_MOLES; +} +cxxNameDouble::cxxNameDouble(const std::vector& el) +// constructor for cxxNameDouble from vector of elt_list +{ + size_t i; + const class elt_list* elt_list_ptr = &el[0]; + for (i = 0; elt_list_ptr[i].elt != NULL; i++) + { + (*this)[elt_list_ptr[i].elt->name] = elt_list_ptr[i].coef; + } + this->type = ND_ELT_MOLES; +} +cxxNameDouble::cxxNameDouble(const cxxNameDouble & old, LDBLE factor) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + for (cxxNameDouble::const_iterator it = old.begin(); it != old.end(); + it++) + { + if (old.type == ND_ELT_MOLES) + { + if (it->second * factor > 0) + { + (*this)[(it->first)] = it->second * factor; + } + } + else + { + (*this)[(it->first)] = it->second * factor; + } + } + this->type = old.type; +} +cxxNameDouble::cxxNameDouble(std::map < std::string, cxxISolutionComp > &comps) + // + // constructor for cxxNameDouble from map of cxxISolutionComp + // +{ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = comps.begin(); it != comps.end(); it++) + { + (*this)[it->first] = it->second.Get_moles(); + } + this->type = ND_ELT_MOLES; +} +cxxNameDouble::cxxNameDouble(class name_coef *nc, int count) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + int i; + for (i = 0; i < count; i++) + { + if (nc[i].name == NULL) + continue; + + if ((*this).find(nc[i].name) == (*this).end()) + { + (*this)[nc[i].name] = nc[i].coef; + } + else + { + (*this)[nc[i].name] = (*this).find(nc[i].name)->second + nc[i].coef; + } + + } + this->type = ND_NAME_COEF; +} + +cxxNameDouble::~cxxNameDouble() +{ +} + +void +cxxNameDouble::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + std::string xmlElement, xmlAtt1, xmlAtt2; + + switch ((*this).type) + { + case cxxNameDouble::ND_SPECIES_LA: + xmlElement = "first << xmlAtt2 << it-> + second << "/>" << "\n"; + } +} + +void +cxxNameDouble::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + + for (const_iterator it = (*this).begin(); it != (*this).end(); it++) + { + s_oss << indent0; + if (it->first.size() < 29 - indent0.size()) + { + s_oss << Utilities::pad_right(it->first, 29 - indent0.size()) << it->second << "\n"; + } + else + { + s_oss << Utilities::pad_right(it->first, it->first.size() + indent0.size()) << " " << it->second << "\n"; + } + } +} + +CParser::STATUS_TYPE cxxNameDouble::read_raw(CParser & parser, + std::istream::pos_type & pos) +{ + std::string token; + LDBLE + d; + + CParser::TOKEN_TYPE j; + j = parser.copy_token(token, pos); + + if (j == CParser::TT_EMPTY) + return CParser::PARSER_OK; + + if (!(parser.get_iss() >> d)) + { + return CParser::PARSER_ERROR; + } + (*this)[token.c_str()] = d; + return CParser::PARSER_OK; +} + +void +cxxNameDouble::add_extensive(const cxxNameDouble & addee, LDBLE factor) +// +// Sums two name doubles, this + factor*nd2 +// +{ + if (factor == 0) + return; + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + (*this)[it->first] = current->second + it->second * factor; + } + else + { + (*this)[it->first] = it->second * factor; + } + } +} +void +cxxNameDouble::add_intensive(const cxxNameDouble & addee, LDBLE f1, + LDBLE f2) +// +// Sums two name doubles, this*f1 + f2*nd2 +// +{ + assert(f1 >= 0 && f2 >= 0); + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + (*this)[it->first] = f1 * current->second + f2 * it->second; + } + else + { + (*this)[it->first] = f2 * it->second; + } + } +} +void +cxxNameDouble::add_log_activities(const cxxNameDouble & addee, LDBLE f1, + LDBLE f2) +// +// Sums two name doubles, this*f1 + f2*nd2, assuming log values +// +{ + assert(f1 >= 0 && f2 >= 0); + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + LDBLE a1 = pow((LDBLE) 10., current->second); + LDBLE a2 = pow((LDBLE) 10., it->second); + (*this)[it->first] = log10(f1 * a1 + f2 * a2); + } + else + { + (*this)[it->first] = it->second + log10(f2); + } + } +} +cxxNameDouble +cxxNameDouble::Simplify_redox(void) const +{ + cxxNameDouble const &nd = *this; + std::basic_string < char >::size_type indexCh; + cxxNameDouble new_totals; + new_totals.type = cxxNameDouble::ND_ELT_MOLES; + { + std::string current_ename; + std::string const *ename_ptr; + cxxNameDouble::const_iterator it; + + // make list of elements in new_totals + for (it = nd.begin(); it != nd.end(); ++it) + { + current_ename = it->first; + if (it->first.size() < 4) + { + ename_ptr = &(it->first); + } + else + { + indexCh = it->first.find("("); + if (indexCh != std::string::npos) + { + current_ename = it->first.substr(0, indexCh); + ename_ptr = &(current_ename); + } + else + { + ename_ptr = &(it->first); + } + } + if (current_ename == "H" || current_ename == "O" || current_ename == "Charge") + continue; + new_totals[*ename_ptr] = 0; + } + } + + // sum totals for elements + { + cxxNameDouble::const_iterator old_it = nd.begin(); + cxxNameDouble::iterator new_it = new_totals.begin(); + std::string old_ename; + std::string const *old_ename_ptr; + while (old_it != nd.end() && new_it != new_totals.end()) + { + if (old_it->first.size() < 4) + { + old_ename_ptr = &old_it->first; + } + else + { + indexCh = old_it->first.find("("); + if (indexCh != std::string::npos) + { + old_ename = old_it->first.substr(0, indexCh); + old_ename_ptr = &old_ename; + } + else + { + old_ename_ptr = &old_it->first; + } + } + int j = strcmp(new_it->first.c_str(), old_ename_ptr->c_str()); + if (j < 0) + { + new_it++; + } + else if (j == 0) + { + new_it->second += old_it->second; + old_it++; + } + else + { + old_it++; + } + } + } + return new_totals; +} + +void +cxxNameDouble::Multiply_activities_redox(std::string str, LDBLE f) +{ + // update original master_activities using just computed factors + cxxNameDouble::iterator it; + LDBLE lg_f = log10(f); + std::string redox_name = str; + redox_name.append("("); + + for (it = this->begin(); it != this->end(); it++) + { + if (str[0] > it->first[0]) continue; + if (it->first == str) + { + // Found exact match + it->second += lg_f; + } + else + { + // no exact match, current is element name, need to find all valences + if (strstr(it->first.c_str(), redox_name.c_str()) == it->first.c_str()) + { + it->second += lg_f; + } + } + if (str[0] < it->first[0]) break; + } +} +LDBLE +cxxNameDouble::Get_total_element(const char *string) const +{ + cxxNameDouble::const_iterator it; + LDBLE d = 0.0; + for (it = this->begin(); it != this->end(); ++it) + { + // C++ way to do it + std::string ename(string); + std::string current_ename(it->first); + std::basic_string < char >::size_type indexCh; + indexCh = current_ename.find("("); + if (indexCh != std::string::npos) + { + current_ename = current_ename.substr(0, indexCh); + } + if (current_ename == ename) + { + d += it->second; + } + } + return (d); +} +void +cxxNameDouble::add(const char *token, LDBLE total) +// +// add to total for a specified element +// +{ + char key[MAX_LENGTH]; + strcpy(key, token); + + cxxNameDouble::iterator current = (*this).find(key); + if (current != (*this).end()) + { + (*this)[key] = current->second + total; + } + else + { + (*this)[key] = total; + } +} +void +cxxNameDouble::multiply(LDBLE extensive) +{ +// +// Multiplies by extensive +// + + for (cxxNameDouble::iterator it = this->begin(); it != this->end(); it++) + { + it->second *= extensive; + } +} +void +cxxNameDouble::merge_redox(const cxxNameDouble & source) +// +// Merges source into this +// Accounts for possible conflicts between redox state and +// totals +// +{ + for (cxxNameDouble::const_iterator sit = source.begin(); sit != source.end(); sit++) + { + + std::string redox_name = sit->first; + std::string elt_name; + size_t pos = redox_name.find("("); + + bool redox; + if (pos != std::string::npos) + { + redox = true; + elt_name = redox_name.substr(0, pos - 1); + } + else + { + redox = false; + elt_name = redox_name; + } + if (redox) + { + // Remove elt_name, if present + if ((*this).find(elt_name) != (*this).end()) + { + (*this).erase((*this).find(elt_name)); + } + // Put in redox name + (*this)[redox_name] = sit->second; + } + else + { + std::string substring; + substring.append(elt_name); + substring.append("("); + + // Remove all redox + bool deleted = true; + while (deleted) + { + deleted = false; + cxxNameDouble::iterator current = (*this).begin(); + for ( ; current != (*this).end(); current++) + { + if (current->first.find(substring) == 0) + { + (*this).erase(current); + deleted = true; + break; + } + } + } + // Put in elt name + (*this)[elt_name] = sit->second; + } + } +} +struct DblCmp { + bool operator()(const std::pair &lhs, const std::pair &rhs) + { + return lhs.second > rhs.second; + } +}; +std::vector< std::pair > +cxxNameDouble::sort_second(void) +{ + std::vector< std::pair > myvec(this->begin(), this->end()); + std::sort(myvec.begin(), myvec.end(), DblCmp()); + + return myvec; +} +void +cxxNameDouble::Serialize(Dictionary &dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back((int) (*this).size()); + for (const_iterator it = (*this).begin(); it != (*this).end(); it++) + { + int n = dictionary.Find(it->first); + ints.push_back(n); + doubles.push_back(it->second); + } +} +void +cxxNameDouble::Deserialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles, int &ii, int &dd) +{ + this->clear(); + int count = ints[ii++]; + for (int j = 0; j < count; j++) + { + int n = ints[ii++]; + assert(n >= 0); + std::string str = dictionary.GetWords()[n]; + if (str.size() != 0) + { + (*this)[str] = doubles[dd++]; + } + } +} diff --git a/phreeqcpp/NameDouble.h b/phreeqcpp/NameDouble.h new file mode 100644 index 00000000..cbc9091f --- /dev/null +++ b/phreeqcpp/NameDouble.h @@ -0,0 +1,66 @@ +#if !defined(NAMEDOUBLE_H_INCLUDED) +#define NAMEDOUBLE_H_INCLUDED + +#include "PHRQ_exports.h" + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +class Phreeqc; +#include "Parser.h" +#include "phrqtype.h" +class Dictionary; +class cxxISolutionComp; + +class IPQ_DLL_EXPORT cxxNameDouble:public + std::map < std::string, LDBLE > +{ + + public: + enum ND_TYPE + { + ND_ELT_MOLES = 1, + ND_SPECIES_LA = 2, + ND_SPECIES_GAMMA = 3, + ND_NAME_COEF = 4 + }; + + cxxNameDouble(); + cxxNameDouble(const std::vector& el); + cxxNameDouble(std::map < std::string, cxxISolutionComp > &comps); + + cxxNameDouble(class name_coef *nc, int count); + cxxNameDouble(const cxxNameDouble & old, LDBLE factor); + ~cxxNameDouble(); + + LDBLE Get_total_element(const char *string) const; + cxxNameDouble Simplify_redox(void) const; + void Multiply_activities_redox(std::string, LDBLE f); + + void dump_xml(std::ostream & s_oss, unsigned int indent) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + CParser::STATUS_TYPE read_raw(CParser & parser, std::istream::pos_type & pos); + void add_extensive(const cxxNameDouble & old, LDBLE factor); + void add_intensive(const cxxNameDouble & addee, LDBLE fthis, LDBLE f2); + void add_log_activities(const cxxNameDouble & addee, LDBLE fthis, LDBLE f2); + void add(const char *key, LDBLE total); + void multiply(LDBLE factor); + void merge_redox(const cxxNameDouble & source); + + std::vector< std::pair > sort_second(void); + + void + insert(const char *str, LDBLE d) + { + (*this)[str] = d; + } + void Serialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles); + void Deserialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles, int &ii, int &dd); + + enum ND_TYPE type; + +}; + +#endif // !defined(NAMEDOUBLE_H_INCLUDED) diff --git a/phreeqcpp/NumKeyword.cxx b/phreeqcpp/NumKeyword.cxx new file mode 100644 index 00000000..4dad6469 --- /dev/null +++ b/phreeqcpp/NumKeyword.cxx @@ -0,0 +1,190 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +// NumKeyword.cxx: implementation of the cxxNumKeyword class. +// +////////////////////////////////////////////////////////////////////// + +#include // ::sscanf +#include "NumKeyword.h" +#include "Parser.h" +#include "Utils.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxNumKeyword::cxxNumKeyword(PHRQ_io *io) +{ + this->n_user = 1; + this->n_user_end = 1; + this->Set_io(io); +} + +cxxNumKeyword::~cxxNumKeyword() +{ +} + +void +cxxNumKeyword::dump_xml(std::ostream & os, unsigned int indent) const +{ + unsigned int i; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->n_user << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->n_user_end << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->description << "" << "\n"; +} + +void +cxxNumKeyword::read_number_description(CParser & parser) +{ + std::string keyword; + std::istream::pos_type ptr; + + // skip keyword + parser.copy_token(keyword, ptr); + + // skip whitespace + while (::isspace(parser.get_iss().peek())) + parser.get_iss().ignore(); + + // read number + if (::isdigit(parser.get_iss().peek()) || parser.get_iss().peek() == '-') + { + parser.get_iss() >> this->n_user; + char ch = (char)parser.get_iss().peek(); + if (ch == '-') + { + parser.get_iss() >> ch; // eat '-' + parser.get_iss() >> this->n_user_end; + if (this->n_user_end < this->n_user) + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + } + + // skip whitespace + while (::isspace(parser.get_iss().peek())) + parser.get_iss().ignore(); + + // copy description + std::getline(parser.get_iss(), this->description); +} + + +void +cxxNumKeyword::read_number_description(std::istream & is) +{ + // KEYWORD [[1[-20]] [This is the description]] + + // eat keyword + std::string token; + is >> token; + + // skip whitespace + while (::isspace(is.peek())) + is.ignore(); + + if (::isdigit(is.peek())) + { + is >> this->n_user; + char ch = (char)is.peek(); + if (ch == '-') + { + is >> ch; // eat '-' + is >> this->n_user_end; + } + else + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + } + + while (::isspace(is.peek())) + is.ignore(); + + std::getline(is, this->description); +} +void +cxxNumKeyword::read_number_description(const std::string & line_in) +{ + std::string keyword, token; + //std::istream::pos_type ptr; + + std::string line = line_in; + std::string::iterator b = line.begin(); + std::string::iterator e = line.end(); + this->description.clear(); + // skip keyword + CParser::copy_token(keyword, b, e); + + // read number + if (CParser::copy_token(token, b, e) == CParser::TT_DIGIT) + { + if (token[0] == '-') + { + token = token.substr(1); + Utilities::replace("-", " ", token); + token = "-" + token; + } + else + { + Utilities::replace("-", " ", token); + } + int j = sscanf(token.c_str(), "%d%d", &this->n_user, &this->n_user_end); + if (j == 0) + { + this->n_user = this->n_user_end = 1; + } + else if (j == 1) + { + this->n_user_end = this->n_user; + } + if (this->n_user_end < this->n_user) + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + this->description = token; + } + + // skip whitespace + std::string::iterator ic; + for (ic = b; ic != e; ic++) + { + this->description += *ic; + } + trim_left(this->description); +} diff --git a/phreeqcpp/NumKeyword.h b/phreeqcpp/NumKeyword.h new file mode 100644 index 00000000..f795c4e9 --- /dev/null +++ b/phreeqcpp/NumKeyword.h @@ -0,0 +1,67 @@ +#if !defined(NUMKEYWORD_H_INCLUDED) +#define NUMKEYWORD_H_INCLUDED + +#include // std::ostream +#include // std::string +#include "PHRQ_base.h" +class CParser; + +class cxxNumKeyword: public PHRQ_base +{ + public: + cxxNumKeyword(PHRQ_io *io=NULL); + virtual ~ cxxNumKeyword(); + const std::string &Get_description() const + { + return this->description; + } + void Set_description(std::string str) + { + this->description = str; + } + void Set_description(const char *str) + { + if (str != NULL) + this->description = str; + } + + int Get_n_user() const + { + return this->n_user; + } + void Set_n_user(int user) + { + this->n_user = user; + } + + int Get_n_user_end() const + { + return this->n_user_end; + } + void Set_n_user_end(int user_end) + { + this->n_user_end = user_end; + } + void Set_n_user_both(int user_end) + { + this->n_user = this->n_user_end = user_end; + } + + bool operator<(const cxxNumKeyword & key) const + { + return (this->n_user < key.n_user); + } + + virtual void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void read_number_description(CParser & parser); + void read_number_description(const std::string & line_in); + protected: + int n_user; + int n_user_end; + std::string description; + + protected: + void read_number_description(std::istream & is); +}; +#endif // !defined(NUMKEYWORD_H_INCLUDED) diff --git a/phreeqcpp/PBasic.cpp b/phreeqcpp/PBasic.cpp new file mode 100644 index 00000000..09016cd9 --- /dev/null +++ b/phreeqcpp/PBasic.cpp @@ -0,0 +1,7546 @@ +#if !(defined(WIN32) && !defined(__GNUC__)) +#include +#define _ASSERTE assert +#endif +#include +#include "Phreeqc.h" +#include "PBasic.h" +#include "phqalloc.h" +#include "NameDouble.h" +#include "Utils.h" +#include "Solution.h" + +/* Run-time library for PhreeqcPtr->use with "p2c", the Pascal to C translator */ + +/* "p2c" Copyright (C) 1989, 1990, 1991 Free Software Foundation. + * By Dave Gillespie, daveg@csvax.cs.caltech.edu. Version 1.20. + * This file may be copied, modified, etc. in any way. It is not restricted + * by the licence agreement accompanying p2c itself. + */ + +#define STOP 1 +#define CONTINUE 0 +#define Isspace(c) isspace(c) /* or "((c) == ' ')" if preferred */ +#define toklength 20 +typedef long chset[9]; + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* Output from p2c, the Pascal-to-C translator */ +/* From input file "basic.p" */ + +PBasic::PBasic(Phreeqc * ptr, PHRQ_io *phrq_io) + : PHRQ_base(phrq_io) +{ + if (ptr == NULL) + { + error_msg("No Phreeqc instance in PBasic constructor\n", 1); + } + PhreeqcPtr = ptr; + inbuf = NULL; + linebase = NULL; + varbase = NULL; + loopbase = NULL; + curline = 0; + stmtline = NULL; + dataline = NULL; + stmttok = NULL; + datatok = NULL; + buf = NULL; + exitflag = false; + EXCP_LINE = 0; + P_escapecode = 0; + P_ioresult = 0; + parse_all = false; + phreeqci_gui = false; + parse_whole_program = true; +#if defined(PHREEQCI_GUI) + hInfiniteLoop = 0; + nIDErrPrompt = 0; +#else + nIDErrPrompt = (PBasic::IDErr)0; +#endif + nErrLineNumber = 0; + punch_tab = true; + skip_punch = false; + // Basic commands initialized at bottom of file +} +PBasic::~PBasic(void) +{ +} + +int PBasic:: +basic_compile(const char *commands, void **lnbase, void **vbase, void **lpbase) +{ /*main */ + int l; + const char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + do + { + try + { + ptr = commands; + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + if (phreeqci_gui) + { + _ASSERTE(false); + } + else + { + char * error_string = PhreeqcPtr->sformatf("%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + } + else + { + if (phreeqci_gui) + { + _ASSERTE(false); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + catch (const PhreeqcStop&) + { + // clean up memory + disposetokens(&buf); + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + throw; // rethrow + } + } + while (!(exitflag || P_eof())); + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + return (P_escapecode); +} + +int PBasic:: +basic_renumber(char *commands, void **lnbase, void **vbase, void **lpbase) +{ /*main */ + int l, i; + const char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + do + { + try + { + i = 0; + ptr = commands; + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + i++; + if (i == 1) + { + strcpy(inbuf, "renum"); + } + else if (i == 2) + { + strcpy(inbuf, "list"); + } + else if (i == 3) + { + strcpy(inbuf, "new"); + } + else if (i == 4) + { + strcpy(inbuf, "bye"); + } + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + + return (P_escapecode); +} + +int PBasic:: +basic_run(char *commands, void *lnbase, void *vbase, void *lpbase) +{ /*main */ + int l; + const char *ptr; + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + linebase = (linerec *) lnbase; + varbase = (varrec *) vbase; + loopbase = (looprec *) lpbase; + do + { + try + { + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + if (phreeqci_gui) + { + _ASSERTE(FALSE); + } + else + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + + // Cleanup after run + clearvars(); + clearloops(); + restoredata(); + + return (P_escapecode); +} + +int PBasic:: +basic_main(const char *commands) +{ /*main */ + int l; + const char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; +#ifdef SKIP + printf("Chipmunk BASIC 1.0\n\n"); +#endif + exitflag = false; + ptr = commands; + do + { + try + { + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + return 1; +/* exit(EXIT_SUCCESS); */ +} + +/* ---------------------------------------------------------------------- */ +int PBasic:: +sget_logical_line(const char **ptr, int *l, char *return_line) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads file fp until end of line, ";", or eof + * stores characters in line_save + * reallocs line_save and line if more space is needed + * + * returns: + * EOF on empty line on end of file or + * OK otherwise + * *l returns length of line + */ + int i; + char c; + i = 0; + if (**ptr == '\0') + return (EOF); + for (;;) + { + c = **ptr; + if (c == '\0') + break; + (*ptr)++; + if (c == ';' || c == '\n') + break; + return_line[i++] = c; + } + return_line[i] = '\0'; + *l = i; + return (1); +} +void PBasic:: +restoredata(void) +{ + dataline = NULL; + datatok = NULL; +} + +void PBasic:: +clearloops(void) +{ + looprec *l; + + while (loopbase != NULL) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } +} + +void PBasic:: +clearvar(varrec * v) +{ + if (v->numdims != 0) + { + if (v->stringvar == 0) + { + PhreeqcPtr->PHRQ_free(v->UU.U0.arr); + v->UU.U0.arr = NULL; + } + else + { + free_dim_stringvar(v); + } + } + else if (v->stringvar && v->UU.U1.sv != NULL) + { + PhreeqcPtr->PHRQ_free(v->UU.U1.sv); + } + v->numdims = 0; + if (v->stringvar) + { + v->UU.U1.sv = NULL; + v->UU.U1.sval = &v->UU.U1.sv; + } + else + { + v->UU.U0.rv = 0.0; + v->UU.U0.val = &v->UU.U0.rv; + } +} + +void PBasic:: +clearvars(void) +{ + varrec *v; + + v = varbase; + while (v != NULL) + { + clearvar(v); + v = v->next; + } +} + +char * PBasic:: +numtostr(char * Result, LDBLE n) +{ + char *l_s; + long i; + + l_s = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (l_s == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + l_s[PhreeqcPtr->max_line - 1] = '\0'; +/* if ((n != 0 && fabs(n) < 1e-2) || fabs(n) >= 1e12) { */ + if (ceil(n) == floor(n)) + { + //if (PhreeqcPtr->current_selected_output != NULL && + // !PhreeqcPtr->current_selected_output->Get_high_precision()) + //{ + // sprintf(l_s, "%12.0f", (double) n); + //} + //else + //{ + // sprintf(l_s, "%20.0f", (double) n); + //} + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (!temp_high_precision) + { + sprintf(l_s, "%12.0f", (double) n); + } + else + { + sprintf(l_s, "%20.0f", (double) n); + } + } + else + { + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (!temp_high_precision) + { + sprintf(l_s, "%12.4e", (double) n); + } + else + { + sprintf(l_s, "%20.12e", (double) n); + } + } + i = (int) strlen(l_s) + 1; + l_s[i - 1] = '\0'; +/* p2c: basic.p, line 237: + * Note: Modification of string length may translate incorrectly [146] */ + strcpy(Result, l_s); + PhreeqcPtr->free_check_null(l_s); + return (Result); +/* } else { + if (PhreeqcPtr->punch.high_precision == FALSE) sprintf(l_s, "%30.10f", n); + else sprintf(l_s, "%30.12f", n); + i = strlen(l_s) + 1; + do { + i--; + } while (l_s[i - 1] == '0'); + if (l_s[i - 1] == '.') + i--; + l_s[i] = '\0'; + * p2c: basic.p, line 248: + * Note: Modification of string length may translate incorrectly [146] * + return strcpy(Result, strltrim(l_s)); + } */ +} + +void PBasic:: +parse(char * l_inbuf, tokenrec ** l_buf) +{ + long i, j, begin, len, m, lp, q; + char token[toklength + 1] = {0}; + tokenrec *t, *tptr; + varrec *v; + char ch; + char *ptr; + + tptr = NULL; + *l_buf = NULL; + i = 1; + lp = q = 0; + do + { + ch = ' '; + while (i <= (int) strlen(l_inbuf) && (ch == ' ' || ch == '\t')) + { + ch = l_inbuf[i - 1]; + i++; + } + if (ch != ' ') + { + t = (tokenrec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(tokenrec)); + if (t == NULL) + PhreeqcPtr->malloc_error(); + if (tptr == NULL) + *l_buf = t; + else + tptr->next = t; + if (phreeqci_gui) + { + t->n_sz = 0; + t->sz_num = 0; + } + tptr = t; + t->next = NULL; + switch (ch) + { + + case '"': + case '\'': + q += 1; + t->kind = tokstr; + j = 0; + len = (int) strlen(l_inbuf); + begin = i; + while (i <= len && l_inbuf[i - 1] != ch) + { + ++j; + ++i; + } + if (l_inbuf[i - 1] == ch) q -= 1; + m = 256; + if (j + 1 > m) + m = j + 1; + t->UU.sp = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (t->UU.sp == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + strncpy(t->UU.sp, l_inbuf + begin - 1, j); + t->UU.sp[j] = '\0'; +/* p2c: basic.p, line 415: + * Note: Modification of string length may translate incorrectly [146] */ + i++; + break; + + case '+': + t->kind = tokplus; + break; + + case '-': + t->kind = tokminus; + break; + + case '*': + t->kind = toktimes; + break; + + case '/': + t->kind = tokdiv; + break; + + case '^': + t->kind = tokup; + break; + + case '(': + case '[': + t->kind = toklp; + lp += 1; + break; + + case ')': + case ']': + t->kind = tokrp; + lp -= 1; + break; + + case ',': + t->kind = tokcomma; + break; + + case ';': + t->kind = toksemi; + break; + + case ':': + t->kind = tokcolon; + break; + + case '?': + t->kind = tokprint; + break; + + case '=': + t->kind = tokeq; + break; + + case '<': + if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '=') + { + t->kind = tokle; + i++; + } + else if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '>') + { + t->kind = tokne; + i++; + } + else + t->kind = toklt; + break; + + case '>': + if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '=') + { + t->kind = tokge; + i++; + } + else + t->kind = tokgt; + break; + + default: + if (isalpha((int) ch)) + { + i--; + j = 0; + token[toklength] = '\0'; + while (i <= (int) strlen(l_inbuf) && + (l_inbuf[i - 1] == '$' || l_inbuf[i - 1] == '_' || + isalnum((int) l_inbuf[i - 1]))) + { + if (j < toklength) + { + j++; + token[j - 1] = l_inbuf[i - 1]; + } + i++; + } + token[j] = '\0'; +/* p2c: basic.p, line 309: + * Note: Modification of string length may translate incorrectly [146] */ + +/* + * Search list + */ + PhreeqcPtr->str_tolower(token); + std::map::const_iterator item; + item = command_tokens.find(token); + if (item != command_tokens.end()) + { + t->kind = item->second; + if (t->kind == tokrem) + { + m = (int) strlen(l_inbuf) + 1; + if (m < 256) + m = 256; + t->UU.sp = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (t->UU.sp == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + sprintf(t->UU.sp, "%.*s", + (int) (strlen(l_inbuf) - i + 1), + l_inbuf + i - 1); + i = (int) strlen(l_inbuf) + 1; + } + } + else + { + t->kind = tokvar; + v = varbase; + while (v != NULL && strcmp(v->name, token)) + v = v->next; + if (v == NULL) + { + v = (varrec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(varrec)); + if (v == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + v->UU.U0.arr = NULL; + v->next = varbase; + varbase = v; + strcpy(v->name, token); + v->numdims = 0; + if (token[strlen(token) - 1] == '$') + { + v->stringvar = true; + v->UU.U1.sv = NULL; + v->UU.U1.sval = &v->UU.U1.sv; + } + else + { + v->stringvar = false; + v->UU.U0.rv = 0.0; + v->UU.U0.val = &v->UU.U0.rv; + } + } + t->UU.vp = v; + } + } + else if (isdigit((int) ch) || ch == '.') + { + t->kind = toknum; + i--; + t->UU.num = strtod(&l_inbuf[i - 1], &ptr); + if (&l_inbuf[i - 1] == ptr) + { + /* + Note: the following causes an infinite loop: + X = ..9 + */ + t->kind = toksnerr; + t->UU.snch = ch; + i++; + break; + } +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + _ASSERTE(t->n_sz == 0); + _ASSERTE(t->sz_num == NULL); + t->n_sz = max(23, ptr - &inbuf[i - 1]); + t->sz_num = + (char *) PhreeqcPtr->PHRQ_calloc((t->n_sz + 1), sizeof(char)); + if (t->sz_num == NULL) + PhreeqcPtr->malloc_error(); + if (ptr > &inbuf[i - 1]) + { + strncpy(t->sz_num, &inbuf[i - 1], + (ptr - &inbuf[i - 1])); + t->sz_num[ptr - &inbuf[i - 1]] = '\0'; + } + else + { + t->sz_num[0] = '\0'; + } + } +#endif + i += (int) (ptr - &l_inbuf[i - 1]); + } + else + { + t->kind = toksnerr; + t->UU.snch = ch; + } + break; + } + } + } + while (i <= (int) strlen(l_inbuf)); + if (q) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_Q; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing \" or \' in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } + if (lp > 0) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_RP; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing ) or ] in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } + else if (lp < 0) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_RP; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing ( or [ in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } +} + +#undef toklength + +void PBasic:: +listtokens(FILE * f, tokenrec * l_buf) +{ + bool ltr; + char STR1[256] = {0}; + char *string; + ltr = false; + while (l_buf != NULL) + { + if ((l_buf->kind >= (long) toknot && l_buf->kind <= (long) tokrenum) || + l_buf->kind == (long) toknum || l_buf->kind == (long) tokvar || + l_buf->kind >= (long) toktc) + { + if (ltr) + /*putc(' ', f); */ + output_msg(" "); + ltr = (bool) (l_buf->kind != toknot); + } + else + ltr = false; + switch (l_buf->kind) + { + + case tokvar: + /*fputs(l_buf->UU.vp->name, f); */ + PhreeqcPtr->output_msg(PhreeqcPtr->sformatf("%s", l_buf->UU.vp->name)); + break; + + case toknum: + /*fputs(numtostr(STR1, l_buf->UU.num), f); */ + string = numtostr(STR1, l_buf->UU.num); + PhreeqcPtr->string_trim(string); + output_msg(PhreeqcPtr->sformatf("%s", string)); + break; + + case tokstr: + if (strchr(l_buf->UU.sp, '\"')) + { + output_msg(PhreeqcPtr->sformatf("\'%s\'", l_buf->UU.sp)); + } + else + { + output_msg(PhreeqcPtr->sformatf("\"%s\"", l_buf->UU.sp)); + } + break; + + case toksnerr: + output_msg(PhreeqcPtr->sformatf("{%c}", l_buf->UU.snch)); + break; + + case tokplus: + /*putc('+', f); */ + output_msg("+"); + break; + + case tokminus: + /*putc('-', f); */ + output_msg("-"); + break; + + case toktimes: + /*putc('*', f); */ + output_msg("*"); + break; + + case tokdiv: + /*putc('/', f); */ + output_msg("/"); + break; + + case tokup: + /*putc('^', f); */ + output_msg("^"); + break; + + case toklp: + /*putc('(', f); */ + output_msg("("); + break; + + case tokrp: + /*putc(')', f); */ + output_msg(")"); + break; + + case tokcomma: + /*putc(',', f); */ + output_msg(","); + break; + + case toksemi: + /*putc(';', f); */ + output_msg(";"); + break; + + case tokcolon: + output_msg(" : "); + break; + + case tokeq: + output_msg(" = "); + break; + + case toklt: + output_msg(" < "); + break; + + case tokgt: + output_msg(" > "); + break; + + case tokle: + output_msg(" <= "); + break; + + case tokge: + output_msg(" >= "); + break; + + case tokne: + output_msg(" <> "); + break; + + case tokand: + output_msg(" AND "); + break; + + case tokor: + output_msg(" OR "); + break; + + case tokxor: + output_msg(" XOR "); + break; + + case tokmod: + output_msg(" MOD "); + break; + + case toknot: + output_msg("NOT "); + break; + + case toksqr: + output_msg("SQR"); + break; + + case toksqrt: + output_msg("SQRT"); + break; + + case toksin: + output_msg("SIN"); + break; + + case tokcos: + output_msg("COS"); + break; + + case toktan: + output_msg("TAN"); + break; + + case tokarctan: + output_msg("ARCTAN"); + break; + + case toklog: + output_msg("LOG"); + break; + + case tokexp: + output_msg("EXP"); + break; + + case tokabs: + output_msg("ABS"); + break; + + case toksgn: + output_msg("SGN"); + break; + + case tokstr_: + output_msg("STR$"); + break; + + case tokval: + output_msg("VAL"); + break; + + case tokchr_: + output_msg("CHR$"); + break; + + case tokasc: + output_msg("ASC"); + break; + + case toklen: + output_msg("LEN"); + break; + + case tokmid_: + output_msg("MID$"); + break; + + case tokpeek: + output_msg("PEEK"); + break; + + case tokrem: + output_msg(PhreeqcPtr->sformatf("REM%s", l_buf->UU.sp)); + break; + + case toklet: + output_msg("LET"); + break; + + case tokinput: + output_msg("INPUT"); + break; + + case tokgoto: + output_msg("GOTO"); + break; + + case tokif: + output_msg("IF"); + break; + + case tokend: + output_msg("END"); + break; + + case tokstop: + output_msg("STOP"); + break; + + case tokfor: + output_msg("FOR"); + break; + + case toknext: + output_msg("NEXT"); + break; + + case tokwhile: + output_msg("WHILE"); + break; + + case tokwend: + output_msg("WEND"); + break; + + case tokgosub: + output_msg("GOSUB"); + break; + + case tokreturn: + output_msg("RETURN"); + break; + + case tokread: + output_msg("READ"); + break; + + case tokdata: + output_msg("DATA"); + break; + + case tokrestore: + output_msg("RESTORE"); + break; + + case tokgotoxy: + output_msg("GOTOXY"); + break; + + case tokon: + output_msg("ON"); + break; + + case tokdim: + output_msg("DIM"); + break; + + case tokpoke: + output_msg("POKE"); + break; + + case toklist: + output_msg("LIST"); + break; + + case tokrun: + output_msg("RUN"); + break; + + case toknew: + output_msg("NEW"); + break; + + case tokload: + output_msg("LOAD"); + break; + + case tokmerge: + output_msg("MERGE"); + break; + + case tokbye: + output_msg("BYE"); + break; + + case tokdel: + output_msg("DEL"); + break; + + case tokrenum: + output_msg("RENUM"); + break; + + case tokthen: + output_msg(" THEN "); + break; + + case tokelse: + output_msg(" ELSE "); + break; + + case tokto: + output_msg(" TO "); + break; + case tokstep: + output_msg(" STEP "); + break; +/* +* PHREEQC functions +*/ + case tokact: + output_msg("ACT"); + break; + case tokadd_heading: + output_msg("ADD_HEADING"); + break; + case tokalk: + output_msg("ALK"); + break; + case tokaphi: + output_msg("APHI"); // mole volume of a phase + break; + case tokcalc_value: + output_msg("CALC_VALUE"); + break; + case tokceil: + output_msg("CEIL"); + break; + case tokcell_no: + output_msg("CELL_NO"); + break; + case tokchange_por: + output_msg("CHANGE_POR"); + break; + case tokchange_surf: + output_msg("CHANGE_SURF"); + break; + case tokcharge_balance: + output_msg("CHARGE_BALANCE"); + break; + case tokcurrent_a: + output_msg("CURRENT_A"); + break; + case tokdebye_length: + output_msg("DEBYE_LENGTH"); // Debye-Hueckel length + break; + case tokdelta_h_phase: + output_msg("DELTA_H_PHASE"); + break; + case tokdelta_h_species: + output_msg("DELTA_H_SPECIES"); + break; + case tokdescription: + output_msg("DESCRIPTION"); + break; + case tokdh_a: + output_msg("DH_A"); // Debye-Hueckel A + break; + case tokdh_a0: + output_msg("DH_A0"); + break; + case tokdh_av: + output_msg("DH_Av"); // Debye-Hueckel Av + break; + case tokdh_b: + output_msg("DH_B"); // Debye-Hueckel B + break; + case tokdh_bdot: + output_msg("DH_BDOT"); + break; + case tokdiff_c: + output_msg("DIFF_C"); + break; + case tokdist: + output_msg("DIST"); + break; + case tokedl: + output_msg("EDL"); + break; + case tokedl_species: + output_msg("EDL_SPECIES"); + break; + case tokeol_: + output_msg("EOL$"); + break; + case tokeol_notab_: + output_msg("EOL_NOTAB$"); + break; + case tokeps_r: + output_msg("EPS_R"); // dielectric constant + break; + case tokeq_frac: + case tokequiv_frac: + output_msg("EQ_FRAC"); + break; + case tokequi: + output_msg("EQUI"); + break; + case tokequi_delta: + output_msg("EQUI_DELTA"); + break; + case tokerase: + output_msg("ERASE"); + break; + case tokexists: + output_msg("EXISTS"); + break; + case tokfloor: + output_msg("FLOOR"); + break; + case tokgamma: + output_msg("GAMMA"); + break; + case tokgas: + output_msg("GAS"); + break; + case tokgas_p: + output_msg("GAS_P"); + break; + case tokgas_vm: + output_msg("GAS_VM"); + break; + case tokget: + output_msg("GET"); + break; + case tokget_por: + output_msg("GET_POR"); + break; + case tokgfw: + output_msg("GFW"); // gram formula weight of a formula + break; +#if defined MULTICHART + case tokgraph_x: + output_msg("GRAPH_X"); + break; + case tokgraph_y: + output_msg("GRAPH_Y"); + break; + case tokgraph_sy: + output_msg("GRAPH_SY"); + break; +#endif + case tokinstr: + output_msg("INSTR"); + break; + case tokiso: + output_msg("ISO"); + break; + case tokiso_unit: + output_msg("ISO_UNIT"); + break; + case tokiterations: + output_msg("ITERATIONS"); + break; + case tokkappa: + output_msg("KAPPA"); // compressibility of pure water, d(rho)/d(P) / rho + break; + case tokkin: + output_msg("KIN"); + break; + case tokkin_delta: + output_msg("KIN_DELTA"); + break; + case tokkin_time: + output_msg("KIN_TIME"); + break; + case tokkinetics_formula: + case tokkinetics_formula_: + output_msg("KINETICS_FORMULA$"); + break; + case tokla: + output_msg("LA"); + break; + case toklg: + output_msg("LG"); + break; + case toklist_s_s: + output_msg("LIST_S_S"); + break; + case toklk_named: + output_msg("LK_NAMED"); + break; + case toklk_phase: + output_msg("LK_PHASE"); + break; + case toklk_species: + output_msg("LK_SPECIES"); + break; + case toklm: + output_msg("LM"); + break; + case tokltrim: + output_msg("LTRIM"); + break; + case tokm: + output_msg("M"); + break; + case tokm0: + output_msg("M0"); + break; + case tokmcd_jtot: + output_msg("MCD_JTOT"); + break; + case tokmcd_jconc: + output_msg("MCD_JCONC"); + break; + case tokmisc1: + output_msg("MISC1"); + break; + case tokmisc2: + output_msg("MISC2"); + break; + case tokmol: + output_msg("MOL"); + break; + case tokmu: + output_msg("MU"); + break; + case tokno_newline_: + output_msg("NO_NEWLINE$"); + break; + case tokosmotic: + output_msg("OSMOTIC"); + break; + case tokpad_: + case tokpad: + output_msg("PAD"); + break; + case tokparm: + output_msg("PARM"); + break; + case tokpercent_error: + output_msg("PERCENT_ERROR"); + break; + case tokphase_formula: + case tokphase_formula_: + output_msg("PHASE_FORMULA$"); + break; + case tokphase_vm: + output_msg("PHASE_VM"); // mole volume of a phase + break; +#if defined MULTICHART + case tokplot_xy: + output_msg("PLOT_XY"); + break; +#endif + case tokpot_v: + output_msg("POT_V"); + break; + case tokpr_p: + output_msg("PR_P"); + break; + case tokpr_phi: + output_msg("PR_PHI"); + break; + case tokpressure: + output_msg("PRESSURE"); + break; + case tokprint: + output_msg("PRINT"); + break; + case tokpunch: + output_msg("PUNCH"); + break; + case tokput: + output_msg("PUT"); + break; + case tokqbrn: + output_msg("QBrn"); // Q_Born, d(eps_r)/d(P)/(eps_r^2) + break; + case tokrho: + output_msg("RHO"); + break; + case tokrho_0: + output_msg("RHO_0"); + break; + case tokrtrim: + output_msg("RTRIM"); + break; + case tokrxn: + output_msg("RXN"); + break; + case toks_s: + output_msg("S_S"); + break; + case toksave: + output_msg("SAVE"); + break; + case toksc: + output_msg("SC"); + break; + case toksetdiff_c: + output_msg("SETDIFF_C"); + break; + case toksi: + output_msg("SI"); + case toksim_no: + output_msg("SIM_NO"); + break; + case toksim_time: + output_msg("SIM_TIME"); + break; + case toksoln_vol: + output_msg("SOLN_VOL"); // volume of solution + break; + case tokspecies_formula: + case tokspecies_formula_: + output_msg("SPECIES_FORMULA$"); + break; + case toksr: + output_msg("SR"); + break; + case tokstep_no: + output_msg("STEP_NO"); + break; + case tokstr_e_: + output_msg("STR_E$"); + break; + case tokstr_f_: + output_msg("STR_F$"); + break; + case toksum_gas: + output_msg("SUM_GAS"); + break; + case toksum_s_s: + output_msg("SUM_s_s"); + break; + case toksum_species: + output_msg("SUM_SPECIES"); + case toksurf: + output_msg("SURF"); + break; + case toksys: + output_msg("SYS"); + break; + case tokt_sc: + output_msg("T_SC"); + break; + case toktc: + output_msg("TC"); + break; + case toktime: + output_msg("TIME"); + break; + case toktitle: + output_msg("TITLE"); + break; + case toktk: + output_msg("TK"); + break; + case toktot: + output_msg("TOT"); + break; + case toktotal_time: + output_msg("TOTAL_TIME"); + break; + case toktotmole: + case toktotmol: + case toktotmoles: + output_msg("TOTMOLE"); + break; + case toktrim: + output_msg("TRIM"); + break; + case tokviscos: + output_msg("VISCOS"); + break; + case tokviscos_0: + output_msg("VISCOS_0"); + break; + case tokvm: + output_msg("VM"); // mole volume of aqueous solute + break; +/* +* End PHREEQC functions +*/ + case toksa_declercq: // Undocumented function + output_msg("SA_DECLERCQ"); + break; + case tokcallback: // PHAST function + output_msg("CALLBACK"); + break; + case tokcell_pore_volume: // PHAST function + case tokporevolume: + output_msg("POREVOLUME"); + break; + case tokcell_porosity: // PHAST function + output_msg("CELL_POROSITY"); + break; + case tokcell_saturation: // PHAST function + output_msg("CELL_SATURATION"); + break; + case tokcell_volume: // PHAST function + output_msg("CELL_VOLUME"); + break; + case toktransport_cell_no: // PHAST function + output_msg("TRANSPORT_CELL_NO"); + break; + case tokvelocity_x: // PHAST function + output_msg("VELOCITY_X"); + break; + case tokvelocity_y: // PHAST function + output_msg("VELOCITY_Y"); + break; + case tokvelocity_z: // PHAST function + output_msg("VELOCITY_Z"); + break; + case toklog10: + output_msg("LOG10"); + break;; + } + l_buf = l_buf->next; + } +} + +void PBasic:: +disposetokens(tokenrec ** tok) +{ + tokenrec *tok1; + + while (*tok != NULL) + { + tok1 = (*tok)->next; + if (phreeqci_gui) + { + if ((*tok)->kind == (long) toknum) + { + PhreeqcPtr->PHRQ_free((*tok)->sz_num); + } +#ifdef _DEBUG + else + { + _ASSERTE((*tok)->sz_num == NULL); + } +#endif /* _DEBUG */ + } + if ((*tok)->kind == (long) tokrem || (*tok)->kind == (long) tokstr) + { + (*tok)->UU.sp = (char *) PhreeqcPtr->free_check_null((*tok)->UU.sp); + } + *tok = (tokenrec *) PhreeqcPtr->free_check_null(*tok); + *tok = tok1; + } +} + +void PBasic:: +parseinput(tokenrec ** l_buf) +{ + linerec *l, *l0, *l1; + + while (PhreeqcPtr->replace("\t", " ", inbuf)); + while (PhreeqcPtr->replace("\r", " ", inbuf)); + PhreeqcPtr->string_trim(inbuf); + curline = 0; + while (*inbuf != '\0' && isdigit((int) inbuf[0])) + { + curline = curline * 10 + inbuf[0] - 48; + memmove(inbuf, inbuf + 1, strlen(inbuf)); + } + parse(inbuf, l_buf); + if (curline == 0) + return; + l = linebase; + l0 = NULL; + while (l != NULL && l->num < curline) + { + l0 = l; + l = l->next; + } + if (l != NULL && l->num == curline) + { + l1 = l; + l = l->next; + if (l0 == NULL) + linebase = l; + else + l0->next = l; + disposetokens(&l1->txt); + PhreeqcPtr->PHRQ_free(l1); + } + if (*l_buf != NULL) + { + l1 = (linerec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(linerec)); + if (l1 == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + l1->next = l; + if (l0 == NULL) + linebase = l1; + else + l0->next = l1; + l1->num = curline; + l1->txt = *l_buf; + strncpy(l1->inbuf, inbuf, MAX_LINE); + l1->inbuf[MAX_LINE-1] = '\0'; + } + clearloops(); + restoredata(); +} + +void PBasic:: +errormsg(const char * l_s) +{ + if (phreeqci_gui) + { + /* set nIDErrPrompt before calling errormsg see snerr */ + _ASSERTE(nIDErrPrompt != 0); + } + else + { + error_msg(l_s, CONTINUE); + } + _Escape(42); +} + +void PBasic:: + snerr(const char * l_s) +{ + char str[MAX_LENGTH] = {0}; + strcpy(str, "Syntax_error "); + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_SYNTAX; + } + strcat(str, l_s); + strcat(str, " in line: "); + if (strcmp(inbuf, "run")) + strcat(str, inbuf); + errormsg(str); +} + +void PBasic:: + tmerr(const char * l_s) +{ + char str[MAX_LENGTH] = {0}; + strcpy(str, "Type mismatch error"); + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_MISMATCH; + } + strcat(str, l_s); + strcat(str, " in line: "); + if (strcmp(inbuf, "run")) + strcat(str, inbuf); + errormsg(str); +} + +void PBasic:: + badsubscr(void) +{ + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_BAD_SUBSCRIPT; + } + errormsg("Bad subscript"); +} + +LDBLE PBasic:: +realfactor(struct LOC_exec *LINK) +{ + valrec n; + n = factor(LINK); + if (n.stringval) + tmerr(": found characters, not a number"); + return (n.UU.val); +} + +char * PBasic:: +strfactor(struct LOC_exec * LINK) +{ + valrec n; + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + return (n.UU.sval); +} + +char * PBasic:: +stringfactor(char * Result, struct LOC_exec * LINK) +{ + valrec n; + + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + strcpy(Result, n.UU.sval); + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result; +} + +const char * PBasic:: +stringfactor(std::string & Result, struct LOC_exec * LINK) +{ + valrec n; + + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + Result = n.UU.sval; + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result.c_str(); +} + +long PBasic:: +intfactor(struct LOC_exec *LINK) +{ + return ((long) floor(realfactor(LINK) + 0.5)); +} + +LDBLE PBasic:: +realexpr(struct LOC_exec *LINK) +{ + valrec n; + + n = expr(LINK); + if (n.stringval) + tmerr(": found characters, not a number"); + return (n.UU.val); +} + +char * PBasic:: +strexpr(struct LOC_exec * LINK) +{ + valrec n; + + n = expr(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + return (n.UU.sval); +} + +char * PBasic:: +stringexpr(char * Result, struct LOC_exec * LINK) +{ + valrec n; + + n = expr(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + strcpy(Result, n.UU.sval); + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result; +} + +long PBasic:: +intexpr(struct LOC_exec *LINK) +{ + return ((long) floor(realexpr(LINK) + 0.5)); +} + +void PBasic:: +require(int k, struct LOC_exec *LINK) +{ + char str[MAX_LENGTH] = {0}; + if (LINK->t == NULL || LINK->t->kind != k) + { + std::map::const_iterator item; + for (item = command_tokens.begin(); item != command_tokens.end(); item++) + { + if (item->second == k) + break; + } + + if (item == command_tokens.end()) + snerr(": missing unknown command"); + else { + strcpy(str, ": missing "); + snerr(strcat(str, item->first.c_str())); + } +#if !defined(R_SO) + exit(4); +#endif + } + LINK->t = LINK->t->next; +} + + +void PBasic:: +skipparen(struct LOC_exec *LINK) +{ + do + { + if (LINK->t == NULL) + { + snerr(": parenthesis missing"); +#if !defined(R_SO) + exit(4); +#endif + } + if (LINK->t->kind == tokrp || LINK->t->kind == tokcomma) + goto _L1; + if (LINK->t->kind == toklp) + { + LINK->t = LINK->t->next; + skipparen(LINK); + } + LINK->t = LINK->t->next; + } + while (true); + _L1:; +} + +varrec * PBasic:: +findvar(struct LOC_exec *LINK) +{ + varrec *v; + long i, j, k; + tokenrec *tok; + long FORLIM; + + if (LINK->t == NULL || LINK->t->kind != tokvar) + { + snerr(": can`t find variable"); +#if !defined(R_SO) + exit(4); +#endif + } + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != toklp) + { + if (v->numdims != 0) + badsubscr(); + return v; + } + if (v->numdims == 0) + { + tok = LINK->t; + i = 0; + j = 1; + do + { + if (i >= maxdims) + badsubscr(); + LINK->t = LINK->t->next; + skipparen(LINK); + j *= 11; + i++; + v->dims[i - 1] = 11; + } + while (LINK->t->kind != tokrp); + v->numdims = (char) i; + if (v->stringvar) + { + v->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc(j * sizeof(char *)); + if (v->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + for (k = 0; k < j; k++) + v->UU.U1.sarr[k] = NULL; + } + else + { + v->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc(j * sizeof(LDBLE)); + if (v->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + for (k = 0; k < j; k++) + v->UU.U0.arr[k] = 0.0; + } + LINK->t = tok; + } + k = 0; + LINK->t = LINK->t->next; + FORLIM = v->numdims; + for (i = 1; i <= FORLIM; i++) + { + j = intexpr(LINK); + if ((unsigned long) j >= (unsigned long) v->dims[i - 1]) + badsubscr(); + k = k * v->dims[i - 1] + j; + if (i < v->numdims) + require(tokcomma, LINK); + } + require(tokrp, LINK); + if (v->stringvar) + v->UU.U1.sval = &v->UU.U1.sarr[k]; + else + v->UU.U0.val = &v->UU.U0.arr[k]; + return v; +} + +valrec PBasic:: +factor(struct LOC_exec * LINK) +{ + char string[MAX_LENGTH] = {0}; + cxxSolution *soln_ptr; + varrec *v; + tokenrec *facttok; + valrec n; + + long i, j, m; + tokenrec *tok, *tok1; + char *l_s; + LDBLE l_dummy; + int i_rate; + union + { + long i; + char *c; + } trick; + LDBLE TEMP; + std::string STR1, STR2; + const char *elt_name, *surface_name, *mytemplate, *name; + varrec *count_varrec = NULL, *names_varrec = NULL, *types_varrec = + NULL, *moles_varrec = NULL; + char **names_arg, **types_arg; + LDBLE *moles_arg; + int arg_num; + LDBLE count_species; + const char *string1, *string2; + + if (LINK->t == NULL) + snerr(": missing variable or command"); + facttok = LINK->t; + LINK->t = LINK->t->next; + n.stringval = false; + switch (facttok->kind) + { + + case toknum: + n.UU.val = facttok->UU.num; + break; + + case tokstr: + n.stringval = true; + m = (int)strlen(facttok->UU.sp) + 1; + if (m < 256) + m = 256; + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, facttok->UU.sp); + break; + + case tokvar: + LINK->t = facttok; + v = findvar(LINK); + n.stringval = v->stringvar; + if (n.stringval) + { + if (*v->UU.U1.sval != NULL) + { + m = (int)strlen(*v->UU.U1.sval) + 1; + if (m < 256) + m = 256; + } + else + { + m = 256; + } + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + if (*v->UU.U1.sval != NULL) + { + strcpy(n.UU.sval, *v->UU.U1.sval); + } + + } + else + n.UU.val = *v->UU.U0.val; + break; + + case toklp: + n = expr(LINK); + require(tokrp, LINK); + break; + + case tokminus: + n.UU.val = -realfactor(LINK); + break; + + case tokplus: + { + n.UU.val = realfactor(LINK); + } + break; + + case toknot: + { + n.UU.val = ~intfactor(LINK); + } + break; + + case toksqr: + { + TEMP = realfactor(LINK); + n.UU.val = TEMP * TEMP; + } + break; + + case toksqrt: + { + n.UU.val = sqrt(realfactor(LINK)); + } + break; + /* + * PHREEQC functions============================================= + */ + case tokact: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->activity(str); + } + break; + + case tokadd_heading: + { + require(toklp, LINK); + name = strexpr(LINK); + require(tokrp, LINK); + if (PhreeqcPtr->current_user_punch != NULL) + { + PhreeqcPtr->current_user_punch->Get_headings().push_back(name); + n.UU.val = (parse_all) ? 1 : (double)PhreeqcPtr->current_user_punch->Get_headings().size(); + } + else { + n.UU.val = 0; + } + name = (char*)PhreeqcPtr->free_check_null((void*) name); + } + break; + + case tokalk: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total_alkalinity / PhreeqcPtr->mass_water_aq_x; + } + break; + + case tokaphi: + { + n.UU.val = PhreeqcPtr->A0; + } + break; + + case tokcalc_value: + { + require(toklp, LINK); + name = stringfactor(STR1, LINK); + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->get_calculate_value(name); + } + break; + + case tokceil: + { + n.UU.val = ceil(realfactor(LINK)); + } + break; + + case tokcell_no: + { + if (parse_all) + { + n.UU.val = 1; + break; + } + n.UU.val = PhreeqcPtr->solution_number(); + } + break; + + case tokcharge_balance: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->cb_x; + } + break; + + case tokcurrent_a: + { + //n.UU.val = (parse_all) ? 1 : PhreeqcPtr->current_x; + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->current_A; + } + break; + + case tokdebye_length: + { + double debye_length = (PhreeqcPtr->eps_r * EPSILON_ZERO * R_KJ_DEG_MOL * 1000.0 * PhreeqcPtr->tk_x) + / (2. * F_C_MOL * F_C_MOL * PhreeqcPtr->mu_x * 1000.); + n.UU.val = sqrt(debye_length); + } + break; + + case tokdelta_h_phase: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_deltah_p(str); + } + break; + + case tokdelta_h_species: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_deltah_s(str); + } + break; + + case tokdescription: + { + n.stringval = true; + if (PhreeqcPtr->state == REACTION) + { + if (PhreeqcPtr->use.Get_mix_in()) + { + sprintf(string, "Mix %d", PhreeqcPtr->use.Get_n_mix_user()); + n.UU.sval = PhreeqcPtr->string_duplicate(string); + } + else + { + soln_ptr = Utilities::Rxn_find(PhreeqcPtr->Rxn_solution_map, + PhreeqcPtr->use.Get_n_solution_user()); + if (soln_ptr != NULL) + { + n.UU.sval = PhreeqcPtr->string_duplicate(soln_ptr->Get_description().c_str()); + } + else + { + n.UU.sval = PhreeqcPtr->string_duplicate("Unknown"); + } + } + } + else if (PhreeqcPtr->state == ADVECTION || PhreeqcPtr->state == TRANSPORT || PhreeqcPtr->state == PHAST) + { + sprintf(string, "Cell %d", PhreeqcPtr->cell_no); + n.UU.sval = PhreeqcPtr->string_duplicate(string); + } + else + { + if (PhreeqcPtr->use.Get_solution_ptr() != NULL) + { + n.UU.sval = PhreeqcPtr->string_duplicate(PhreeqcPtr->use.Get_solution_ptr()->Get_description().c_str()); + } + else + { + n.UU.sval = PhreeqcPtr->string_duplicate("Unknown"); + } + } + while (PhreeqcPtr->replace("\t", " ", n.UU.sval)); + } + break; + + case tokdh_a: + { + if (PhreeqcPtr->llnl_temp.size() > 0) + { + n.UU.val = PhreeqcPtr->a_llnl; + } + else + { + n.UU.val = PhreeqcPtr->DH_A; + } + } + break; + + case tokdh_a0: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->dh_a0(str); + } + break; + + case tokdh_av: + { + n.UU.val = PhreeqcPtr->DH_Av; + } + break; + + case tokdh_b: + { + if (PhreeqcPtr->llnl_temp.size() > 0) + { + n.UU.val = PhreeqcPtr->b_llnl; + } + else + { + n.UU.val = PhreeqcPtr->DH_B; + } + } + break; + + case tokdh_bdot: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->dh_bdot(str); + } + break; + + case tokdiff_c: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->diff_c(str); + } + break; + + case tokdist: + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = 0; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->cell_data[PhreeqcPtr->cell].mid_cell_x; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = (parse_all) ? 1 : (LDBLE)PhreeqcPtr->use.Get_n_solution_user(); + } + else + { + n.UU.val = 0; + } + } + break; + + case tokedl: + { + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + surface_name = stringfactor(STR2, LINK); + } + else + { + surface_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->diff_layer_total(elt_name, surface_name); + } + break; + + case tokedl_species: + { + double area = 0.0, thickness = 0.0; + require(toklp, LINK); + const char* surf_name = stringfactor(STR1, LINK); + require(tokcomma, LINK); + // variable for number of species + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !count_varrec || count_varrec->stringvar != 0) + { + snerr(": Missing or wrong type count variable."); +#if !defined(R_SO) + exit(4); +#endif + } + // variable for species names + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !names_varrec || names_varrec->stringvar != 1) + { + snerr(": Missing or wrong type name variable."); +#if !defined(R_SO) + exit(4); +#endif + } + // variable for species concentrations + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": Missing or wrong type moles variable."); + // variable for area + LINK->t = LINK->t->next; + require(tokcomma, LINK); + varrec* area_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || area_varrec->stringvar != 0) + snerr(": Missing or wrong type area varaiable."); + // varaiable for thickness + LINK->t = LINK->t->next; + require(tokcomma, LINK); + varrec* thickness_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || thickness_varrec->stringvar != 0) + snerr(": Missing or wrong type thickness variable."); + LINK->t = LINK->t->next; + require(tokrp, LINK); + + free_dim_stringvar(names_varrec); + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + + // Call subroutine + if (parse_all) + { + PhreeqcPtr->sys_tot = 0; + //PhreeqcPtr->count_sys = 1000; + //int count_sys = PhreeqcPtr->count_sys; + size_t count_sys = 1000; + names_arg = (char**)PhreeqcPtr->PHRQ_calloc((count_sys + 1), sizeof(char*)); + if (names_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_arg = (LDBLE*)PhreeqcPtr->PHRQ_calloc((count_sys + 1), sizeof(LDBLE)); + if (moles_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + names_arg[0] = NULL; + moles_arg[0] = 0; + count_species = (LDBLE)count_sys; + n.UU.val = 0; + } + else + { + //n.UU.val = PhreeqcPtr->system_total(elt_name, &count_species, &(names_arg), + // &(types_arg), &(moles_arg)); + n.UU.val = PhreeqcPtr->edl_species(surf_name, &count_species, &(names_arg), &(moles_arg), &area, &thickness); + } + /* + * fill in varrec structures + */ + *count_varrec->UU.U0.val = count_species; + names_varrec->UU.U1.sarr = names_arg; + moles_varrec->UU.U0.arr = moles_arg; + *area_varrec->UU.U0.val = area; + *thickness_varrec->UU.U0.val = thickness; + + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + names_varrec->dims[0] = (long)(*count_varrec->UU.U0.val) + 1; + moles_varrec->dims[0] = (long)(*count_varrec->UU.U0.val) + 1; + names_varrec->numdims = 1; + moles_varrec->numdims = 1; + } + break; + + case tokeol_: + { + n.stringval = true; + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, "\n"); + } + break; + + case tokeol_notab_: + { + n.stringval = true; + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, "\n"); + punch_tab = false; + } + break; + + case tokeps_r: + { + n.UU.val = PhreeqcPtr->eps_r; + } + break; + + case tokeq_frac: + case tokequiv_frac: + { + // left parenthesis + require(toklp, LINK); + + // species name + std::string species_name(stringfactor(STR1, LINK)); + + require(tokcomma, LINK); + + // equivalents + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find equivalents variable"); + + LINK->t = LINK->t->next; + require(tokcomma, LINK); + + // exchange or surface element + varrec* elt_varrec = NULL; + elt_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elt_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + free_dim_stringvar(elt_varrec); + *elt_varrec->UU.U1.sval = (char*)PhreeqcPtr->free_check_null(*elt_varrec->UU.U1.sval); + + // right parenthesis + LINK->t = LINK->t->next; + require(tokrp, LINK); + + // set function value + LDBLE eq; + std::string elt_name; + + // return equivalent fraction + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equivalent_fraction(species_name.c_str(), &eq, elt_name); + + // set equivalents + *count_varrec->UU.U0.val = (parse_all) ? 1 : eq; + + // set element name + size_t l = elt_name.size(); + l = l < 256 ? 256 : l + 1; + char* token = (char*)PhreeqcPtr->PHRQ_malloc(l * sizeof(char)); + strcpy(token, elt_name.c_str()); + *elt_varrec->UU.U1.sval = token; + } + break; + + case tokequi: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equi_phase(str); + } + break; + + case tokequi_delta: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equi_phase_delta(str); + } + break; + + case tokexists: + { + std::ostringstream oss; + require(toklp, LINK); + + /* get first subscript */ + if (LINK->t != NULL && LINK->t->kind != tokrp) + { + i = intexpr(LINK); + oss << i << ","; + } + + /* get other subscripts */ + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + oss << j << ","; + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + if (parse_all) + { + n.UU.val = 1; + } + else + { + std::map::iterator it = PhreeqcPtr->save_values.find(oss.str()); + n.UU.val = (it == PhreeqcPtr->save_values.end()) ? 0 : 1; + } + } + break; + + case tokfloor: + { + n.UU.val = floor(realfactor(LINK)); + } + break; + + case tokmcd_jtot: + { + double f = 0.0; + const char* str = stringfactor(STR1, LINK); + if (PhreeqcPtr->state == TRANSPORT && PhreeqcPtr->multi_Dflag) + { + f = PhreeqcPtr->flux_mcd(str, 1); + } + n.UU.val = (parse_all) ? 1 : f; + } + break; + case tokmcd_jconc: + { + double f = 0.0; + const char* str = stringfactor(STR1, LINK); + if (PhreeqcPtr->state == TRANSPORT && PhreeqcPtr->multi_Dflag) + { + f = PhreeqcPtr->flux_mcd(str, 2); + } + n.UU.val = (parse_all) ? 1 : f; + } + break; + + case tokgamma: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->activity_coefficient(str); + } + break; + + case tokgas: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_comp(str); + } + break; + + case tokgas_p: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_p(); + } + break; + + case tokgas_vm: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_vm(); + } + break; + + case tokget: + { + std::ostringstream oss; + require(toklp, LINK); + + /* get first subscript */ + if (LINK->t != NULL && LINK->t->kind != tokrp) + { + i = intexpr(LINK); + oss << i << ","; + } + + /* get other subscripts */ + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + oss << j << ","; + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + if (parse_all) + { + n.UU.val = 1; + } + else + { + std::map::iterator it = PhreeqcPtr->save_values.find(oss.str()); + n.UU.val = (it == PhreeqcPtr->save_values.end()) ? 0 : it->second; + } + break; + } + + case tokget_por: + { + i = intfactor(LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + if (PhreeqcPtr->phast != TRUE) + { + if (i <= 0 || i > PhreeqcPtr->count_cells * (1 + PhreeqcPtr->stag_data.count_stag) + 1 + || i == PhreeqcPtr->count_cells + 1) + { + /* warning_msg("Note... no porosity for boundary solutions."); */ + n.UU.val = 0; + break; + } + else + n.UU.val = PhreeqcPtr->cell_data[i].por; + break; + } + else + { + n.UU.val = PhreeqcPtr->cell_porosity; + break; + } + } + } + break; + + case tokgfw: + { + const char* str = stringfactor(STR1, LINK); + LDBLE gfw; + PhreeqcPtr->compute_gfw(str, &gfw); + n.UU.val = (parse_all) ? 1 : gfw; + } + break; + + case tokinstr: + { + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokcomma, LINK); + string2 = stringfactor(STR2, LINK); + require(tokrp, LINK); + { + const char* cptr = strstr(string1, string2); + if (cptr == NULL) + { + n.UU.val = 0; + } + else + { + n.UU.val = ((LDBLE)(cptr - string1)) + 1; + } + } + } + break; + + case tokiso: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->iso_value(str); + } + break; + + case tokiso_unit: + { + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim(STR1); + n.UU.sval = (parse_all) ? PhreeqcPtr->string_duplicate("unknown") : PhreeqcPtr->iso_unit(STR1.c_str()); + } + break; + + case tokiterations: + { + n.UU.val = (parse_all) ? 0 : PhreeqcPtr->overall_iterations; + } + break; + + case tokkappa: + { + n.UU.val = PhreeqcPtr->kappa_0; + } + break; + + case tokkin: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->kinetics_moles(str); + } + break; + + case tokkin_delta: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->kinetics_moles_delta(str); + } + break; + + case tokkin_time: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->rate_kin_time; + } + break; + + case tokkinetics_formula: + case tokkinetics_formula_: + { + require(toklp, LINK); + std::string kinetics_name(stringfactor(STR1, LINK)); + varrec* elts_varrec = NULL, * coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* kinetics_formula("calcite", count, elt, coef) */ + /* return formula */ + /*int c; */ + /* struct varrec *count_varrec, *names_varrec, *types_varrec, *moles_varrec; */ + /* struct varrec *count_varrec, *elt_varrec, *coef_varrec; */ + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of elements */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + std::string form = PhreeqcPtr->kinetics_formula(kinetics_name, stoichiometry); + + // put formula as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(form.c_str()); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE)count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char**)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char*)); + if (elts_varrec->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + coef_varrec->UU.U0.arr = (LDBLE*)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long)(count + 1); + coef_varrec->dims[0] = (long)(count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + } + } + break; + + case tokla: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_activity(str); + } + break; + + case toklg: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_activity_coefficient(str); + } + break; + + case toklist_s_s: + { + /* list_s_s("calcite", count, name$, moles) */ + /* return total moles */ + require(toklp, LINK); + std::string s_s_name(stringfactor(STR1, LINK)); + cxxNameDouble composition; + /* + * Parse arguments + */ + arg_num = -1; + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of components */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || names_varrec->stringvar != 1) + snerr(": Cannot find component string variable"); + + /* return number of moles of components */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": Cannot find moles of component variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + snerr(": Expected 4 arguments for list_s_s"); +#if !defined(R_SO) + exit(4); +#endif + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(names_varrec); + if (moles_varrec) + { + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + } + } + /* + * Call subroutine + */ + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->list_ss(s_s_name, composition); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = composition.size(); + *count_varrec->UU.U0.val = (LDBLE)count; + /* + * malloc space + */ + names_varrec->UU.U1.sarr = (char**)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char*)); + if (names_varrec->UU.U1.sarr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_varrec->UU.U0.arr = (LDBLE*)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (moles_varrec->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + + // first position not used + names_varrec->UU.U1.sarr[0] = NULL; + moles_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + names_varrec->dims[0] = (long)(count + 1); + moles_varrec->dims[0] = (long)(count + 1); + names_varrec->numdims = 1; + moles_varrec->numdims = 1; + + // fill in arrays + i = 1; + std::vector< std::pair > sort_comp = composition.sort_second(); + size_t j; + for (j = 0; j != sort_comp.size(); j++) + { + names_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate(sort_comp[j].first.c_str()); + moles_varrec->UU.U0.arr[i] = sort_comp[j].second; + i++; + } + + } + } + break; + + case toklk_named: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_n(str); + } + break; + + case toklk_phase: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_p(str); + } + break; + + case toklk_species: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_s(str); + } + break; + + case toklm: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_molality(str); + } + break; + + case tokltrim: + { + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim_left(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + } + break; + + case tokm: + { + n.UU.val = PhreeqcPtr->rate_m; + } + break; + + case tokm0: + { + n.UU.val = PhreeqcPtr->rate_m0; + } + break; + + case tokmisc1: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_misc1(str); + } + break; + + case tokmisc2: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_misc2(str); + } + break; + + case tokmol: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->molality(str); + } + break; + + case tokmu: + { + n.UU.val = PhreeqcPtr->mu_x; + } + break; + + case tokno_newline_: + { + n.stringval = true; + PhreeqcPtr->Set_output_newline(false); + this->skip_punch = true; + } + break; + + case tokosmotic: + { + if (PhreeqcPtr->pitzer_model == TRUE || PhreeqcPtr->sit_model == TRUE) + { + n.UU.val = PhreeqcPtr->COSMOT; + } + else + { + n.UU.val = 0.0; + } + } + break; + + case tokpad_: + case tokpad: + { + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokcomma, LINK); + i = intexpr(LINK); + require(tokrp, LINK); + n.UU.sval = PhreeqcPtr->string_pad(string1, i); + } + break; + + case tokparm: + { + i_rate = intfactor(LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + if (i_rate > PhreeqcPtr->count_rate_p || i_rate == 0) + { + errormsg("Parameter subscript out of range."); + } + n.UU.val = PhreeqcPtr->rate_p[(size_t)i_rate - 1]; + } + } + break; + + case tokpercent_error: + { + n.UU.val = (parse_all) ? 1 : 100 * PhreeqcPtr->cb_x / PhreeqcPtr->total_ions_x; + } + break; + + case tokphase_formula: + case tokphase_formula_: + { + require(toklp, LINK); + std::string phase_name(stringfactor(STR1, LINK)); + varrec* elts_varrec = NULL, * coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* phase_formula("calcite", count, elt, coef) */ + /* return formula */ + /*int c; */ + /* struct varrec *count_varrec, *names_varrec, *types_varrec, *moles_varrec; */ + /* struct varrec *count_varrec, *elt_varrec, *coef_varrec; */ + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + std::string form = PhreeqcPtr->phase_formula(phase_name, stoichiometry); + + // put formula as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(form.c_str()); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE)count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char**)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char*)); + if (elts_varrec->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + coef_varrec->UU.U0.arr = (LDBLE*)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long)(count + 1); + coef_varrec->dims[0] = (long)(count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + + } + } + break; + + case tokphase_vm: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->phase_vm(str); + } + break; + + case tokpot_v: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->use.Get_solution_ptr()->Get_potV(); + } + break; + + case tokpr_p: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->pr_pressure(stringfactor(STR1, LINK)); + } + break; + + case tokpr_phi: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->pr_phi(stringfactor(STR1, LINK)); + } + break; + + case tokpressure: + { + n.UU.val = PhreeqcPtr->pressure(); + } + break; + + case tokqbrn: + { + n.UU.val = PhreeqcPtr->QBrn; + } + break; + + case tokrho: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_dens(); + } + break; + + case tokrho_0: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->rho_0; + } + break; + + case tokrtrim: + { + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim_right(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + } + break; + + case tokrxn: + { + if (PhreeqcPtr->state == REACTION || + PhreeqcPtr->state == ADVECTION || + PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->step_x; + } + else + { + n.UU.val = 0.0; + } + } + break; + + case toks_s: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_ss_comp(str); + } + break; + + case toksc: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_SC(); + } + break; + + case toksetdiff_c: + { + double d; + + require(toklp, LINK); + + const char* str = stringfactor(STR1, LINK); + require(tokcomma, LINK); + + // double arugument + d = realexpr(LINK); + require(tokrp, LINK); + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->setdiff_c(str, d); + + //PhreeqcPtr->PHRQ_free((void *) str); + } + break; + + case toksi: + { + const char* str = stringfactor(STR1, LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + PhreeqcPtr->saturation_index(str, &l_dummy, &n.UU.val); + } + } + break; + + case toksim_no: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->simulation; + } + break; + + case toksim_time: + { + if (!PhreeqcPtr->use.Get_kinetics_in()) + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = PhreeqcPtr->rate_sim_time; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->transport_step * PhreeqcPtr->timest; + } + else if (PhreeqcPtr->state == ADVECTION) + { + if (PhreeqcPtr->advection_kin_time_defined == TRUE) + { + n.UU.val = PhreeqcPtr->advection_step * PhreeqcPtr->advection_kin_time; + } + else + { + n.UU.val = PhreeqcPtr->advection_step; + } + } + else + { + n.UU.val = 0; + } + } + else + { + n.UU.val = PhreeqcPtr->rate_sim_time; + } + } + break; + + case toksoln_vol: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_solution_volume(); + } + break; + + case tokspecies_formula: + case tokspecies_formula_: + { + require(toklp, LINK); + std::string species_name(stringfactor(STR1, LINK)); + varrec* elts_varrec = NULL, * coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + require(tokcomma, LINK); + + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + + require(tokrp, LINK); + + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + /* + * Call subroutine + */ + std::string type = PhreeqcPtr->species_formula(species_name, stoichiometry); + + // put type as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(type.c_str()); + + /* + * fill in varrec structure + */ + + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE)count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char**)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char*)); + if (elts_varrec->UU.U1.sarr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + coef_varrec->UU.U0.arr = (LDBLE*)PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long)(count + 1); + coef_varrec->dims[0] = (long)(count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + } + break; + + case toksr: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->saturation_ratio(str); + } + break; + + case tokstep_no: + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = 0; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->transport_step; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = PhreeqcPtr->advection_step; + } + else if (PhreeqcPtr->state == REACTION) + { + n.UU.val = PhreeqcPtr->reaction_step; + } + else + { + n.UU.val = 0; + } + } + break; + + case tokstr_e_: + { + // left parenthesis + require(toklp, LINK); + + // set number + LDBLE nmbr; + nmbr = realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int length = (int)realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int width = (int)realexpr(LINK); + + // right parenthesis + require(tokrp, LINK); + + // Make work space + int max_length = length < 256 ? 256 : length; + char* token = (char*)PhreeqcPtr->PHRQ_calloc((max_length + 1), sizeof(char)); + if (token == NULL) PhreeqcPtr->malloc_error(); + + std::string std_num; + { + sprintf(token, "%*.*e", length, width, nmbr); + std_num = token; + } + + // set function value + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(std_num.size() + 2, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, std_num.c_str()); + n.stringval = true; + + // free work space + PhreeqcPtr->free_check_null(token); + } + break; + + case tokstr_f_: + { + // left parenthesis + require(toklp, LINK); + + // set number + LDBLE nmbr; + nmbr = realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int length = (int)realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int width = (int)realexpr(LINK); + + // right parenthesis + require(tokrp, LINK); + + // Make work space + int max_length = length < 256 ? 256 : length; + char* token = (char*)PhreeqcPtr->PHRQ_calloc((max_length + 1), sizeof(char)); + if (token == NULL) PhreeqcPtr->malloc_error(); + + std::string std_num; + { + sprintf(token, "%*.*f", length, width, nmbr); + std_num = token; + } + + // set function value + n.UU.sval = (char*)PhreeqcPtr->PHRQ_calloc(std_num.size() + 2, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, std_num.c_str()); + n.stringval = true; + + // free work space + PhreeqcPtr->free_check_null(token); + } + break; + + case toksum_gas: + { + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_gases(mytemplate, elt_name); + } + break; + + case toksum_s_s: + { + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_ss(mytemplate, elt_name); + } + break; + + case toksum_species: + { + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_species(mytemplate, elt_name); + } + break; + + case toksurf: + { + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + surface_name = stringfactor(STR2, LINK); + } + else + { + surface_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->surf_total(elt_name, surface_name); + } + break; + + case toksys: + { + int isort = 0; + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !count_varrec || count_varrec->stringvar != 0) + { + snerr(": can`t find variable"); +#if !defined(R_SO) + exit(4); +#endif + } + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !names_varrec || names_varrec->stringvar != 1) + { + snerr(": can`t find name of species"); +#if !defined(R_SO) + exit(4); +#endif + } + + /* return number of types of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + types_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || types_varrec->stringvar != 1) + snerr(": can`t find type of species"); + + /* return number of moles of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": can`t find moles of species"); + LINK->t = LINK->t->next; + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + isort = intexpr(LINK); + arg_num = 5; + } + else + { + arg_num = 4; + } + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(names_varrec); + free_dim_stringvar(types_varrec); + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + /* + n.UU.val = system_total(elt_name, count_varrec->UU.U0.val, &(names_varrec->UU.U1.sarr), &(types_varrec->UU.U1.sarr), &(moles_varrec->UU.U0.arr)); + */ + if (parse_all) + { + PhreeqcPtr->sys_tot = 0; + //PhreeqcPtr->count_sys = 1000; + //int count_sys = PhreeqcPtr->count_sys; + size_t count_sys = 1000; + names_arg = (char**)PhreeqcPtr->PHRQ_calloc((size_t)(count_sys + 1), sizeof(char*)); + if (names_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + types_arg = (char**)PhreeqcPtr->PHRQ_calloc((size_t)(count_sys + 1), sizeof(char*)); + if (types_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_arg = (LDBLE*)PhreeqcPtr->PHRQ_calloc((size_t)(count_sys + 1), sizeof(LDBLE)); + if (moles_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + names_arg[0] = NULL; + types_arg[0] = NULL; + moles_arg[0] = 0; + count_species = (LDBLE)count_sys; + n.UU.val = 0; + } + else + { + n.UU.val = PhreeqcPtr->system_total(elt_name, &count_species, &(names_arg), + &(types_arg), &(moles_arg), isort); + } + + /* + * fill in varrec structure + */ + if (arg_num > 1) + { + *count_varrec->UU.U0.val = count_species; + names_varrec->UU.U1.sarr = names_arg; + types_varrec->UU.U1.sarr = types_arg; + moles_varrec->UU.U0.arr = moles_arg; + + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + types_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + names_varrec->dims[0] = (long)(*count_varrec->UU.U0.val) + 1; + types_varrec->dims[0] = (long)(*count_varrec->UU.U0.val) + 1; + moles_varrec->dims[0] = (long)(*count_varrec->UU.U0.val) + 1; + names_varrec->numdims = 1; + types_varrec->numdims = 1; + moles_varrec->numdims = 1; + } + else + { + for (i = 0; i < count_species + 1; i++) + { + PhreeqcPtr->free_check_null(names_arg[i]); + PhreeqcPtr->free_check_null(types_arg[i]); + } + PhreeqcPtr->free_check_null(names_arg); + PhreeqcPtr->free_check_null(types_arg); + PhreeqcPtr->free_check_null(moles_arg); + } + } + break; + + case tokt_sc: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_t_sc(str); + } + break; + + case toktc: + { + n.UU.val = PhreeqcPtr->tc_x; + } + break; + + case toktime: + { + n.UU.val = PhreeqcPtr->rate_time; + } + break; + + case toktitle: + { + n.stringval = true; + if (strlen(PhreeqcPtr->last_title_x.c_str()) == 0) + { + PhreeqcPtr->last_title_x = " "; + } + n.UU.sval = PhreeqcPtr->string_duplicate(PhreeqcPtr->last_title_x.c_str()); + while (PhreeqcPtr->replace("\t", " ", n.UU.sval)); + } + break; + + case toktk: + { + n.UU.val = PhreeqcPtr->tc_x + 273.15; + } + break; + + case toktot: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total(str); + } + break; + + case toktotal_time: + { + if (!PhreeqcPtr->use.Get_kinetics_in()) + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = PhreeqcPtr->rate_sim_time_end; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->initial_total_time + PhreeqcPtr->transport_step * PhreeqcPtr->timest; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = + PhreeqcPtr->initial_total_time + PhreeqcPtr->advection_step * PhreeqcPtr->advection_kin_time; + } + else + { + n.UU.val = 0; + } + } + else + { + n.UU.val = PhreeqcPtr->initial_total_time + PhreeqcPtr->rate_sim_time; + } + } + break; + + case toktotmole: + case toktotmol: + case toktotmoles: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total_mole(str); + } + break; + + case toktrim: + { + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + STR1 = trim(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + } + break; + + case tokviscos: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->viscos; + } + break; + + case tokviscos_0: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->viscos_0; + } + break; + + case tokvm: + { + const char* str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->aqueous_vm(str); + } + break; + /* + * End of PHREEQC functions + */ + case toksa_declercq: // Undocumented function + { + double type, sa, d, m, m0, gfw; + + // left parenthesis + require(toklp, LINK); + + // first double arugument, type + type = realfactor(LINK); + require(tokcomma, LINK); + + // second double arugument, Sa + sa = realfactor(LINK); + require(tokcomma, LINK); + + // third double arugument, Sa + d = realfactor(LINK); + require(tokcomma, LINK); + + // fourth double arugument, m + m = realfactor(LINK); + require(tokcomma, LINK); + + // fifth double arugument, m0 + m0 = realfactor(LINK); + require(tokcomma, LINK); + + // sixth double arugument, gfw + gfw = realfactor(LINK); + require(tokrp, LINK); + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sa_declercq(type, sa, d, m, m0, gfw); + } + break; + + case tokcallback: // PHAST function + { + double x1, x2; + char* str; + + // left parenthesis + require(toklp, LINK); + + // first double arugument + x1 = realfactor(LINK); + require(tokcomma, LINK); + + // second double arugument + x2 = realfactor(LINK); + require(tokcomma, LINK); + + // string arugument + str = strexpr(LINK); + + require(tokrp, LINK); + + // call callback Basic function + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x2, str); + PhreeqcPtr->PHRQ_free(str); + } + break; + + case tokcell_pore_volume: // PHAST function + case tokporevolume: + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_pore_volume"); + } + break; + + case tokcell_porosity: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_porosity"); + } + break; + + case tokcell_saturation: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_saturation"); + } + break; + + case tokcell_volume: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_volume"); + } + break; + + case toktransport_cell_no: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "transport_cell_no"); + } + break; + + case tokvelocity_x: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "velocity_x"); + } + break; + + case tokvelocity_y: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "velocity_y"); + } + break; + + case tokvelocity_z: // PHAST function + { + double x1 = (double)PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "velocity_z"); + } + break; + + case toklog10: + { + LDBLE t = realfactor(LINK); + { + n.UU.val = log10(t); + } + } + break; + + case toksin: + { + n.UU.val = sin(realfactor(LINK)); + } + break; + + case tokcos: + { + n.UU.val = cos(realfactor(LINK)); + } + break; + + case toktan: + { + n.UU.val = realfactor(LINK); + n.UU.val = sin(n.UU.val) / cos(n.UU.val); + } + break; + + case tokarctan: + { + n.UU.val = atan(realfactor(LINK)); + } + break; + + case toklog: + { + n.UU.val = log(realfactor(LINK)); + } + break; + + case tokexp: + { + n.UU.val = exp(realfactor(LINK)); + } + break; + + case tokabs: + { + n.UU.val = fabs(realfactor(LINK)); + } + break; + + case toksgn: + { + n.UU.val = realfactor(LINK); + n.UU.val = (double)(n.UU.val > 0) - (double)(n.UU.val < 0); + } + break; + + case tokstr_: + n.stringval = true; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + numtostr(n.UU.sval, realfactor(LINK)); + break; + case tokval: + l_s = strfactor(LINK); + tok1 = LINK->t; + parse(l_s, &LINK->t); + tok = LINK->t; + if (tok == NULL) + n.UU.val = 0.0; + else + n = expr(LINK); + disposetokens(&tok); + LINK->t = tok1; + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokchr_: + n.stringval = true; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, " "); + n.UU.sval[0] = (char) intfactor(LINK); + break; + + case tokasc: + l_s = strfactor(LINK); + if (*l_s == '\0') + n.UU.val = 0.0; + else + n.UU.val = l_s[0]; + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokmid_: + n.stringval = true; + require(toklp, LINK); + n.UU.sval = strexpr(LINK); + require(tokcomma, LINK); + i = intexpr(LINK); + if (i < 1) + i = 1; + j = (int) strlen(n.UU.sval); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + } + { + std::string str = n.UU.sval; + str = str.substr((size_t)i - 1, (size_t)j); + strcpy(n.UU.sval, str.c_str()); + } + require(tokrp, LINK); + break; + case toklen: + l_s = strfactor(LINK); + n.UU.val = (double) strlen(l_s); + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokpeek: +/* p2c: basic.p, line 1029: Note: Range checking is OFF [216] */ + if (parse_all) + { + intfactor(LINK); + n.UU.val = 1.0; + } + else + { + trick.i = intfactor(LINK); + n.UU.val = *trick.c; + } +/* p2c: basic.p, line 1032: Note: Range checking is ON [216] */ + break; + + default: + snerr(": missing \" or ("); + break; + } + return n; +} + +valrec PBasic:: +upexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + n = factor(LINK); + while (LINK->t != NULL && LINK->t->kind == tokup) + { + if (n.stringval) + tmerr(": not a number before ^"); + LINK->t = LINK->t->next; + n2 = upexpr(LINK); + if (n2.stringval) + tmerr(": not a number after ^"); + if (n.UU.val >= 0) + { + if (n.UU.val > 0) + { + n.UU.val = exp(n2.UU.val * log(n.UU.val)); + } + continue; + } + if (n2.UU.val != (long) n2.UU.val) + { + tmerr(": negative number cannot be raised to a fractional power."); + } + else + { + n.UU.val = exp(n2.UU.val * log(-n.UU.val)); + if (((long) n2.UU.val) & 1) + n.UU.val = -n.UU.val; + } + } + return n; +} + +valrec PBasic:: +term(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k; + + n = upexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & ((1L << ((long) toktimes)) | + (1L << ((long) tokdiv)) | (1L << + ((long) tokmod)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = upexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(": found char, but need a number for * or /"); + if (k == tokmod) + { + /* n.UU.val = (long)floor(n.UU.val + 0.5) % (long)floor(n2.UU.val + 0.5); */ + if (n.UU.val != 0) + { + n.UU.val = + fabs(n.UU.val) / n.UU.val * fmod(fabs(n.UU.val) + + 1e-14, n2.UU.val); + } + else + { + n.UU.val = 0; + } +/* p2c: basic.p, line 1078: + * Note: Using % for possibly-negative arguments [317] */ + } + else if (k == toktimes) + n.UU.val *= n2.UU.val; + else if (n2.UU.val != 0) + { + n.UU.val /= n2.UU.val; + } + else + { + if (!parse_all) + { + char * error_string = PhreeqcPtr->sformatf( "Zero divide in BASIC line\n %ld %s.\nValue set to zero.", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + n.UU.val = 0; + } + } + return n; +} + +valrec PBasic:: +sexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k, m; + + n = term(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokplus)) | (1L << ((long) tokminus)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = term(LINK); + if (n.stringval != n2.stringval) + tmerr(": found char, but need a number for + or - "); + if (k == tokplus) + { + if (n.stringval) + { + m = 1; + if (n.UU.sval) + { + m += (int) strlen(n.UU.sval); + } + if (n2.UU.sval) + { + m += (int) strlen(n2.UU.sval); + } + //m = (int) strlen(n.UU.sval) + (int) strlen(n2.UU.sval) + 1; + if (m < 256) + m = 256; + + n.UU.sval = (char *) PhreeqcPtr->PHRQ_realloc(n.UU.sval, (size_t) m * sizeof(char)); + if (n.UU.sval == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + if (n2.UU.sval) + { + strcat(n.UU.sval, n2.UU.sval); + PhreeqcPtr->PHRQ_free(n2.UU.sval); + } + } + } + else + n.UU.val += n2.UU.val; + } + else + { + if (n.stringval) + tmerr(": found char, but need a number for - "); + else + n.UU.val -= n2.UU.val; + } + } + return n; +} + +valrec PBasic:: +relexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + bool f; + int k; + + n = sexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokne + 1)) - (1L << ((long) tokeq)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = sexpr(LINK); + if (n.stringval != n2.stringval) + tmerr(""); + if (n.stringval) + { + f = (bool) ((!strcmp(n.UU.sval, n2.UU.sval) + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokeq)) | + (1L << ((long) tokge)) | (1L << + ((long) tokle)))) + != 0) || (strcmp(n.UU.sval, n2.UU.sval) < 0 + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) toklt)) | + (1L << ((long) tokle)) | (1L << + ((long) + tokne)))) + != 0) + || (strcmp(n.UU.sval, n2.UU.sval) > 0 + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokgt)) | + (1L << ((long) tokge)) | (1L << + ((long) + tokne)))) != + 0)); + /* p2c: basic.p, line 2175: Note: + * Line breaker spent 0.0+1.00 seconds, 5000 tries on line 1518 [251] */ + PhreeqcPtr->PHRQ_free(n.UU.sval); + PhreeqcPtr->PHRQ_free(n2.UU.sval); + } + else + f = (bool) ((n.UU.val == n2.UU.val && (unsigned long) k < 32 && + ((1L << ((long) k)) & ((1L << ((long) tokeq)) | + (1L << ((long) tokge)) | + (1L << ((long) tokle)))) != + 0) || (n.UU.val < n2.UU.val + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) toklt)) | + (1L << ((long) tokle)) | (1L << + ((long) + tokne)))) + != 0) || (n.UU.val > n2.UU.val + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokgt)) | + (1L << ((long) tokge)) | (1L + << + ((long) tokne)))) != 0)); + /* p2c: basic.p, line 2175: Note: + * Line breaker spent 0.0+2.00 seconds, 5000 tries on line 1532 [251] */ + n.stringval = false; + n.UU.val = f; + } + return n; +} + +valrec PBasic:: +andexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + n = relexpr(LINK); + while (LINK->t != NULL && LINK->t->kind == tokand) + { + LINK->t = LINK->t->next; + n2 = relexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(""); + n.UU.val = ((long) n.UU.val) & ((long) n2.UU.val); + } + return n; +} + +valrec PBasic:: +expr(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k; + + n = andexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokor)) | (1L << ((long) tokxor)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = andexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(""); + if (k == tokor) + n.UU.val = ((long) n.UU.val) | ((long) n2.UU.val); + else + n.UU.val = ((long) n.UU.val) ^ ((long) n2.UU.val); + } + return n; +} + +void PBasic:: +checkextra(struct LOC_exec *LINK) +{ + if (LINK->t != NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_EXTRA; + } + errormsg("Extra information on line"); + } +} + +bool PBasic:: +iseos(struct LOC_exec *LINK) +{ + return ((bool) (LINK->t == NULL || LINK->t->kind == (long) tokelse || + LINK->t->kind == (long) tokcolon)); +} + +void PBasic:: +skiptoeos(struct LOC_exec *LINK) +{ + while (!iseos(LINK)) + LINK->t = LINK->t->next; +} + +linerec * PBasic:: +findline(long n) +{ + linerec *l; + + l = linebase; + while (l != NULL && l->num != n) + l = l->next; + return l; +} +linerec * PBasic:: +mustfindline(long n) +{ + linerec *l; + + l = findline(n); + if (phreeqci_gui) + { + if (parse_whole_program) + { + if (l == NULL) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_UNDEF_LINE; + char * error_string = PhreeqcPtr->sformatf( "Undefined line %ld", n); + errormsg(error_string); + } + } + } + else + { + if (l == NULL) + { + char * error_string = PhreeqcPtr->sformatf( "Undefined line %ld", n); + errormsg(error_string); + } + } + return l; +} +void PBasic:: +cmdend(struct LOC_exec *LINK) +{ + stmtline = NULL; + LINK->t = NULL; +} + +void PBasic:: +cmdnew(struct LOC_exec *LINK) +{ + void *p; + int i, k; + + cmdend(LINK); + clearloops(); + restoredata(); + while (linebase != NULL) + { + p = linebase->next; + disposetokens(&linebase->txt); + PhreeqcPtr->PHRQ_free(linebase); + linebase = (linerec *) p; + } + while (varbase != NULL) + { + p = varbase->next; + if (varbase->stringvar) + { + if (varbase->numdims > 0) + { + k = 1; + for (i = 0; i < varbase->numdims; i++) + { + k = k * (varbase->dims[i]); + } + for (i = 0; i < k; i++) + { + PhreeqcPtr->free_check_null(varbase->UU.U1.sarr[i]); + } + PhreeqcPtr->free_check_null(varbase->UU.U1.sarr); + } + else if (*varbase->UU.U1.sval != NULL) + { + *varbase->UU.U1.sval = + (char *) PhreeqcPtr->free_check_null(*varbase->UU.U1.sval); + } + + } + else + { + PhreeqcPtr->free_check_null(varbase->UU.U0.arr); + varbase->UU.U0.arr = NULL; + } + PhreeqcPtr->PHRQ_free(varbase); + varbase = (varrec *) p; + } +} + +void PBasic:: +cmdlist(struct LOC_exec *LINK) +{ + linerec *l; + long n1, n2; + + do + { + n1 = 0; + n2 = LONG_MAX; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n1 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != tokminus) + n2 = n1; + } + if (LINK->t != NULL && LINK->t->kind == tokminus) + { + LINK->t = LINK->t->next; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n2 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + } + else + n2 = LONG_MAX; + } + l = linebase; + while (l != NULL && l->num <= n2) + { + if (l->num >= n1) + { + /* printf("%ld ", l->num); */ + /* listtokens(stdout, l->txt); */ + /* putchar('\n'); */ + output_msg(PhreeqcPtr->sformatf("%ld ", l->num)); + listtokens(NULL, l->txt); + output_msg("\n"); + } + l = l->next; + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmdload(bool merging, char * name, struct LOC_exec *LINK) +{ + FILE *f; + tokenrec *l_buf; + char STR1[256] = {0}; + char *TEMP; + + f = NULL; + if (!merging) + cmdnew(LINK); + if (f != NULL) + { + sprintf(STR1, "%s.TEXT", name); + f = freopen(STR1, "r", f); + } + else + { + sprintf(STR1, "%s.TEXT", name); + f = fopen(STR1, "r"); + } + if (f == NULL) + { + _EscIO(FileNotFound); + return; + } + while (fgets(inbuf, 256, f) != NULL) + { + TEMP = strchr(inbuf, '\n'); + if (TEMP != NULL) + *TEMP = 0; + parseinput(&l_buf); + if (curline == 0) + { + /* printf("Bad line in file\n"); */ + output_msg("Bad line in file\n"); + disposetokens(&l_buf); + } + } + if (f != NULL) + fclose(f); + f = NULL; + if (f != NULL) + fclose(f); +} + +void PBasic:: +cmdrun(struct LOC_exec *LINK) +{ + linerec *l; + long i; + char *l_s; + + l_s = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (l_s == NULL) + PhreeqcPtr->malloc_error(); + + l = linebase; + if (!iseos(LINK)) + { + if (LINK->t->kind == toknum) + /*l = mustfindline(intexpr(LINK), LINK); */ + l = mustfindline(intexpr(LINK)); + else + { + stringexpr(l_s, LINK); + i = 0; + if (!iseos(LINK)) + { + require(tokcomma, LINK); + i = intexpr(LINK); + } + checkextra(LINK); + cmdload(false, l_s, LINK); + if (i == 0) + l = linebase; + else + l = mustfindline(i); + } + } + stmtline = l; + LINK->gotoflag = true; + clearvars(); + clearloops(); + restoredata(); + PhreeqcPtr->free_check_null(l_s); + return; +} + +/* PhreeqcPtr->replace basic save command with transport of rate back to calc_kinetic_rate */ +void PBasic:: +cmdsave(struct LOC_exec *LINK) +{ + valrec n; + + while (!iseos(LINK)) + { + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (n.stringval) + { + snerr(": in SAVE command"); + } + else + { + PhreeqcPtr->rate_moles = n.UU.val; + } + } +} + +void PBasic:: +cmdput(struct LOC_exec *LINK) +{ + int j; + std::ostringstream oss; + + /* get parentheses */ + require(toklp, LINK); + + /* get first argumen */ + double value = realexpr(LINK); + + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + oss << j << ","; + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + if (!parse_all) + { + PhreeqcPtr->save_values[oss.str()] = value; + } +} + +void PBasic:: +cmdchange_por(struct LOC_exec *LINK) +{ + int j; + LDBLE TEMP; + require(toklp, LINK); + /* get new porosity */ + TEMP = realexpr(LINK); + require(tokcomma, LINK); + /* get cell_no */ + j = intexpr(LINK); + require(tokrp, LINK); + if (j > 0 && j <= PhreeqcPtr->count_cells * (1 + PhreeqcPtr->stag_data.count_stag) + 1 + && j != PhreeqcPtr->count_cells + 1) + PhreeqcPtr->cell_data[j].por = TEMP; +} + +void PBasic:: +cmdchange_surf(struct LOC_exec *LINK) +{ +/* + change_surf("Hfo", 0.3, "Sfo", 0, 5 ) + (old_name, fraction, new_name, new_Dw, cell_no) + */ + char *c1; + int count; + + PhreeqcPtr->change_surf_count += 1; + count = PhreeqcPtr->change_surf_count; + if (PhreeqcPtr->change_surf[count - 1].next == FALSE) + PhreeqcPtr->change_surf = PhreeqcPtr->change_surf_alloc(count + 1); + + require(toklp, LINK); + /* get surface component name (change affects all comps of the same charge structure) */ + c1 = strexpr(LINK); + PhreeqcPtr->change_surf[count - 1].comp_name = PhreeqcPtr->string_hsave(c1); + PhreeqcPtr->PHRQ_free(c1); + require(tokcomma, LINK); + /* get fraction of comp to change */ + PhreeqcPtr->change_surf[count - 1].fraction = realexpr(LINK); + require(tokcomma, LINK); + /* get new surface component name */ + c1 = strexpr(LINK); + PhreeqcPtr->change_surf[count - 1].new_comp_name = PhreeqcPtr->string_hsave(c1); + PhreeqcPtr->PHRQ_free(c1); + require(tokcomma, LINK); + /* get new Dw (no transport if 0) */ + PhreeqcPtr->change_surf[count - 1].new_Dw = realexpr(LINK); + require(tokcomma, LINK); + /* get cell_no */ + PhreeqcPtr->change_surf[count - 1].cell_no = intexpr(LINK); + require(tokrp, LINK); + + if (PhreeqcPtr->change_surf->cell_no == 0 || PhreeqcPtr->change_surf->cell_no == PhreeqcPtr->count_cells + 1) + PhreeqcPtr->change_surf[count - 1].cell_no = -99; +} + +void PBasic:: +cmdbye(void) +{ + exitflag = true; +} + +void PBasic:: +cmddel(struct LOC_exec *LINK) +{ + linerec *l, *l0, *l1; + long n1, n2; + + do + { + if (iseos(LINK)) + snerr(": no variable name after del"); + n1 = 0; + n2 = LONG_MAX; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n1 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != tokminus) + n2 = n1; + } + if (LINK->t != NULL && LINK->t->kind == tokminus) + { + LINK->t = LINK->t->next; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n2 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + } + else + n2 = LONG_MAX; + } + l = linebase; + l0 = NULL; + while (l != NULL && l->num <= n2) + { + l1 = l->next; + if (l->num >= n1) + { + if (l == stmtline) + { + cmdend(LINK); + clearloops(); + restoredata(); + } + if (l0 == NULL) + linebase = l->next; + else + l0->next = l->next; + disposetokens(&l->txt); + PhreeqcPtr->PHRQ_free(l); + } + else + l0 = l; + l = l1; + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmdrenum(struct LOC_exec *LINK) +{ + linerec *l, *l1; + tokenrec *tok; + long lnum, step; + + lnum = 10; + step = 10; + if (!iseos(LINK)) + { + lnum = intexpr(LINK); + if (!iseos(LINK)) + { + require(tokcomma, LINK); + step = intexpr(LINK); + } + } + l = linebase; + if (l == NULL) + return; + while (l != NULL) + { + l->num2 = lnum; + lnum += step; + l = l->next; + } + l = linebase; + do + { + tok = l->txt; + do + { + if (tok->kind == (long) tokdel || tok->kind == (long) tokrestore + || tok->kind == (long) toklist || tok->kind == (long) tokrun + || tok->kind == (long) tokelse || tok->kind == (long) tokthen + || tok->kind == (long) tokgosub + || tok->kind == (long) tokgoto) + { + while (tok->next != NULL && tok->next->kind == toknum) + { + tok = tok->next; + lnum = (long) floor(tok->UU.num + 0.5); + l1 = linebase; + while (l1 != NULL && l1->num != lnum) + l1 = l1->next; + if (l1 == NULL) + { + /* printf("Undefined line %ld in line %ld\n", lnum, + l->num2); */ + output_msg(PhreeqcPtr->sformatf("Undefined line %ld in line %ld\n", lnum, l->num2)); + } + else + { +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + _snprintf(tok->sz_num, tok->n_sz, "%ld", l1->num2); + } +#endif + tok->UU.num = l1->num2; + } + if (tok->next != NULL && tok->next->kind == tokcomma) + tok = tok->next; + } + } + tok = tok->next; + } + while (tok != NULL); + l = l->next; + } + while (l != NULL); + l = linebase; + while (l != NULL) + { + l->num = l->num2; + l = l->next; + } +} + +void PBasic:: +cmdprint(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256] = {0}; + + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (n.stringval) + { + if (!skip_punch) { + /* fputs(n.UU.sval, stdout); */ + output_msg(PhreeqcPtr->sformatf("%s ", n.UU.sval)); + } + n.UU.sval = (char*)PhreeqcPtr->free_check_null(n.UU.sval); + } + else +/* printf("%s ", numtostr(STR1, n.UU.val)); */ + output_msg(PhreeqcPtr->sformatf("%s ", numtostr(STR1, n.UU.val))); + } + if (!semiflag && PhreeqcPtr->Get_output_newline()) +/* putchar('\n');*/ + output_msg("\n"); + PhreeqcPtr->Set_output_newline(true); + skip_punch = false; +} + +void PBasic:: +cmdpunch(struct LOC_exec *LINK) +{ + valrec n; + + /* char STR1[256]; */ + + while (!iseos(LINK)) + { + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (!this->skip_punch) + { + if (n.stringval) + { + /* fputs(n.UU.sval, stdout); */ + { + if (!temp_high_precision) + { + if (strlen(n.UU.sval) <= 12) + { + if (punch_tab) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%12.12s\t", n.UU.sval); + } + else { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%12.12s", n.UU.sval); + } + } + else + { + if (punch_tab) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s\t", n.UU.sval); + } + else { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s", n.UU.sval); + } + } + } + else + { + if (strlen(n.UU.sval) <= 20) + { + if (punch_tab) { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%20.20s\t", n.UU.sval); + } + else { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%20.20s", n.UU.sval); + } + } + else + { + if (punch_tab) { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s\t", n.UU.sval); + } + else { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s", n.UU.sval); + } + } + } + } + n.UU.sval = (char*)PhreeqcPtr->free_check_null(n.UU.sval); + } + else if (!temp_high_precision) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%12.4e\t", (double)n.UU.val); + } + else + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%20.12e\t", (double)n.UU.val); + } + punch_tab = true; + ++PhreeqcPtr->n_user_punch_index; + } + else + { + n.UU.sval = (char*)PhreeqcPtr->free_check_null(n.UU.sval); + } + this->skip_punch = false; + } +} + +#if defined PHREEQ98 +void PBasic:: +cmdgraph_x(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "x"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "x"); + colnr++; + } +} + +void PBasic:: +cmdgraph_y(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "y"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "y"); + colnr++; + } +} + +void PBasic:: +cmdgraph_sy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "s"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "s"); + colnr++; + } +} +#endif + +void PBasic:: +cmdlet(bool implied, struct LOC_exec *LINK) +{ + varrec *v; + char *old, *mynew; + LDBLE d_value; + LDBLE *target; + char **starget; + target = NULL; + starget = NULL; + if (implied) + LINK->t = stmttok; + v = findvar(LINK); + if (v->stringvar) + { + starget = v->UU.U1.sval; + } + else + { + target = v->UU.U0.val; + } + require(tokeq, LINK); + if (!v->stringvar) + { + /* in case array is used on right hand side */ + d_value = realexpr(LINK); + v->UU.U0.val = target; + *v->UU.U0.val = d_value; + /* *v->UU.U0.val = realexpr(LINK); */ + return; + } + mynew = strexpr(LINK); + v->UU.U1.sval = starget; + old = *v->UU.U1.sval; + *v->UU.U1.sval = mynew; + if (old != NULL) + PhreeqcPtr->PHRQ_free(old); +} + +void PBasic:: +cmdgoto(struct LOC_exec *LINK) +{ + stmtline = mustfindline(intexpr(LINK)); + LINK->t = NULL; + LINK->gotoflag = true; +} + +void PBasic:: +cmdif(struct LOC_exec *LINK) +{ + LDBLE n; + long i; + + n = realexpr(LINK); + require(tokthen, LINK); + if (n == 0) + { + i = 0; + do + { + if (LINK->t != NULL) + { + if (LINK->t->kind == tokif) + i++; + if (LINK->t->kind == tokelse) + i--; + LINK->t = LINK->t->next; + } + } + while (LINK->t != NULL && i >= 0); + } + if (LINK->t != NULL && LINK->t->kind == toknum) + cmdgoto(LINK); + else + LINK->elseflag = true; +} + +void PBasic:: +cmdelse(struct LOC_exec *LINK) +{ + LINK->t = NULL; +} + +bool PBasic:: +skiploop(int up, int dn, struct LOC_exec *LINK) +{ + bool Result; + long i; + linerec *saveline; + + saveline = stmtline; + i = 0; + do + { + while (LINK->t == NULL) + { + if (stmtline == NULL || stmtline->next == NULL) + { + Result = false; + stmtline = saveline; + goto _L1; + } + stmtline = stmtline->next; + LINK->t = stmtline->txt; + } + if (LINK->t->kind == up) + i++; + if (LINK->t->kind == dn) + i--; + LINK->t = LINK->t->next; + } + while (i >= 0); + Result = true; + _L1: + return Result; +} + +void PBasic:: +cmdfor(struct LOC_exec *LINK) +{ + looprec *l, lr; + linerec *saveline; + long i, j; + + lr.UU.U0.vp = findvar(LINK); + if (lr.UU.U0.vp->stringvar) + snerr(": error in FOR command"); + require(tokeq, LINK); + *lr.UU.U0.vp->UU.U0.val = realexpr(LINK); + require(tokto, LINK); + lr.UU.U0.max = realexpr(LINK); + if (LINK->t != NULL && LINK->t->kind == tokstep) + { + LINK->t = LINK->t->next; + lr.UU.U0.step = realexpr(LINK); + } + else + lr.UU.U0.step = 1.0; + lr.homeline = stmtline; + lr.hometok = LINK->t; + lr.kind = forloop; + lr.next = loopbase; + if ((lr.UU.U0.step >= 0 && *lr.UU.U0.vp->UU.U0.val > lr.UU.U0.max) || + (lr.UU.U0.step <= 0 && *lr.UU.U0.vp->UU.U0.val < lr.UU.U0.max)) + { + saveline = stmtline; + i = 0; + j = 0; + do + { + while (LINK->t == NULL) + { + if (stmtline == NULL || stmtline->next == NULL) + { + stmtline = saveline; + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_FOR_WO_NEXT; + } + errormsg("FOR without NEXT"); + } + stmtline = stmtline->next; + LINK->t = stmtline->txt; + } + if (LINK->t->kind == tokfor) + { + if (LINK->t->next != NULL && LINK->t->next->kind == tokvar && + LINK->t->next->UU.vp == lr.UU.U0.vp) + j++; + else + i++; + } + if (LINK->t->kind == toknext) + { + if (LINK->t->next != NULL && LINK->t->next->kind == tokvar && + LINK->t->next->UU.vp == lr.UU.U0.vp) + j--; + else + i--; + } + LINK->t = LINK->t->next; + } + while (i >= 0 && j >= 0); + skiptoeos(LINK); + return; + } + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + *l = lr; + loopbase = l; + } +} + +void PBasic:: +cmdnext(struct LOC_exec *LINK) +{ + varrec *v; + bool found; + looprec *l, *WITH; + + if (!iseos(LINK)) + v = findvar(LINK); + else + v = NULL; + do + { + if (loopbase == NULL || loopbase->kind == gosubloop) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_NEXT_WO_FOR; + } + errormsg("NEXT without FOR"); + } + found = (bool) (loopbase->kind == forloop && + (v == NULL || loopbase->UU.U0.vp == v)); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + WITH = loopbase; + *WITH->UU.U0.vp->UU.U0.val += WITH->UU.U0.step; + if ((WITH->UU.U0.step < 0 + || *WITH->UU.U0.vp->UU.U0.val <= WITH->UU.U0.max) + && (WITH->UU.U0.step > 0 + || *WITH->UU.U0.vp->UU.U0.val >= WITH->UU.U0.max)) + { + stmtline = WITH->homeline; + LINK->t = WITH->hometok; + return; + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; +} + +void PBasic:: +cmdwhile(struct LOC_exec *LINK) +{ + looprec *l; + + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + return; + } + l->next = loopbase; + loopbase = l; + l->kind = whileloop; + l->homeline = stmtline; + l->hometok = LINK->t; + if (iseos(LINK)) + return; + if (realexpr(LINK) != 0) + return; + if (phreeqci_gui) + { + if (parse_whole_program == TRUE) + { + if (!skiploop(tokwhile, tokwend, LINK)) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_WHILE_WO_WEND; + errormsg("WHILE without WEND"); + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); + } + } + else + { + if (!skiploop(tokwhile, tokwend, LINK)) + { + errormsg("WHILE without WEND"); + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); + } +} + +void PBasic:: +cmdwend(struct LOC_exec *LINK) +{ + tokenrec *tok; + linerec *tokline; + looprec *l; + bool found; + if (phreeqci_gui && !parse_whole_program) + { + return; + } + do + { + if (loopbase == NULL || loopbase->kind == gosubloop) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_WEND_WO_WHILE; + } + errormsg("WEND without WHILE"); + } + found = (bool) (loopbase->kind == whileloop); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + if (!iseos(LINK)) + { + if (realexpr(LINK) != 0) + found = false; + } + tok = LINK->t; + tokline = stmtline; + if (found) + { + stmtline = loopbase->homeline; + LINK->t = loopbase->hometok; + if (!iseos(LINK)) + { + if (realexpr(LINK) == 0) + found = false; + } + } + if (found) + return; + LINK->t = tok; + stmtline = tokline; + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; +} + +void PBasic:: +cmdgosub(struct LOC_exec *LINK) +{ + looprec *l; + + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + return; + } + l->next = loopbase; + loopbase = l; + l->kind = gosubloop; + l->homeline = stmtline; + l->hometok = LINK->t; + cmdgoto(LINK); +} + +void PBasic:: +cmdreturn(struct LOC_exec *LINK) +{ + looprec *l; + bool found; + + if (phreeqci_gui && !parse_whole_program) + { + return; + } + + do + { + if (loopbase == NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_RETURN_WO_GOSUB; + } + errormsg("RETURN without GOSUB"); + } + found = (bool) (loopbase->kind == gosubloop); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + stmtline = loopbase->homeline; + LINK->t = loopbase->hometok; + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); +} + +void PBasic:: +cmdread(struct LOC_exec *LINK) +{ + varrec *v; + tokenrec *tok; + bool found; + + do + { + v = findvar(LINK); + tok = LINK->t; + LINK->t = datatok; + if (phreeqci_gui) + { + if (parse_whole_program) + { + if (dataline == NULL) + { + dataline = linebase; + LINK->t = dataline->txt; + } + if (LINK->t == NULL || LINK->t->kind != tokcomma) + { + do + { + while (LINK->t == NULL) + { + if (dataline == NULL || dataline->next == NULL) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_OUT_OF_DATA; + errormsg("Out of Data"); + } + dataline = dataline->next; + LINK->t = dataline->txt; + } + found = (bool) (LINK->t->kind == tokdata); + LINK->t = LINK->t->next; + } + while (!found || iseos(LINK)); + } + else + LINK->t = LINK->t->next; + if (v->stringvar) + { + if (*v->UU.U1.sval != NULL) + *v->UU.U1.sval = (char *) PhreeqcPtr->free_check_null(*v->UU.U1.sval); + *v->UU.U1.sval = strexpr(LINK); + } + else + *v->UU.U0.val = realexpr(LINK); + } + } + else + { + if (dataline == NULL) + { + dataline = linebase; + LINK->t = dataline->txt; + } + if (LINK->t == NULL || LINK->t->kind != tokcomma) + { + do + { + while (LINK->t == NULL) + { + if (dataline == NULL || dataline->next == NULL) + errormsg("Out of Data"); + dataline = dataline->next; + LINK->t = dataline->txt; + } + found = (bool) (LINK->t->kind == tokdata); + LINK->t = LINK->t->next; + } + while (!found || iseos(LINK)); + } + else + LINK->t = LINK->t->next; + if (v->stringvar) + { + if (*v->UU.U1.sval != NULL) + *v->UU.U1.sval = (char *) PhreeqcPtr->free_check_null(*v->UU.U1.sval); + *v->UU.U1.sval = strexpr(LINK); + } + else + *v->UU.U0.val = realexpr(LINK); + } + datatok = LINK->t; + LINK->t = tok; + if (!iseos(LINK)) + require(tokcomma, LINK); + + } + while (!iseos(LINK)); +} + +void PBasic:: +cmddata(struct LOC_exec *LINK) +{ + skiptoeos(LINK); +} + +void PBasic:: +cmdrestore(struct LOC_exec *LINK) +{ + if (iseos(LINK)) + restoredata(); + else + { + dataline = mustfindline(intexpr(LINK)); + if (phreeqci_gui) + { + if (parse_whole_program) + { + datatok = dataline->txt; + } + } + else + { + datatok = dataline->txt; + } + } +} + +void PBasic:: +cmdgotoxy(struct LOC_exec *LINK) +{ + intexpr(LINK); + require(tokcomma, LINK); +} + +void PBasic:: +cmdon(struct LOC_exec *LINK) +{ + long i; + looprec *l; + + i = intexpr(LINK); + if (LINK->t != NULL && LINK->t->kind == tokgosub) + { + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + l->next = loopbase; + loopbase = l; + l->kind = gosubloop; + l->homeline = stmtline; + l->hometok = LINK->t; + LINK->t = LINK->t->next; + } + } + else + require(tokgoto, LINK); + if (i < 1) + { + skiptoeos(LINK); + return; + } + while (i > 1 && !iseos(LINK)) + { + require(toknum, LINK); + if (!iseos(LINK)) + require(tokcomma, LINK); + i--; + } + if (!iseos(LINK)) + cmdgoto(LINK); +} + +void PBasic:: +cmddim(struct LOC_exec *LINK) +{ + long i, j, k; + varrec *v; + bool done; + + do + { + if (LINK->t == NULL || LINK->t->kind != tokvar) + snerr(": error in DIM command"); + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + if (v->numdims != 0) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_ARRAY_ALREADY; + } + errormsg("Array already dimensioned before"); + } + j = 1; + i = 0; + require(toklp, LINK); + do + { + k = intexpr(LINK) + 1; + if (k < 1) + badsubscr(); + if (i >= maxdims) + badsubscr(); + i++; + v->dims[i - 1] = k; + j *= k; + done = (bool) (LINK->t != NULL && LINK->t->kind == tokrp); + if (!done) + require(tokcomma, LINK); + } + while (!done); + LINK->t = LINK->t->next; + v->numdims = (char) i; + if (v->stringvar) + { + v->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc(j * sizeof(char *)); + if (!v->UU.U1.sarr) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + if (v->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + for (i = 0; i < j; i++) + v->UU.U1.sarr[i] = NULL; + } + else + { + v->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc(j * sizeof(LDBLE)); + if (v->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + for (i = 0; i < j; i++) + v->UU.U0.arr[i] = 0.0; + } + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmderase(struct LOC_exec *LINK) +{ + varrec *v = NULL; + do + { + if (LINK->t == NULL || LINK->t->kind != tokvar) + { + snerr(": error in DIM command"); + } + else + { + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + clearvar(v); + if (!iseos(LINK)) require(tokcomma, LINK); + } + } + while (!iseos(LINK)); +} + + +void PBasic:: +cmdpoke(struct LOC_exec *LINK) +{ + union + { + long i; + char *c; + } trick; + +/* p2c: basic.p, line 2073: Note: Range checking is OFF [216] */ + trick.i = intexpr(LINK); + require(tokcomma, LINK); + *trick.c = (char) intexpr(LINK); +/* p2c: basic.p, line 2077: Note: Range checking is ON [216] */ +} + +void PBasic:: +exec(void) +{ + struct LOC_exec V; + V.gotoflag = false; + V.elseflag = false; + V.t = NULL; + char STR1[256] = {0}; + + try + { + do + { + do + { + V.gotoflag = false; + V.elseflag = false; + while (stmttok != NULL && stmttok->kind == tokcolon) + stmttok = stmttok->next; + V.t = stmttok; + if (V.t != NULL) + { + V.t = V.t->next; +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + if (WaitForSingleObject(hInfiniteLoop, 0) == WAIT_OBJECT_0) + { + nIDErrPrompt = IDS_ERR_INFINITE_LOOP; + errormsg("Possible infinite loop"); + } + } +#endif + switch (stmttok->kind) + { + + case tokrem: + /* blank case */ + break; + + case toklist: + cmdlist(&V); + break; + + case tokrun: + cmdrun(&V); + break; + + case toknew: + cmdnew(&V); + break; + + case tokload: + cmdload(false, stringexpr(STR1, &V), &V); + break; + + case tokmerge: + cmdload(true, stringexpr(STR1, &V), &V); + break; + + case toksave: + cmdsave(&V); + break; + + case tokbye: + cmdbye(); + break; + + case tokdel: + cmddel(&V); + break; + + case tokrenum: + cmdrenum(&V); + break; + + case toklet: + cmdlet(false, &V); + break; + + case tokvar: + cmdlet(true, &V); + break; + + case tokprint: + cmdprint(&V); + break; + + case tokpunch: + cmdpunch(&V); + break; + + case tokput: + cmdput(&V); + break; + + case tokchange_por: + cmdchange_por(&V); + break; + + case tokchange_surf: + cmdchange_surf(&V); + break; + +#if defined PHREEQ98 || defined MULTICHART + case tokgraph_x: + cmdgraph_x(&V); + break; + + case tokgraph_y: + cmdgraph_y(&V); + break; + + case tokgraph_sy: + cmdgraph_sy(&V); + break; +#endif +#if defined MULTICHART + case tokplot_xy: + cmdplot_xy(&V); + break; +#endif + + case tokinput: + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_INPUT_NOTLEGAL; + errormsg ("Basic command INPUT is not a legal command in PHREEQC."); + } + else + { + error_msg ("Basic command INPUT is not a legal command in PHREEQC.", STOP); + } + break; + + case tokgoto: + cmdgoto(&V); + break; + + case tokif: + cmdif(&V); + break; + + case tokelse: + cmdelse(&V); + break; + + case tokend: + cmdend(&V); + break; + + case tokstop: + P_escapecode = -20; + throw PBasicStop(); + //goto _Ltry1; + break; + + case tokfor: + cmdfor(&V); + break; + + case toknext: + cmdnext(&V); + break; + + case tokwhile: + cmdwhile(&V); + break; + + case tokwend: + cmdwend(&V); + break; + + case tokgosub: + cmdgosub(&V); + break; + + case tokreturn: + cmdreturn(&V); + break; + + case tokread: + cmdread(&V); + break; + + case tokdata: + cmddata(&V); + break; + + case tokrestore: + cmdrestore(&V); + break; + + case tokgotoxy: + cmdgotoxy(&V); + break; + + case tokon: + cmdon(&V); + break; + + case tokdim: + cmddim(&V); + break; + + case tokerase: + cmderase(&V); + break; + + case tokpoke: + cmdpoke(&V); + break; + + default: + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_ILLEGAL; + } + strcat(STR1, "Illegal command in line: "); + if (strcmp(inbuf, "run")) + strcat(STR1, inbuf); + errormsg(STR1); + break; + } + } + if (!V.elseflag && !iseos(&V)) + checkextra(&V); + stmttok = V.t; + } + while (V.t != NULL); + if (stmtline != NULL) + { + if (!V.gotoflag) + stmtline = stmtline->next; + if (stmtline != NULL) + stmttok = stmtline->txt; + } + } + while (stmtline != NULL); + } + catch (const PBasicStop&) + { + //_Ltry1: + if (P_escapecode == -20) + PhreeqcPtr->warning_msg("Break"); + /* printf("Break"); */ + else if (P_escapecode != 42) + { + switch (P_escapecode) + { + + case -4: + { + char * error_string = PhreeqcPtr->sformatf( "Integer overflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -5: + { + char * error_string = PhreeqcPtr->sformatf( "Divide by zero in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -6: + { + char * error_string = PhreeqcPtr->sformatf( "Real math overflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -7: + { + char * error_string = PhreeqcPtr->sformatf( "Real math underflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -8: + case -19: + case -18: + case -17: + case -16: + case -15: + { + char * error_string = PhreeqcPtr->sformatf( "Value range error in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -10: + { + char * error_string = PhreeqcPtr->sformatf("I/O Error %d", (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + break; + + default: + if (EXCP_LINE != -1) + { + char * error_string = PhreeqcPtr->sformatf( "%12ld\n", EXCP_LINE); + PhreeqcPtr->warning_msg(error_string); + } + _Escape(P_escapecode); + break; + } + } + if (stmtline != NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nErrLineNumber == 0); + nErrLineNumber = stmtline->num; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + error_msg(error_string, CONTINUE); + } + } + } // end catch +} /*exec */ + +int PBasic:: +free_dim_stringvar(varrec *l_varbase) +{ + int i, k; + if (l_varbase->numdims > 0) + { + k = 1; + for (i = 0; i < l_varbase->numdims; i++) + { + k = k * (l_varbase->dims[i]); + } + for (i = 0; i < k; i++) + { + PhreeqcPtr->free_check_null(l_varbase->UU.U1.sarr[i]); + } + l_varbase->UU.U1.sarr = (char **) PhreeqcPtr->free_check_null(l_varbase->UU.U1.sarr); + } + return (OK); +} + +#if defined MULTICHART +void PBasic:: +cmdplot_xy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n[2]; + + char STR[2][256]; + int i = 0; + semiflag = false; + + while (!iseos(LINK) && i < 2) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + i++; + continue; + } + n[i] = expr(LINK); + if (n[i].stringval) + { + strcpy(STR[i], n[i].UU.sval); + PhreeqcPtr->PHRQ_free(n[i].UU.sval); + } + else + numtostr(STR[i], n[i].UU.val); + } + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + std::string x_str(STR[0]), y_str(STR[1]); + +// Code formerly in PlotXY, included here + { + + bool new_sim = false, new_trans = false; + + if (chart->Get_FirstCallToUSER_GRAPH() && chart->Get_colnr() == 0) + chart->Set_prev_sim_no(PhreeqcPtr->simulation); + else + chart->Set_prev_sim_no(PhreeqcPtr->simulation); + + // Add a curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + std::string head(""); + if (chart->Get_new_headings().size() > 0) + { + head = chart->Get_new_headings()[0]; + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + if (chart->Get_new_plotxy_curves().size() > 0) + { + // find plotxy curve definitions + chart->Add_curve(true, head, + chart->Get_new_plotxy_curves()[0].Get_line_w(), + chart->Get_new_plotxy_curves()[0].Get_symbol(), + chart->Get_new_plotxy_curves()[0].Get_symbol_size(), + chart->Get_new_plotxy_curves()[0].Get_y_axis(), + chart->Get_new_plotxy_curves()[0].Get_color() + ); + // pop plotxy curve definition + chart->Get_new_plotxy_curves().erase(chart->Get_new_plotxy_curves().begin()); + } + else + { + chart->Add_curve(true, head); + } + chart->Set_curve_added(true); + } + + if (x_str.size() > 0 && y_str.size() > 0) + { + chart->Get_Curves()[chart->Get_colnr()]->Get_x().push_back(atof(x_str.c_str())); + chart->Get_Curves()[chart->Get_colnr()]->Get_y().push_back(atof(y_str.c_str())); + chart->Set_point_added(true); + + // Mark added curve for first point, might have been invisible in DefineCurves + if (chart->Get_Curves()[chart->Get_colnr()]->Get_x().size() == 1) + chart->Set_curve_added(true); + } + } + chart->Set_colnr(chart->Get_colnr() + 1); +} + +void PBasic:: +cmdgraph_x(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Set_graph_x(atof(n.UU.sval)); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + chart->Set_graph_x(n.UU.val); + } + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + // remove x heading + //if (chart->Get_colnr() == chart->Get_ColumnOffset()) + { + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + } + } +} + +void PBasic:: +cmdgraph_y(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + // wait until x value is known before storing in curve + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Get_graph_y()[chart->Get_colnr()] = atof(n.UU.sval); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + { + chart->Get_graph_y()[chart->Get_colnr()] = n.UU.val; + } + chart->Set_point_added(true); + + // Add a new curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + chart->Add_curve(false, chart->Get_new_headings()[0]); + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + else + { + chart->Add_curve(false); + } + chart->Set_curve_added(true); + } + chart->Set_colnr(chart->Get_colnr() + 1); + } +} + +void PBasic:: +cmdgraph_sy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + chart->Get_secondary_y()[chart->Get_colnr()] = true; + + // wait until x value is known before storing in curve + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Get_graph_y()[chart->Get_colnr()] = atof(n.UU.sval); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + chart->Get_graph_y()[chart->Get_colnr()] = n.UU.val; + chart->Set_point_added(true); + // Add a new curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + chart->Add_curve(false, chart->Get_new_headings()[0]); + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + else + { + chart->Add_curve(false); + } + chart->Set_curve_added(true); + } + chart->Set_colnr(chart->Get_colnr() + 1); + } +} +#endif // MULTICHART + +/* In case your system lacks these... */ + +long PBasic:: +my_labs(long l_x) +{ + return ((l_x > 0) ? l_x : -l_x); +} + + +/* #define __STDC__ */ /* PHREEQ98 */ + +void * PBasic:: +my_memmove(void * d, Const void * l_s, size_t n) +{ + char *dd = (char *) d, *ss = (char *) l_s; + if (dd < ss || (unsigned int) (dd - ss) >= n) + { + memcpy(dd, ss, n); + } + else if (n > 0) + { + dd += n; + ss += n; + while (n-- > 0) + *--dd = *--ss; + } + return d; +} + +void * PBasic:: +my_memcpy(void * d, Const void * l_s, size_t n) +{ + char *ss = (char *) l_s, *dd = (char *) d; + while (n-- > 0) + *dd++ = *ss++; + return d; +} + +int PBasic:: +my_memcmp(Const void * s1, Const void * s2, size_t n) +{ + char *a = (char *) s1, *b = (char *) s2; + int i; + while (n-- > 0) + if ((i = (*a++) - (*b++)) != 0) + return i; + return 0; +} + +void * PBasic:: +my_memset(void * d, int c, size_t n) +{ + char *dd = (char *) d; + while (n-- > 0) + *dd++ = (char) c; + return d; +} + +int PBasic:: +my_toupper(int c) +{ + if (islower(c)) + return _toupper(c); + else + return c; +} + +int PBasic:: +my_tolower(int c) +{ + if (isupper(c)) + return _tolower(c); + else + return c; +} + +long PBasic:: +ipow(long a, long b) +{ + long v; + + if (a == 0 || a == 1) + return a; + if (a == -1) + return (b & 1) ? -1 : 1; + if (b < 0) + return 0; + if (a == 2) + return 1L << b; + v = (b & 1) ? a : 1; + while ((b >>= 1) > 0) + { + a *= a; + if (b & 1) + v *= a; + } + return v; +} + +/* Common string functions: */ + +/* Store in "ret" the substring of length "len" starting from "pos" (1-based). + Store a shorter or null string if out-of-range. Return "ret". */ +char * PBasic:: +strsub(char *ret, char *l_s, int pos, + int len) +{ + char *s2; + + if (--pos < 0 || len <= 0) + { + *ret = 0; + return ret; + } + while (pos > 0) + { + if (!*l_s++) + { + *ret = 0; + return ret; + } + pos--; + } + s2 = ret; + while (--len >= 0) + { + if (!(*s2++ = *l_s++)) + return ret; + } + *s2 = 0; + return ret; +} + +/* Return the index of the first occurrence of "pat" as a substring of "s", + starting at index "pos" (1-based). Result is 1-based, 0 if not found. */ + +int PBasic:: +strpos2(char *l_s, char *pat, int pos) +{ + char *cp, ch; + int slen; + + if (--pos < 0) + return 0; + slen = (int) strlen(l_s) - pos; + cp = l_s + pos; + if (!(ch = *pat++)) + return 0; + pos = (int) strlen(pat); + slen -= pos; + while (--slen >= 0) + { + if (*cp++ == ch && !strncmp(cp, pat, pos)) + return (int) (cp - l_s); + } + return 0; +} + +/* Case-insensitive version of strcmp. */ +int PBasic:: +strcicmp(char *s1, char *s2) +{ + unsigned char c1, c2; + + while (*s1) + { + if (*s1++ != *s2++) + { + if (!s2[-1]) + return 1; + c1 = (unsigned char) toupper(s1[-1]); + c2 = (unsigned char) toupper(s2[-1]); + if (c1 != c2) + return c1 - c2; + } + } + if (*s2) + return -1; + return 0; +} + +/* HP and Turbo Pascal string functions: */ + +/* Trim blanks at left end of string. */ +char * PBasic:: +strltrim(char *l_s) +{ + while (Isspace((int) *l_s++)); + return l_s - 1; +} + +/* Trim blanks at right end of string. */ +char * PBasic:: +strrtrim(char *l_s) +{ + char *s2 = l_s; + + if (!*l_s) + return l_s; + while (*++s2); + while (s2 > l_s && Isspace((int) *--s2)) + *s2 = 0; + return l_s; +} + +/* Store in "ret" string "s" with enough pad chars added to reach "size". */ + +/* Copy the substring of length "len" from index "spos" of "s" (1-based) + to index "dpos" of "d", lengthening "d" if necessary. Length and + indices must be in-range. */ +void PBasic:: +strmove(int len, char *l_s, int spos, + char *d, int dpos) +{ + l_s += (size_t)spos - 1; + d += (size_t)dpos - 1; + while (*d && --len >= 0) + *d++ = *l_s++; + if (len > 0) + { + while (--len >= 0) + *d++ = *l_s++; + *d = 0; + } +} + +/* Insert string "src" at index "pos" of "dst". */ +void PBasic:: +strinsert(char *src, char *dst, int pos) +{ + int slen, dlen; + + if (--pos < 0) + return; + dlen = (int) strlen(dst); + dst += dlen; + dlen -= pos; + if (dlen <= 0) + { + strcpy(dst, src); + return; + } + slen = (int) strlen(src); + do + { + dst[slen] = *dst; + --dst; + } + while (--dlen >= 0); + dst++; + while (--slen >= 0) + *dst++ = *src++; +} + +/* File functions */ + +/* Peek at next character of input stream; return EOF at end-of-file. */ +int PBasic:: +P_peek(FILE * f) +{ + int ch; + + ch = getc(f); + if (ch == EOF) + return EOF; + ungetc(ch, f); + return (ch == '\n') ? ' ' : ch; +} + +/* Check if at end of file, using Pascal "eof" semantics. End-of-file for + stdin is broken; remove the special case for it to be broken in a + different way. */ +/*int P_eof(FILE *f)*/ +int PBasic:: +P_eof(void) +{ + return 0; +} + +/* Check if at end of line (or end of entire file). */ +int PBasic:: +P_eoln(FILE * f) +{ + int ch; + + ch = getc(f); + if (ch == EOF) + return 1; + ungetc(ch, f); + return (ch == '\n'); +} + +/* Read a packed array of characters from a file. */ +void PBasic:: +P_readpaoc(FILE * f, char *l_s, int len) +{ + int ch; + + for (;;) + { + if (len <= 0) + return; + ch = getc(f); + if (ch == EOF || ch == '\n') + break; + *l_s++ = (char) ch; + --len; + } + while (--len >= 0) + *l_s++ = ' '; + if (ch != EOF) + ungetc(ch, f); +} + +void PBasic:: +P_readlnpaoc(FILE * f, char *l_s, int len) +{ + int ch; + + for (;;) + { + ch = getc(f); + if (ch == EOF || ch == '\n') + break; + if (len > 0) + { + *l_s++ = (char) ch; + --len; + } + } + while (--len >= 0) + *l_s++ = ' '; +} + +/* Compute maximum legal "seek" index in file (0-based). */ +long PBasic:: +P_maxpos(FILE * f) +{ + long savepos = ftell(f); + long val; + + if (fseek(f, 0L, SEEK_END)) + return -1; + val = ftell(f); + if (fseek(f, savepos, SEEK_SET)) + return -1; + return val; +} + +/* Use packed array of char for a file name. */ +char * PBasic:: +P_trimname(char * fn, int len) +{ + char *cp = fnbuf; + + while (--len >= 0 && *fn && !isspace((int) *fn)) + *cp++ = *fn++; + *cp = 0; + return fnbuf; +} + +/* Pascal's "memavail" doesn`t make much sense in Unix with virtual memory. + We fix memory size as 10Meg as a reasonable compromise. */ + +long PBasic:: +memavail(void) +{ + return 10000000; /* worry about this later! */ +} + +long PBasic:: +maxavail(void) +{ + return memavail(); +} + +/* Sets are stored as an array of longs. S[0] is the size of the set; + S[N] is the n`th 32-bit chunk of the set. S[0] equals the maximum + I such that S[I] is nonzero. S[0] is zero for an empty set. Within + each long, bits are packed from lsb to msb. The first bit of the + set is the element with ordinal value 0. (Thus, for a "set of 5..99", + the lowest five bits of the first long are unused and always zero.) */ + +/* (Sets with 32 or fewer elements are normally stored as plain longs.) */ +long * PBasic:: +P_setunion(long *d, long *s1, long *s2) /* d := s1 + s2 */ +{ + long *dbase = d++; + int sz1 = *s1++, sz2 = *s2++; + while (sz1 > 0 && sz2 > 0) + { + *d++ = *s1++ | *s2++; + sz1--, sz2--; + } + while (--sz1 >= 0) + *d++ = *s1++; + while (--sz2 >= 0) + *d++ = *s2++; + *dbase = (int) (d - dbase - 1); + return dbase; +} + +long * PBasic:: +P_setint(long *d, long *s1, long *s2) /* d := s1 * s2 */ +{ + long *dbase = d++; + int sz1 = *s1++, sz2 = *s2++; + while (--sz1 >= 0 && --sz2 >= 0) + *d++ = *s1++ & *s2++; + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_setdiff(long *d, long *s1, long *s2) /* d := s1 - s2 */ +{ + long *dbase = d++; + int sz1 = *s1++, sz2 = *s2++; + while (--sz1 >= 0 && --sz2 >= 0) + *d++ = *s1++ & ~*s2++; + if (sz1 >= 0) + { + while (sz1-- >= 0) + *d++ = *s1++; + } + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_setxor(long *d, long *s1, long *s2) /* d := s1 / s2 */ +{ + long *dbase = d++; + int sz1 = *s1++, sz2 = *s2++; + while (sz1 > 0 && sz2 > 0) + { + *d++ = *s1++ ^ *s2++; + sz1--, sz2--; + } + while (--sz1 >= 0) + *d++ = *s1++; + while (--sz2 >= 0) + *d++ = *s2++; + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_addset(long *l_s, unsigned val) /* s := s + [val] */ +{ + long *sbase = l_s; + int bit, size; + bit = val % SETBITS; + val /= SETBITS; + size = *l_s; + if ((long) ++val > size) + { + l_s += size; + while ((long) val > size) + *++l_s = 0, size++; + *sbase = size; + } + else + l_s += val; + *l_s |= 1L << bit; + return sbase; +} + +//long * PBasic:: +//P_addsetr(long *l_s, unsigned v1, unsigned v2) /* s := s + [v1..v2] */ +//{ +// long *sbase = l_s; +// int b1, b2, size; +// if ((int) v1 > (int) v2) +// return sbase; +// b1 = v1 % SETBITS; +// v1 /= SETBITS; +// b2 = v2 % SETBITS; +// v2 /= SETBITS; +// size = *l_s; +// v1++; +// if ((int) ++v2 > size) +// { +// while ((int) v2 > size) +// l_s[++size] = 0; +// l_s[v2] = 0; +// *l_s = v2; +// } +// l_s += v1; +// if (v1 == v2) +// { +// *l_s |= (~((-2L) << (b2 - b1))) << b1; +// } +// else +// { +// *l_s++ |= (-1L) << b1; +// while (++v1 < v2) +// *l_s++ = -1; +// *l_s |= ~((-2L) << b2); +// } +// return sbase; +//} + +long * PBasic:: +P_remset(long *l_s, unsigned val) /* s := s - [val] */ +{ + int bit; + bit = val % SETBITS; + val /= SETBITS; + if ((long) ++val <= *l_s) + { + if (!(l_s[val] &= ~(1L << bit))) + while (*l_s && !l_s[*l_s]) + (*l_s)--; + } + return l_s; +} + +int PBasic:: +P_setequal(long *s1, long *s2) /* s1 = s2 */ +{ + int size = *s1++; + if (*s2++ != size) + return 0; + while (--size >= 0) + { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +int PBasic:: +P_subset(long *s1, long *s2) /* s1 <= s2 */ +{ + int sz1 = *s1++, sz2 = *s2++; + if (sz1 > sz2) + return 0; + while (--sz1 >= 0) + { + if (*s1++ & ~*s2++) + return 0; + } + return 1; +} + +long * PBasic:: +P_setcpy(long *d, long *l_s) /* d := s */ +{ + long *save_d = d; + +#ifdef SETCPY_MEMCPY + memcpy(d, l_s, (*l_s + 1) * sizeof(long)); +#else + int i = *l_s + 1; + while (--i >= 0) + *d++ = *l_s++; +#endif + return save_d; +} + +/* s is a "smallset", i.e., a 32-bit or less set stored + directly in a long. */ +long * PBasic:: +P_expset(long *d, long l_s) /* d := s */ +{ + if (l_s) + { + d[1] = l_s; + *d = 1; + } + else + *d = 0; + return d; +} + +long PBasic:: +P_packset(long *l_s) /* convert s to a small-set */ +{ + if (*l_s++) + return *l_s; + else + return 0; +} + +int PBasic:: +_OutMem(void) +{ + return _Escape(-2); +} + +int PBasic:: +_CaseCheck(void) +{ + return _Escape(-9); +} + +int PBasic:: +_NilCheck(void) +{ + return _Escape(-3); +} + +/* The following is suitable for the HP Pascal operating system. + It might want to be revised when emulating another system. */ + +char * PBasic:: +_ShowEscape(char *buf, int code, int ior, char *prefix) +{ + char *bufp; + + if (prefix && *prefix) + { + strcpy(buf, prefix); + strcat(buf, ": "); + bufp = buf + strlen(buf); + } + else + { + bufp = buf; + } + if (code == -10) + { + sprintf(bufp, "Pascal system I/O error %d", ior); + switch (ior) + { + case 3: + strcat(buf, " (illegal I/O request)"); + break; + case 7: + strcat(buf, " (bad file name)"); + break; + case FileNotFound: /*10 */ + strcat(buf, " (file not found)"); + break; + case FileNotOpen: /*13 */ + strcat(buf, " (file not open)"); + break; + case BadInputFormat: /*14 */ + strcat(buf, " (bad input format)"); + break; + case 24: + strcat(buf, " (not open for reading)"); + break; + case 25: + strcat(buf, " (not open for writing)"); + break; + case 26: + strcat(buf, " (not open for direct access)"); + break; + case 28: + strcat(buf, " (string subscript out of range)"); + break; + case EndOfFile: /*30 */ + strcat(buf, " (end-of-file)"); + break; + case FileWriteError: /*38 */ + strcat(buf, " (file write error)"); + break; + } + } + else + { + sprintf(bufp, "Pascal system error %d", code); + switch (code) + { + case -2: + strcat(buf, " (out of memory)"); + break; + case -3: + strcat(buf, " (reference to NIL pointer)"); + break; + case -4: + strcat(buf, " (integer overflow)"); + break; + case -5: + strcat(buf, " (divide by zero)"); + break; + case -6: + strcat(buf, " (real math overflow)"); + break; + case -8: + strcat(buf, " (value range error)"); + break; + case -9: + strcat(buf, " (CASE value range error)"); + break; + case -12: + strcat(buf, " (bus error)"); + break; + case -20: + strcat(buf, " (stopped by user)"); + break; + } + } + return buf; +} + +int PBasic:: +_Escape(int code) +{ + P_escapecode = code; + throw PBasicStop(); + + // following not used +} + +int PBasic:: +_EscIO(int code) +{ + P_ioresult = code; + return _Escape(-10); +} + +const std::map::value_type temp_tokens[] = { + std::map::value_type("+", PBasic::tokplus), + std::map::value_type("-", PBasic::tokminus), + std::map::value_type("*", PBasic::toktimes), + std::map::value_type("/", PBasic::tokdiv), + std::map::value_type("^", PBasic::tokup), + std::map::value_type("( or [", PBasic::toklp), + std::map::value_type(") or ]", PBasic::tokrp), + std::map::value_type("]", PBasic::tokcomma), + std::map::value_type(";", PBasic::toksemi), + std::map::value_type(":", PBasic::tokcolon), + std::map::value_type("=", PBasic::tokeq), + std::map::value_type("<", PBasic::toklt), + std::map::value_type("<=", PBasic::tokle), + std::map::value_type(">", PBasic::tokgt), + std::map::value_type(">=", PBasic::tokge), + std::map::value_type("and", PBasic::tokand), + std::map::value_type("or", PBasic::tokor), + std::map::value_type("xor", PBasic::tokxor), + std::map::value_type("not", PBasic::toknot), + std::map::value_type("mod", PBasic::tokmod), + std::map::value_type("sqr", PBasic::toksqr), + std::map::value_type("sqrt", PBasic::toksqrt), + std::map::value_type("ceil", PBasic::tokceil), + std::map::value_type("floor", PBasic::tokfloor), + std::map::value_type("sin", PBasic::toksin), + std::map::value_type("cos", PBasic::tokcos), + std::map::value_type("tan", PBasic::toktan), + std::map::value_type("arctan", PBasic::tokarctan), + std::map::value_type("log", PBasic::toklog), + std::map::value_type("exp", PBasic::tokexp), + std::map::value_type("abs", PBasic::tokabs), + std::map::value_type("sgn", PBasic::toksgn), + std::map::value_type("str$", PBasic::tokstr_), + std::map::value_type("val", PBasic::tokval), + std::map::value_type("chr$", PBasic::tokchr_), + std::map::value_type("eol$", PBasic::tokeol_), + std::map::value_type("eol_notab$", PBasic::tokeol_notab_), + std::map::value_type("no_newline$", PBasic::tokno_newline_), + std::map::value_type("asc", PBasic::tokasc), + std::map::value_type("len", PBasic::toklen), + std::map::value_type("mid$", PBasic::tokmid_), + std::map::value_type("peek", PBasic::tokpeek), + std::map::value_type("let", PBasic::toklet), + std::map::value_type("input", PBasic::tokinput), + std::map::value_type("goto", PBasic::tokgoto), + std::map::value_type("go to", PBasic::tokgoto), + std::map::value_type("if", PBasic::tokif), + std::map::value_type("end", PBasic::tokend), + std::map::value_type("stop", PBasic::tokstop), + std::map::value_type("for", PBasic::tokfor), + std::map::value_type("next", PBasic::toknext), + std::map::value_type("while", PBasic::tokwhile), + std::map::value_type("wend", PBasic::tokwend), + std::map::value_type("gosub", PBasic::tokgosub), + std::map::value_type("return", PBasic::tokreturn), + std::map::value_type("read", PBasic::tokread), + std::map::value_type("data", PBasic::tokdata), + std::map::value_type("restore", PBasic::tokrestore), + std::map::value_type("gotoxy", PBasic::tokgotoxy), + std::map::value_type("on", PBasic::tokon), + std::map::value_type("dim", PBasic::tokdim), + std::map::value_type("erase", PBasic::tokerase), + std::map::value_type("poke", PBasic::tokpoke), + std::map::value_type("list", PBasic::toklist), + std::map::value_type("run", PBasic::tokrun), + std::map::value_type("new", PBasic::toknew), + std::map::value_type("load", PBasic::tokload), + std::map::value_type("merge", PBasic::tokmerge), + std::map::value_type("save", PBasic::toksave), + std::map::value_type("bye", PBasic::tokbye), + std::map::value_type("quit", PBasic::tokbye), + std::map::value_type("del", PBasic::tokdel), + std::map::value_type("renum", PBasic::tokrenum), + std::map::value_type("then", PBasic::tokthen), + std::map::value_type("else", PBasic::tokelse), + std::map::value_type("to", PBasic::tokto), + std::map::value_type("step", PBasic::tokstep), + std::map::value_type("act", PBasic::tokact), + std::map::value_type("add_heading", PBasic::tokadd_heading), + std::map::value_type("alk", PBasic::tokalk), + std::map::value_type("aphi", PBasic::tokaphi), + std::map::value_type("calc_value", PBasic::tokcalc_value), + std::map::value_type("callback", PBasic::tokcallback), + std::map::value_type("cell_no", PBasic::tokcell_no), + std::map::value_type("change_por", PBasic::tokchange_por), + std::map::value_type("change_surf", PBasic::tokchange_surf), + std::map::value_type("charge_balance", PBasic::tokcharge_balance), + std::map::value_type("current_a", PBasic::tokcurrent_a), + std::map::value_type("debye_length", PBasic::tokdebye_length), + std::map::value_type("delta_h_phase", PBasic::tokdelta_h_phase), + std::map::value_type("delta_h_species", PBasic::tokdelta_h_species), + std::map::value_type("description", PBasic::tokdescription), + std::map::value_type("dh_a0", PBasic::tokdh_a0), + std::map::value_type("dh_a", PBasic::tokdh_a), + std::map::value_type("dh_av", PBasic::tokdh_av), + std::map::value_type("dh_b", PBasic::tokdh_b), + std::map::value_type("dh_bdot", PBasic::tokdh_bdot), + std::map::value_type("diff_c", PBasic::tokdiff_c), + std::map::value_type("dist", PBasic::tokdist), + std::map::value_type("edl", PBasic::tokedl), + std::map::value_type("edl_species", PBasic::tokedl_species), + std::map::value_type("eps_r", PBasic::tokeps_r), + std::map::value_type("eq_frac", PBasic::tokeq_frac), + std::map::value_type("equi", PBasic::tokequi), + std::map::value_type("equi_delta", PBasic::tokequi_delta), + std::map::value_type("equiv_frac", PBasic::tokeq_frac), + std::map::value_type("exists", PBasic::tokexists), + std::map::value_type("gamma", PBasic::tokgamma), + std::map::value_type("gas", PBasic::tokgas), + std::map::value_type("gas_p", PBasic::tokgas_p), + std::map::value_type("gas_vm", PBasic::tokgas_vm), + std::map::value_type("get", PBasic::tokget), + std::map::value_type("get_por", PBasic::tokget_por), + std::map::value_type("gfw", PBasic::tokgfw), +#if defined (PHREEQ98) || defined (MULTICHART) + std::map::value_type("graph_x", PBasic::tokgraph_x), + std::map::value_type("graph_y", PBasic::tokgraph_y), + std::map::value_type("graph_sy", PBasic::tokgraph_sy), +#endif + std::map::value_type("instr", PBasic::tokinstr), + std::map::value_type("iso", PBasic::tokiso), + std::map::value_type("iso_unit", PBasic::tokiso_unit), + std::map::value_type("iterations", PBasic::tokiterations), + std::map::value_type("kappa", PBasic::tokkappa), + std::map::value_type("kin", PBasic::tokkin), + std::map::value_type("kin_delta", PBasic::tokkin_delta), + std::map::value_type("kin_time", PBasic::tokkin_time), + std::map::value_type("kinetics_formula", PBasic::tokkinetics_formula), + std::map::value_type("kinetics_formula$", PBasic::tokkinetics_formula), + std::map::value_type("la", PBasic::tokla), + std::map::value_type("lg", PBasic::toklg), + std::map::value_type("list_s_s", PBasic::toklist_s_s), + std::map::value_type("lk_named", PBasic::toklk_named), + std::map::value_type("lk_phase", PBasic::toklk_phase), + std::map::value_type("lk_species", PBasic::toklk_species), + std::map::value_type("lm", PBasic::toklm), + std::map::value_type("log10", PBasic::toklog10), + std::map::value_type("ltrim", PBasic::tokltrim), + std::map::value_type("m0", PBasic::tokm0), + std::map::value_type("m", PBasic::tokm), + std::map::value_type("mcd_jtot", PBasic::tokmcd_jtot), + std::map::value_type("mcd_jconc", PBasic::tokmcd_jconc), + std::map::value_type("misc1", PBasic::tokmisc1), + std::map::value_type("misc2", PBasic::tokmisc2), + std::map::value_type("mol", PBasic::tokmol), + std::map::value_type("mu", PBasic::tokmu), + std::map::value_type("osmotic", PBasic::tokosmotic), + std::map::value_type("pad", PBasic::tokpad), + std::map::value_type("pad$", PBasic::tokpad_), + std::map::value_type("parm", PBasic::tokparm), + std::map::value_type("percent_error", PBasic::tokpercent_error), + std::map::value_type("phase_formula", PBasic::tokphase_formula), + std::map::value_type("phase_formula$", PBasic::tokphase_formula_), + std::map::value_type("phase_vm", PBasic::tokphase_vm), +#if defined MULTICHART + std::map::value_type("plot_xy", PBasic::tokplot_xy), +#endif + std::map::value_type("porevolume", PBasic::tokporevolume), + std::map::value_type("pot_v", PBasic::tokpot_v), + std::map::value_type("pr_p", PBasic::tokpr_p), + std::map::value_type("pr_phi", PBasic::tokpr_phi), + std::map::value_type("pressure", PBasic::tokpressure), + std::map::value_type("print", PBasic::tokprint), + std::map::value_type("punch", PBasic::tokpunch), + std::map::value_type("put", PBasic::tokput), + std::map::value_type("qbrn", PBasic::tokqbrn), + std::map::value_type("rem", PBasic::tokrem), + std::map::value_type("rho", PBasic::tokrho), + std::map::value_type("rho_0", PBasic::tokrho_0), + std::map::value_type("rtrim", PBasic::tokrtrim), + std::map::value_type("rxn", PBasic::tokrxn), + std::map::value_type("s_s", PBasic::toks_s), + std::map::value_type("sc", PBasic::toksc), + std::map::value_type("setdiff_c", PBasic::toksetdiff_c), + std::map::value_type("si", PBasic::toksi), + std::map::value_type("sim_no", PBasic::toksim_no), + std::map::value_type("sim_time", PBasic::toksim_time), + std::map::value_type("soln_vol", PBasic::toksoln_vol), + std::map::value_type("species_formula", PBasic::tokspecies_formula), + std::map::value_type("species_formula$", PBasic::tokspecies_formula_), + std::map::value_type("sr", PBasic::toksr), + std::map::value_type("step_no", PBasic::tokstep_no), + std::map::value_type("str_e$", PBasic::tokstr_e_), + std::map::value_type("str_f$", PBasic::tokstr_f_), + std::map::value_type("sum_gas", PBasic::toksum_gas), + std::map::value_type("sum_s_s", PBasic::toksum_s_s), + std::map::value_type("sum_species", PBasic::toksum_species), + std::map::value_type("surf", PBasic::toksurf), + std::map::value_type("sys", PBasic::toksys), + std::map::value_type("t_sc", PBasic::tokt_sc), + std::map::value_type("tc", PBasic::toktc), + std::map::value_type("time", PBasic::toktime), + std::map::value_type("title", PBasic::toktitle), + std::map::value_type("tk", PBasic::toktk), + std::map::value_type("tot", PBasic::toktot), + std::map::value_type("total_time", PBasic::toktotal_time), + std::map::value_type("totmol", PBasic::toktotmol), + std::map::value_type("totmole", PBasic::toktotmole), + std::map::value_type("totmoles", PBasic::toktotmoles), + std::map::value_type("trim", PBasic::toktrim), + std::map::value_type("viscos", PBasic::tokviscos), + std::map::value_type("viscos_0", PBasic::tokviscos_0), + std::map::value_type("vm", PBasic::tokvm), + /* PHAST */ + std::map::value_type("cell_pore_volume", PBasic::tokcell_pore_volume), + std::map::value_type("cell_porosity", PBasic::tokcell_porosity), + std::map::value_type("cell_saturation", PBasic::tokcell_saturation), + std::map::value_type("cell_volume", PBasic::tokcell_volume), + std::map::value_type("transport_cell_no", PBasic::toktransport_cell_no), + std::map::value_type("velocity_x", PBasic::tokvelocity_x), + std::map::value_type("velocity_y", PBasic::tokvelocity_y), + std::map::value_type("velocity_z", PBasic::tokvelocity_z), + /* Undocumented */ + std::map::value_type("sa_declercq", PBasic::toksa_declercq) +}; +std::map PBasic::command_tokens(temp_tokens, temp_tokens + sizeof temp_tokens / sizeof temp_tokens[0]); + +/* End. */ + diff --git a/phreeqcpp/PBasic.h b/phreeqcpp/PBasic.h new file mode 100644 index 00000000..749b7077 --- /dev/null +++ b/phreeqcpp/PBasic.h @@ -0,0 +1,559 @@ +#ifndef _INC_PBasic_H +#define _INC_PBasic_H +#if defined(PHREEQCI_GUI) +#include +#endif +#include +#include +#include +#include +#include +#include +#include "phrqtype.h" +#include "PHRQ_base.h" +#include "global_structures.h" +class Phreeqc; + +class PBasicStop : public std::exception +{ +}; + +#define forloop 0 +#define whileloop 1 +#define gosubloop 2 +#define checking true +#define varnamelen 20 +#define maxdims 4 +#define MAX_LINE 4096 +#define FileNotFound 10 +#define FileNotOpen 13 +#define FileWriteError 38 +#define BadInputFormat 14 +#define EndOfFile 30 +#define SETBITS 32 +#define Const + +typedef char varnamestring[varnamelen + 1]; + +/* Header file for code generated by "p2c", the Pascal-to-C translator */ + +/* "p2c" Copyright (C) 1989, 1990, 1991 Free Software Foundation. + * By Dave Gillespie, daveg@csvax.cs.caltech.edu. Version 1.20. + * This file may be copied, modified, etc. in any way. It is not restricted + * by the licence agreement accompanying p2c itself. + */ + +typedef struct varrec +{ + varnamestring name; + struct varrec *next; + long dims[maxdims]; + char numdims; + bool stringvar; + union + { + struct + { + LDBLE *arr; + LDBLE *val, rv; + } U0; + struct + { + char **sarr; + char **sval, *sv; + } U1; + } UU; +} varrec; + +typedef struct tokenrec +{ + struct tokenrec *next; + int kind; + union + { + struct varrec *vp; + LDBLE num; + char *sp; + char snch; + } UU; +//#ifdef PHREEQCI_GUI + size_t n_sz; + char *sz_num; +//#endif +} tokenrec; + +typedef struct linerec +{ + long num, num2; + tokenrec *txt; + char inbuf[MAX_LINE]; + struct linerec *next; +} linerec; + +class valrec +{ +public: + valrec() + { + stringval = false; + UU.val = 0; + } + ~valrec() {} + bool stringval; + union + { + LDBLE val; + char *sval; + } UU; +}; + +typedef struct looprec +{ + struct looprec *next; + linerec *homeline; + tokenrec *hometok; + int kind; + union + { + struct + { + varrec *vp; + LDBLE max, step; + } U0; + } UU; +} looprec; + +/* variables for exec: */ +struct LOC_exec +{ + bool gotoflag, elseflag; + tokenrec *t; +}; + +class PBasic: public PHRQ_base +{ +public: + PBasic(Phreeqc *ptr, PHRQ_io *phrq_io=NULL); + virtual ~PBasic(); + + enum BASIC_TOKEN + { + tokvar, + toknum, + tokstr, + toksnerr, + tokplus, + tokminus, + toktimes, + tokdiv, + tokup, + toklp, + tokrp, + tokcomma, + toksemi, + tokcolon, + tokeq, + toklt, + tokgt, + tokle, + tokge, + tokne, + tokand, + tokor, + tokxor, + tokmod, + toknot, + toksqr, + toksqrt, + toksin, + tokcos, + toktan, + tokarctan, + toklog, + toklog10, + tokexp, + tokabs, + toksgn, + tokstr_, + tokval, + tokchr_, + tokasc, + toklen, + tokmid_, + tokpeek, + tokrem, + toklet, + tokinput, + tokgoto, + tokif, + tokend, + tokstop, + tokfor, + toknext, + tokwhile, + tokwend, + tokgosub, + tokreturn, + tokread, + tokdata, + tokrestore, + tokgotoxy, + tokon, + tokdim, + tokpoke, + toklist, + tokrun, + toknew, + tokload, + tokmerge, + tokbye, + tokdel, + tokrenum, + tokthen, + tokelse, + tokto, + tokstep, + /* start phreeqc */ + tokact, + tokadd_heading, + tokalk, + tokaphi, + tokcalc_value, + tokceil, + tokcell_no, + tokchange_por, + tokchange_surf, + tokcharge_balance, + tokcurrent_a, + tokdebye_length, + tokdelta_h_phase, + tokdelta_h_species, + tokdescription, + tokdh_a, + tokdh_a0, + tokdh_av, + tokdh_b, + tokdh_bdot, + tokdiff_c, + tokdist, + tokedl, + tokedl_species, + tokeol_, + tokeol_notab_, + tokeps_r, + tokeq_frac, + tokequiv_frac, + tokequi, + tokequi_delta, + tokerase, + tokexists, + tokfloor, + tokgamma, + tokgas, + tokgas_p, + tokgas_vm, + tokget, + tokget_por, + tokgfw, + tokgraph_x, + tokgraph_y, + tokgraph_sy, + tokinstr, + tokiso, + tokiso_unit, + tokiterations, + tokkappa, + tokkin, + tokkin_delta, + tokkin_time, + tokkinetics_formula, + tokkinetics_formula_, + tokla, + toklg, + toklist_s_s, + toklk_named, + toklk_phase, + toklk_species, + toklm, + tokltrim, + tokm, + tokm0, + tokmcd_jtot, + tokmcd_jconc, + tokmisc1, + tokmisc2, + tokmol, + tokmu, + tokno_newline_, + tokosmotic, + tokpad_, + tokpad, + tokparm, + tokpercent_error, + tokphase_formula, + tokphase_formula_, + tokphase_vm, + tokplot_xy, + tokpot_v, + tokpr_p, + tokpr_phi, + tokpressure, + tokprint, + tokpunch, + tokput, + tokqbrn, + tokrho, + tokrho_0, + tokrtrim, + tokrxn, + toks_s, + toksave, + toksc, + toksetdiff_c, + toksi, + toksim_no, + toksim_time, + toksoln_vol, + tokspecies_formula, + tokspecies_formula_, + toksr, + tokstep_no, + tokstr_e_, + tokstr_f_, + toksum_gas, + toksum_s_s, + toksum_species, + toksurf, + toksys, + tokt_sc, + toktc, + toktime, + toktitle, + toktk, + toktot, + toktotal_time, + toktotmole, + toktotmol, + toktotmoles, + toktrim, + tokviscos, + tokviscos_0, + tokvm, + /* end phreeqc */ + toksa_declercq, // Undocumented function + tokcallback, // PHAST function + tokcell_pore_volume, // PHAST function + tokporevolume, // PHAST function + tokcell_porosity, // PHAST function + tokcell_saturation, // PHAST function + tokcell_volume, // PHAST function + toktransport_cell_no, // PHAST function + tokvelocity_x, // PHAST function + tokvelocity_y, // PHAST function + tokvelocity_z // PHAST function + }; + +#if !defined(PHREEQCI_GUI) + enum IDErr + { + IDS_ERR_ARRAY_ALREADY, + IDS_ERR_BAD_SUBSCRIPT, + IDS_ERR_EXTRA, + IDS_ERR_FOR_WO_NEXT, + IDS_ERR_ILLEGAL, + IDS_ERR_INFINITE_LOOP, + IDS_ERR_INPUT_NOTLEGAL, + IDS_ERR_MISMATCH, + IDS_ERR_MISSING_Q, + IDS_ERR_MISSING_RP, + IDS_ERR_NEXT_WO_FOR, + IDS_ERR_OUT_OF_DATA, + IDS_ERR_RETURN_WO_GOSUB, + IDS_ERR_SYNTAX, + IDS_ERR_UNDEF_LINE, + IDS_ERR_WEND_WO_WHILE, + IDS_ERR_WHILE_WO_WEND + }; +#endif + + // Methods + bool Get_phreeqci_gui(void) const {return phreeqci_gui;}; + void Set_phreeqci_gui(bool tf) {phreeqci_gui = tf;}; + bool Get_parse_all(void) const {return parse_all;}; + void Set_parse_all(bool tf) {parse_all = tf;}; + bool Get_parse_whole_program(void) const {return parse_whole_program;}; + void Set_parse_whole_program(bool tf) {parse_whole_program = tf;}; + int Get_nErrLineNumber(void) const {return nErrLineNumber;}; + void Set_nErrLineNumber(int i) {nErrLineNumber = i;}; +#if defined(PHREEQCI_GUI) + UINT Get_nIDErrPrompt(void)const {return nIDErrPrompt;}; + void Set_nIDErrPrompt(UINT u) {nIDErrPrompt = u;}; + int Get_P_escapecode(void)const {return P_escapecode;}; + void Set_P_escapecode(int code) {P_escapecode = code;}; + HANDLE Get_hInfiniteLoop(void) const {return hInfiniteLoop;}; + void Set_hInfiniteLoop(HANDLE h) {hInfiniteLoop = h;}; +#endif + int free_dim_stringvar(varrec *varbase); + + void exec(void); + int basic_renumber(char *commands, void **lnbase, void **vbase, void **lpbase); + void restoredata(void); + void clearloops(void); + void clearvar(varrec * v); + void clearvars(void); + char * numtostr(char * Result, LDBLE n); + void parse(char * inbuf, tokenrec ** buf); + void listtokens(FILE * f, tokenrec * buf); + void disposetokens(tokenrec ** tok); + void parseinput(tokenrec ** buf); + void errormsg(const char * s); + void snerr(const char * s); + void tmerr(const char * s); + void badsubscr(void); + LDBLE realfactor(struct LOC_exec *LINK); + char * strfactor(struct LOC_exec * LINK); + char *stringfactor(char * Result, struct LOC_exec *LINK); + const char *stringfactor(std::string & Result, struct LOC_exec * LINK); + long intfactor(struct LOC_exec *LINK); + LDBLE realexpr(struct LOC_exec *LINK); + char * strexpr(struct LOC_exec * LINK); + char * stringexpr(char * Result, struct LOC_exec * LINK); + long intexpr(struct LOC_exec *LINK); + void require(int k, struct LOC_exec *LINK); + void skipparen(struct LOC_exec *LINK); + varrec * findvar(struct LOC_exec *LINK); + valrec factor(struct LOC_exec *LINK); + valrec upexpr(struct LOC_exec * LINK); + valrec term(struct LOC_exec * LINK); + valrec sexpr(struct LOC_exec * LINK); + valrec relexpr(struct LOC_exec * LINK); + valrec andexpr(struct LOC_exec * LINK); + valrec expr(struct LOC_exec *LINK); + void checkextra(struct LOC_exec *LINK); + bool iseos(struct LOC_exec *LINK); + void skiptoeos(struct LOC_exec *LINK); + linerec * findline(long n); + linerec * mustfindline(long n); + void cmdend(struct LOC_exec *LINK); + void cmdnew(struct LOC_exec *LINK); + void cmdlist(struct LOC_exec *LINK); + void cmdload(bool merging, char * name, struct LOC_exec *LINK); + void cmdrun(struct LOC_exec *LINK); + void cmdsave(struct LOC_exec *LINK); + void cmdput(struct LOC_exec *LINK); + void cmdchange_por(struct LOC_exec *LINK); + void cmdchange_surf(struct LOC_exec *LINK); + void cmdbye(void); + void cmddel(struct LOC_exec *LINK); + void cmdrenum(struct LOC_exec *LINK); + void cmdprint(struct LOC_exec *LINK); + void cmdpunch(struct LOC_exec *LINK); +#if defined PHREEQ98 || defined MULTICHART + void cmdgraph_x(struct LOC_exec *LINK); + void cmdgraph_y(struct LOC_exec *LINK); + void cmdgraph_sy(struct LOC_exec *LINK); +#endif +#if defined MULTICHART + void cmdplot_xy(struct LOC_exec *LINK); +#endif + void cmdlet(bool implied, struct LOC_exec *LINK); + void cmdgoto(struct LOC_exec *LINK); + void cmdif(struct LOC_exec *LINK); + void cmdelse(struct LOC_exec *LINK); + bool skiploop(int up, int dn, struct LOC_exec *LINK); + void cmdfor(struct LOC_exec *LINK); + void cmdnext(struct LOC_exec *LINK); + void cmdwhile(struct LOC_exec *LINK); + void cmdwend(struct LOC_exec *LINK); + void cmdgosub(struct LOC_exec *LINK); + void cmdreturn(struct LOC_exec *LINK); + void cmdread(struct LOC_exec *LINK); + void cmddata(struct LOC_exec *LINK); + void cmdrestore(struct LOC_exec *LINK); + void cmdgotoxy(struct LOC_exec *LINK); + void cmdon(struct LOC_exec *LINK); + void cmddim(struct LOC_exec *LINK); + void cmderase(struct LOC_exec *LINK); + void cmdpoke(struct LOC_exec *LINK); + int basic_main(const char *commands); + int basic_compile(const char *commands, void **lnbase, void **vbase, void **lpbase); + int basic_run(char *commands, void *lnbase, void *vbase, void *lpbase); + int basic_init(void); + int sget_logical_line(const char **ptr, int *l, char *return_line); + long my_labs(long x); + void * my_memmove(void * d, Const void * s, size_t n); + void * my_memcpy(void * d, Const void * s, size_t n); + int my_memcmp(Const void * s1, Const void * s2, size_t n); + void * my_memset(void * d, int c, size_t n); + int my_toupper(int c); + int my_tolower(int c); + long ipow(long a, long b); + char * strsub(char *ret, char *s, int pos, + int len); + int strpos2(char *s, char *pat, int pos); + int strcicmp(char *s1, char *s2); + char * strltrim(char *s); + char * strrtrim(char *s); + void strmove(int len, char *s, int spos, + char *d, int dpos); + void strinsert(char *src, char *dst, int pos); + int P_peek(FILE * f); + int P_eof(void); + int P_eoln(FILE * f); + void P_readpaoc(FILE * f, char *s, int len); + void P_readlnpaoc(FILE * f, char *s, int len); + long P_maxpos(FILE * f); + char * P_trimname(char * fn, int len); + long memavail(void); + long maxavail(void); + long * P_setunion(long *d, long *s1, long *s2); + long * P_setint(long *d, long *s1, long *s2); + long * P_setdiff(long *d, long *s1, long *s2); + long * P_setxor(long *d, long *s1, long *s2); + long * P_addset(long *s, unsigned val); +// long * P_addsetr(long *s, unsigned v1, unsigned v2); + long * P_remset(long *s, unsigned val); + int P_setequal(long *s1, long *s2); + int P_subset(long *s1, long *s2); + long * P_setcpy(long *d, long *s); + long * P_expset(long *d, long s); + long P_packset(long *s); + int _OutMem(void); + int _CaseCheck(void); + int _NilCheck(void); + static char * _ShowEscape(char *buf, int code, int ior, char *prefix); + int _Escape(int code); + int _EscIO(int code); + + // data members +protected: + Phreeqc * PhreeqcPtr; + char *inbuf; + linerec *linebase; + varrec *varbase; + looprec *loopbase; + long curline; + linerec *stmtline, *dataline; + tokenrec *stmttok, *datatok, *buf; + bool exitflag; + long EXCP_LINE; + static std::map command_tokens; + int P_escapecode; + int P_ioresult; + + bool phreeqci_gui; + bool parse_all; /* true, most function values set to 1 for testing compilation */ + bool parse_whole_program; + char fnbuf[256]; +#if defined(PHREEQCI_GUI) + HANDLE hInfiniteLoop; + UINT nIDErrPrompt; +#else + IDErr nIDErrPrompt; +#endif + int nErrLineNumber; + bool punch_tab; + bool skip_punch; +}; + +#endif /* _INC_PBasic_H */ diff --git a/phreeqcpp/PHRQ_io_output.cpp b/phreeqcpp/PHRQ_io_output.cpp new file mode 100644 index 00000000..5eaeb5c1 --- /dev/null +++ b/phreeqcpp/PHRQ_io_output.cpp @@ -0,0 +1,411 @@ +#include +#include "Phreeqc.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (state == TRANSPORT && transport_warnings == FALSE) + return (OK); + if (state == ADVECTION && advection_warnings == FALSE) + return (OK); + count_warnings++; + if (pr.warnings >= 0) + { + if (count_warnings > pr.warnings) + return (OK); + } + if (phrq_io) + { + if (status_on) + { + phrq_io->screen_msg("\n"); + } + std::ostringstream msg; + msg << "WARNING: " << err_str; + phrq_io->warning_msg(msg.str().c_str()); + status_on = false; + } + + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +echo_msg(const char *str) +/* ---------------------------------------------------------------------- */ +{ + if (pr.echo_input == TRUE) + { + if (phrq_io) phrq_io->echo_msg(str); + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_forward_output_to_log(int value) +/* ---------------------------------------------------------------------- */ +{ + forward_output_to_log = value; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_forward_output_to_log(void) +/* ---------------------------------------------------------------------- */ +{ + return forward_output_to_log; +} + +void Phreeqc:: +fpunchf_heading(const char *name) +{ + if (pr.punch == TRUE && current_selected_output != NULL) + { + punch_msg(name); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, double d) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(const std::bad_alloc&) + { + malloc_error(); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, char * s) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, s); + } + catch(const std::bad_alloc&) + { + malloc_error(); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, int d) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(const std::bad_alloc&) + { + malloc_error(); + } +} + +void Phreeqc:: +fpunchf_user(int user_index, const char *format, double d) +{ + const char *name; + + if (current_user_punch == NULL) + return; + // check headings + //if (user_index < user_punch_count_headings) + int user_punch_count_headings = (int) current_user_punch->Get_headings().size(); + if (user_index < user_punch_count_headings) + { + //name = user_punch_headings[user_index]; + name = current_user_punch->Get_headings()[user_index].c_str(); + } + else + { + if (fpunchf_user_s_warning == 0) + { + error_string = sformatf( + "USER_PUNCH: Headings count does not match number of calls to PUNCH.\n"); + warning_msg(error_string); + fpunchf_user_s_warning = 1; + } + sprintf(fpunchf_user_buffer, "no_heading_%d", + (user_index - user_punch_count_headings) + 1); + name = fpunchf_user_buffer; + } + try + { + if (phrq_io) phrq_io->fpunchf(name, format, (double) d); + } + catch(const std::bad_alloc&) + { + malloc_error(); + } +} + +void Phreeqc:: +fpunchf_user(int user_index, const char *format, char * d) +{ + const char *name; + + if (current_user_punch == NULL) + return; + int user_punch_count_headings = (int) current_user_punch->Get_headings().size(); + // check headings + if (user_index < user_punch_count_headings) + { + //name = user_punch_headings[user_index]; + name = current_user_punch->Get_headings()[user_index].c_str(); + } + else + { + if (fpunchf_user_s_warning == 0) + { + error_string = sformatf( + "USER_PUNCH: Headings count does not match number of calls to PUNCH.\n"); + warning_msg(error_string); + fpunchf_user_s_warning = 1; + } + sprintf(fpunchf_user_buffer, "no_heading_%d", + (user_index - user_punch_count_headings) + 1); + name = fpunchf_user_buffer; + } + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(const std::bad_alloc&) + { + malloc_error(); + } +} + +int Phreeqc:: +fpunchf_end_row(const char *format) +{ + if (phrq_io) + { + phrq_io->fpunchf_end_row(format); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +screen_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ +#ifndef TESTING + if (phrq_io) phrq_io->screen_msg(err_str); +#endif +} +// ---------------------------------------------------------------------- */ +// dump file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +dump_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->dump_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_msg(str); +} +// ---------------------------------------------------------------------- */ +// error file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +error_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->error_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->error_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->error_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + if (get_input_errors() <= 0) + input_error = 1; + if (phrq_io) + { + std::ostringstream msg; + msg << "ERROR: " << err_str << "\n"; + + phrq_io->output_msg(msg.str().c_str()); + phrq_io->log_msg(msg.str().c_str()); + + if (status_on) + { + phrq_io->screen_msg("\n"); + } + status_on = false; + phrq_io->error_msg(msg.str().c_str(), stop); + } + + if (stop) + { + throw PhreeqcStop(); + } +} +// ---------------------------------------------------------------------- */ +// log file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +log_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->log_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_msg(str); +} +// ---------------------------------------------------------------------- */ +// output_temp file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +output_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->output_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->output_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->output_close(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + { + if (get_forward_output_to_log()) + { + phrq_io->log_msg(str); + } + else + { + phrq_io->output_msg(str); + } + } +} +// ---------------------------------------------------------------------- */ +// punch file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +punch_open(const char *file_name, int n_user) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->punch_open(file_name, std::ios_base::out, n_user); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_close(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_msg(str); +} diff --git a/phreeqcpp/PPassemblage.cxx b/phreeqcpp/PPassemblage.cxx new file mode 100644 index 00000000..8b9b830e --- /dev/null +++ b/phreeqcpp/PPassemblage.cxx @@ -0,0 +1,375 @@ +// PPassemblage.cxx: implementation of the cxxPPassemblage class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "PPassemblage.h" +#include "cxxMix.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPPassemblage::cxxPPassemblage(PHRQ_io * io) + // + // default constructor for cxxPPassemblage + // +: cxxNumKeyword(io) +{ + new_def = false; + eltList.type = cxxNameDouble::ND_ELT_MOLES; +} + +cxxPPassemblage::cxxPPassemblage(const std::map < int, + cxxPPassemblage > &entities, cxxMix & mix, + int l_n_user, PHRQ_io * io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + eltList.type = cxxNameDouble::ND_ELT_MOLES; + this->new_def = false; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxPPassemblage *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } +} + +cxxPPassemblage::~cxxPPassemblage() +{ +} + +void +cxxPPassemblage::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // PPassemblage element and attributes + s_oss << indent0; + s_oss << "eltList.dump_xml(s_oss, indent + 1); + + // ppAssemblageComps + s_oss << indent1; + s_oss << "::const_iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + (*it).second.dump_xml(s_oss, indent + 2); + } +} + +void +cxxPPassemblage::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // PPassemblage element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "EQUILIBRIUM_PHASES_RAW " << n_user_local << " " << this-> + description << "\n"; + + + s_oss << indent1 << "# EXCHANGE_MODIFY candidates; use new_def=true #\n"; + s_oss << indent1 << "-new_def " << 0 << "\n"; + for (std::map < std::string, cxxPPassemblageComp >::const_iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + s_oss << indent1; + s_oss << "-component " << (*it).second.Get_name() << "\n"; + (*it).second.dump_raw(s_oss, indent + 2); + } + s_oss << indent1; + s_oss << "-eltList # List of all elements in phases and alternate reactions\n"; + this->eltList.dump_raw(s_oss, indent + 2); + + s_oss << indent1 << "# PPassemblage workspace variables #\n"; + s_oss << indent1 << "-assemblage_totals" << "\n"; + this->assemblage_totals.dump_raw(s_oss, indent + 1); +} + +void +cxxPPassemblage::read_raw(CParser & parser, bool check) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read PPassemblage number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser. + error_msg("Unknown input in EQUILIBRIUM_PHASES_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // eltList + if (this->eltList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 0; + break; + case 1: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxPPassemblageComp temp_comp(this->io); + temp_comp.Set_name(str.c_str()); + cxxPPassemblageComp *comp_ptr = this->Find(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + this->pp_assemblage_comps[str] = temp_comp; + useLastLine = true; + } + } + break; + case 2: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for new_def in PPassemblage.", + PHRQ_io::OT_CONTINUE); + } + break; + case 3: // assemblage_totals + if (this->assemblage_totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for PPassemblage totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 3; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} + +void +cxxPPassemblage::totalize(Phreeqc * phreeqc_ptr) +{ + this->assemblage_totals.clear(); + // component structures + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + (*it).second.totalize(phreeqc_ptr); + this->assemblage_totals.add_extensive((*it).second.Get_totals(), 1.0); + } + return; +} +std::set +cxxPPassemblage::GetPhases(Phreeqc * phreeqc_ptr) +{ + std::set phase_list; + // component structures + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + int l; + phase * phase_ptr = phreeqc_ptr->phase_bsearch((*it).second.Get_name().c_str(), &l, FALSE);; + if (phase_ptr != NULL) + { + phase_list.insert(phase_ptr->name); + } + } + return phase_list; +} +void +cxxPPassemblage::add(const cxxPPassemblage & addee, LDBLE extensive) + // + // Add to existing ppassemblage to "this" ppassemblage + // +{ + if (extensive == 0.0) + return; + for (std::map < std::string, cxxPPassemblageComp >::const_iterator itadd = addee.pp_assemblage_comps.begin(); + itadd != addee.pp_assemblage_comps.end(); ++itadd) + { + bool found = false; + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + this->pp_assemblage_comps.begin(); + it != this->pp_assemblage_comps.end(); ++it) + { + if ((*it).second.Get_name() == itadd->second.Get_name()) + { + (*it).second.add((*itadd).second, extensive); + found = true; + break; + } + } + if (!found) + { + cxxPPassemblageComp entity = (*itadd).second; + entity.multiply(extensive); + std::string str(entity.Get_name()); + this->pp_assemblage_comps[str] = entity; + } + } + //cxxNameDouble eltList; + this->eltList.add_extensive(addee.eltList, extensive); +} +cxxPPassemblageComp * cxxPPassemblage:: +Find(const std::string name_in) +{ + cxxPPassemblageComp * comp = NULL; + std::map::iterator it; + it = this->pp_assemblage_comps.begin(); + for ( ; it != this->pp_assemblage_comps.end(); it++) + { + if (Utilities::strcmp_nocase(name_in.c_str(), it->first.c_str()) == 0) + { + comp = &it->second; + break; + } + } + return comp; +} +/* ---------------------------------------------------------------------- */ +void +cxxPPassemblage::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + /* int n_user; */ + ints.push_back(this->n_user); + ints.push_back(this->new_def ? 1 : 0); + ints.push_back((int) this->pp_assemblage_comps.size()); + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + this->pp_assemblage_comps.begin(); it != this->pp_assemblage_comps.end(); + it++) + { + (*it).second.Serialize(dictionary, ints, doubles); + } + this->eltList.Serialize(dictionary, ints, doubles); + this->assemblage_totals.Serialize(dictionary, ints, doubles); +} + +/* ---------------------------------------------------------------------- */ +void +cxxPPassemblage::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + /* int n_user; */ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->new_def = (ints[ii++] != 0); + int count = ints[ii++]; + this->pp_assemblage_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxPPassemblageComp ppc(this->io); + ppc.Deserialize(dictionary, ints, doubles, ii, dd); + std::string str(ppc.Get_name()); + this->pp_assemblage_comps[str] = ppc; + } + this->eltList.Deserialize(dictionary, ints, doubles, ii, dd); + this->assemblage_totals.Deserialize(dictionary, ints, doubles, ii, dd); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("eltlist"), // 0 + std::vector< std::string >::value_type("component"), // 1 + std::vector< std::string >::value_type("new_def"), // 2 + std::vector< std::string >::value_type("assemblage_totals") // 3 +}; +const std::vector< std::string > cxxPPassemblage::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/PPassemblage.h b/phreeqcpp/PPassemblage.h new file mode 100644 index 00000000..868599c5 --- /dev/null +++ b/phreeqcpp/PPassemblage.h @@ -0,0 +1,70 @@ +#if !defined(PPASSEMBLAGE_H_INCLUDED) +#define PPASSEMBLAGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "PPassemblageComp.h" +class cxxMix; + +class cxxPPassemblage:public cxxNumKeyword +{ + + public: + cxxPPassemblage(PHRQ_io * io=NULL); + cxxPPassemblage(const std::map < int, cxxPPassemblage > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io=NULL); + ~cxxPPassemblage(); + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + const cxxNameDouble & Get_assemblage_totals() const + { + return this->assemblage_totals; + }; + const cxxNameDouble & Get_eltList() const + { + return this->eltList; + }; + void Set_eltList(cxxNameDouble & nd) {this->eltList = nd;} + std::map & Get_pp_assemblage_comps() + { + return this->pp_assemblage_comps; + }; + const std::map & Get_pp_assemblage_comps() const + { + return this->pp_assemblage_comps; + }; + void Set_pp_assemblage_comps(std::map & c) + { + this->pp_assemblage_comps = c; + }; + bool Get_new_def(void) const {return this->new_def;} + void Set_new_def(bool tf) {this->new_def = tf;} + + cxxPPassemblageComp *Find(const std::string name); + std::set GetPhases(Phreeqc * phreeqc_ptr); + void totalize(Phreeqc * phreeqc_ptr); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + void add(const cxxPPassemblage & addee, LDBLE extensive); + // not written + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + +protected: + bool new_def; + std::map pp_assemblage_comps; + cxxNameDouble eltList; // list of elements in phases (and alternate reactions) + cxxNameDouble assemblage_totals; // after totalize, total moles of elements in the PPassemblage + const static std::vector < std::string > vopts; +}; + +#endif // !defined(PPASSEMBLAGE_H_INCLUDED) diff --git a/phreeqcpp/PPassemblageComp.cxx b/phreeqcpp/PPassemblageComp.cxx new file mode 100644 index 00000000..4e95552c --- /dev/null +++ b/phreeqcpp/PPassemblageComp.cxx @@ -0,0 +1,441 @@ +// PPassemblageComp.cxx: implementation of the cxxPPassemblageComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "PPassemblageComp.h" +#include "Dictionary.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPPassemblageComp::cxxPPassemblageComp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxPPassemblageComp + // +{ + si = 0; + si_org = 0; + moles = 10; + delta = 0; + initial_moles = 0; + force_equality = false; + dissolve_only = false; + precipitate_only = false; +} + +cxxPPassemblageComp::~cxxPPassemblageComp() +{ +} + +void +cxxPPassemblageComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Pure_Phase element and attributes + + s_oss << indent0 << "name=\"" << this->name << "\"" << "\n"; + s_oss << indent0 << "add_formula=\"" << this->add_formula << "\"" << "\n"; + s_oss << indent0 << "si=\"" << this->si << "\"" << "\n"; + s_oss << indent0 << "si_org=\"" << this->si_org << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "delta=\"" << this->delta << "\"" << "\n"; + s_oss << indent0 << "initial_moles=\"" << this-> + initial_moles << "\"" << "\n"; + s_oss << indent0 << "force_equality=\"" << this-> + force_equality << "\"" << "\n"; + s_oss << indent0 << "dissolve_only=\"" << this-> + dissolve_only << "\"" << "\n"; + s_oss << indent0 << "precipitate_only=\"" << this-> + precipitate_only << "\"" << "\n"; + +} + +void +cxxPPassemblageComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Pure_Phase element and attributes + s_oss << indent1 << "# EQUILIBRIUM_PHASES_MODIFY candidate identifiers #\n"; + if (this->add_formula.size() != 0) + s_oss << indent1 << "-add_formula " << this->add_formula << "\n"; + s_oss << indent1 << "-si " << this->si << "\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + s_oss << indent1 << "-force_equality " << this->force_equality << "\n"; + s_oss << indent1 << "-dissolve_only " << this->dissolve_only << "\n"; + s_oss << indent1 << "-precipitate_only " << this->precipitate_only << "\n"; + + s_oss << indent1 << "# PPassemblage workspace variables #\n"; + s_oss << indent1 << "-si_org " << this->si_org << "\n"; + s_oss << indent1 << "-delta " << this->delta << "\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; + s_oss << indent1 << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} + +void +cxxPPassemblageComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool si_defined(false); + bool moles_defined(false); + bool delta_defined(false); + bool initial_moles_defined(false); + bool dissolve_only_defined(false); + bool force_equality_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name + parser.warning_msg("-name ignored. Name is defined with -component."); + break; + + case 1: // add_formula + if (!(parser.get_iss() >> str)) + { + this->add_formula.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for add_formula.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->add_formula = str; + } + break; + + case 2: // si + if (!(parser.get_iss() >> this->si)) + { + this->si = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for si.", + PHRQ_io::OT_CONTINUE); + } + si_defined = true; + break; + + case 3: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 4: // delta + if (!(parser.get_iss() >> this->delta)) + { + this->delta = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for delta.", + PHRQ_io::OT_CONTINUE); + } + delta_defined = true; + break; + + case 5: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + initial_moles_defined = true; + break; + + + case 6: // dissolve_only + if (!(parser.get_iss() >> this->dissolve_only)) + { + this->dissolve_only = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for dissolve_only.", + PHRQ_io::OT_CONTINUE); + } + dissolve_only_defined = true; + if (this->dissolve_only) + { + this->precipitate_only = false; + } + break; + + case 7: // force_equality + if (!(parser.get_iss() >> this->force_equality)) + { + this->force_equality = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for force_equality.", + PHRQ_io::OT_CONTINUE); + } + force_equality_defined = true; + break; + + case 8: // precipitate_only + if (!(parser.get_iss() >> this->precipitate_only)) + { + this->precipitate_only = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for precipitate_only.", + PHRQ_io::OT_CONTINUE); + } + if (this->precipitate_only) + { + this->dissolve_only = false; + } + break; + case 9: // si_org + if (!(parser.get_iss() >> this->si_org)) + { + this->si_org = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for si_org.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Surface totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 10; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (si_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Si not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (delta_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Delta not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (initial_moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Initial_moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (dissolve_only_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dissolve_only not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (force_equality_defined == false) + { + parser.incr_input_error(); + parser.error_msg + ("Force_equality not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxPPassemblageComp::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + if (this->add_formula.size() != 0) + return; + class phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(this->name.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, this->moles); + } + else + { + assert(false); + } + return; +} + + +void +cxxPPassemblageComp::add(const cxxPPassemblageComp & addee, LDBLE extensive) +{ + LDBLE ext1, ext2, f1, f2; + if (extensive == 0.0) + return; + if (addee.name.size() == 0) + return; + // this and addee must have same name + // otherwise generate a new PPassemblagComp with multiply + + ext1 = this->moles; + ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + + if (this->name.size() == 0 && addee.name.size() == 0) + { + return; + } + assert(this->name == addee.name); + if (this->add_formula != addee.add_formula) + { + std::ostringstream oss; + oss << + "Cannot mix two Equilibrium_phases with differing add_formulae., " + << this->name; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + this->si = this->si * f1 + addee.si * f2; + this->si_org = this->si_org * f1 + addee.si_org * f2; + this->moles += addee.moles * extensive; + this->delta += addee.delta * extensive; + this->initial_moles += addee.initial_moles * extensive; +} + +void +cxxPPassemblageComp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + this->delta *= extensive; + this->initial_moles *= extensive; +} +void +cxxPPassemblageComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->name)); + ints.push_back(dictionary.Find(this->add_formula)); + doubles.push_back(this->si); + doubles.push_back(this->si_org); + doubles.push_back(this->moles); + doubles.push_back(this->delta); + doubles.push_back(this->initial_moles); + ints.push_back( this->force_equality ? 1 : 0); + ints.push_back(this->dissolve_only ? 1 : 0); + ints.push_back(this->precipitate_only ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxPPassemblageComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->add_formula = dictionary.GetWords()[ints[ii++]]; + this->si = doubles[dd++]; + this->si_org = doubles[dd++]; + this->moles = doubles[dd++]; + this->delta = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->force_equality = (ints[ii++] != 0); + this->dissolve_only = (ints[ii++] != 0); + this->precipitate_only = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("add_formula"), // 1 + std::vector< std::string >::value_type("si"), // 2 + std::vector< std::string >::value_type("moles"), // 3 + std::vector< std::string >::value_type("delta"), // 4 + std::vector< std::string >::value_type("initial_moles"), // 5 + std::vector< std::string >::value_type("dissolve_only"), // 6 + std::vector< std::string >::value_type("force_equality"), // 7 + std::vector< std::string >::value_type("precipitate_only"), // 8 + std::vector< std::string >::value_type("si_org"), // 9 + std::vector< std::string >::value_type("totals") // 10 +}; +const std::vector< std::string > cxxPPassemblageComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/PPassemblageComp.h b/phreeqcpp/PPassemblageComp.h new file mode 100644 index 00000000..318707c1 --- /dev/null +++ b/phreeqcpp/PPassemblageComp.h @@ -0,0 +1,83 @@ +#if !defined(PPASSEMBLAGECOMP_H_INCLUDED) +#define PPASSEMBLAGECOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxPPassemblageComp: public PHRQ_base +{ + + public: + cxxPPassemblageComp(PHRQ_io *io=NULL); + virtual ~cxxPPassemblageComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const {return this->name;} + void Set_name(const char * s) + { + if(s != NULL) + this->name = std::string(s); + else + this->name.clear(); + } + const std::string &Get_add_formula() const {return this->add_formula;} + void Set_add_formula(const char * s) + { + if(s != NULL) + this->add_formula = std::string(s); + else + this->add_formula.clear(); + } + + void totalize(Phreeqc * phreeqc_ptr); + const cxxNameDouble & Get_totals() const {return (this->totals);} + void Get_totals(cxxNameDouble & nd) {this->totals = nd;} + LDBLE Get_si() const {return this->si;} + void Set_si(LDBLE t) {this->si = t;} + LDBLE Get_si_org() const {return this->si_org;} + void Set_si_org(LDBLE t) {this->si_org = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_delta() const {return this->delta;} + void Set_delta(LDBLE t) {this->delta = t;} + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + + bool Get_force_equality() const {return this->force_equality;} + void Set_force_equality(bool tf) {this->force_equality = tf;} + bool Get_dissolve_only() const {return this->dissolve_only;} + void Set_dissolve_only(bool tf) {this->dissolve_only = tf;} + bool Get_precipitate_only() const {return this->precipitate_only;} + void Set_precipitate_only(bool tf) {this->precipitate_only = tf;} + + void add(const cxxPPassemblageComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + std::string add_formula; + LDBLE si; + LDBLE si_org; + LDBLE moles; + LDBLE delta; + LDBLE initial_moles; + bool force_equality; + bool dissolve_only; + bool precipitate_only; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(PPASSEMBLAGECOMP_H_INCLUDED) diff --git a/phreeqcpp/Phreeqc.cpp b/phreeqcpp/Phreeqc.cpp new file mode 100644 index 00000000..00febed2 --- /dev/null +++ b/phreeqcpp/Phreeqc.cpp @@ -0,0 +1,2077 @@ +#include "Phreeqc.h" +#include // std::replace + +#include "NameDouble.h" +#include "Solution.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Exchange.h" +#include "Surface.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "phqalloc.h" +#include "PBasic.h" +#include "Temperature.h" +#include "SSassemblage.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +//const const_iso Phreeqc::iso_defaults[] = { +// {"13C", -10, 1}, +// {"13C(4)", -10, 1}, +// {"13C(-4)", -50, 5}, +// {"34S", 10, 1}, +// {"34S(6)", 10, 1}, +// {"34S(-2)", -30, 5}, +// {"2H", -28, 1}, +// {"2H(1)", -28, 1}, +// {"2H(0)", -28, 1}, +// {"18O", -5, .1}, +// {"18O(-2)", -5, .1}, +// {"18O(0)", -5, .1}, +// {"87Sr", .71, .01}, +// {"11B", 20, 5} +//}; +const const_iso Phreeqc::iso_defaults[] = { + const_iso("13C", -10, 1), + const_iso("13C(4)", -10, 1), + const_iso("13C(-4)", -50, 5), + const_iso("34S", 10, 1), + const_iso("34S(6)", 10, 1), + const_iso("34S(-2)", -30, 5), + const_iso("2H", -28, 1), + const_iso("2H(1)", -28, 1), + const_iso("2H(0)", -28, 1), + const_iso("18O", -5, .1), + const_iso("18O(-2)", -5, .1), + const_iso("18O(0)", -5, .1), + const_iso("87Sr", .71, .01), + const_iso("11B", 20, 5) +}; + +const int Phreeqc::count_iso_defaults = (sizeof(iso_defaults) / sizeof(class const_iso)); + +Phreeqc::~Phreeqc(void) +{ + + clean_up(); + + PHRQ_free_all(); + if (phrq_io == &ioInstance) + { + this->phrq_io->clear_istream(); + this->phrq_io->close_ostreams(); + } +} + +void Phreeqc::set_phast(int tf) +{ + this->phast = tf; +} +size_t Phreeqc::list_components(std::list &list_c) +/* + * Find all elements in any class definition + */ +{ + cxxNameDouble accumulator; + //accumulator.add("H", 1); + //accumulator.add("O", 1); + + // solutions + { + std::map::const_iterator cit = Rxn_solution_map.begin(); + for (; cit != Rxn_solution_map.end(); cit++) + { + cxxSolution entity(cit->second); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // irreversible reactions + { + std::map::const_iterator cit = Rxn_reaction_map.begin(); + for (; cit != Rxn_reaction_map.end(); cit++) + { + cxxReaction r_ptr(cit->second); + reaction_calc(&r_ptr); + accumulator.add_extensive(r_ptr.Get_elementList(), 1.0); + } + } + + // pure phases + { + std::map::const_iterator cit = Rxn_pp_assemblage_map.begin(); + for (; cit != Rxn_pp_assemblage_map.end(); cit++) + { + cxxPPassemblage entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_eltList(), 1.0); + } + } + // exchangers + { + std::map::const_iterator cit = Rxn_exchange_map.begin(); + for (; cit != Rxn_exchange_map.end(); cit++) + { + cxxExchange entity = cit->second; + entity.totalize(); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // surfaces + { + std::map::const_iterator cit = Rxn_surface_map.begin(); + for (; cit != Rxn_surface_map.end(); cit++) + { + cxxSurface entity = cit->second; + entity.totalize(); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // gas phases + { + std::map::const_iterator cit = Rxn_gas_phase_map.begin(); + for (; cit != Rxn_gas_phase_map.end(); cit++) + { + cxxGasPhase entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // solid-solutions + { + std::map::const_iterator cit = Rxn_ss_assemblage_map.begin(); + for (; cit != Rxn_ss_assemblage_map.end(); cit++) + { + cxxSSassemblage entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // kinetics + { + std::map::iterator it = Rxn_kinetics_map.begin(); + for (; it != Rxn_kinetics_map.end(); it++) + { + calc_dummy_kinetic_reaction_tally(&(it->second)); + cxxKinetics entity = it->second; + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // Put in all primaries + cxxNameDouble::iterator it; + for (it = accumulator.begin(); it != accumulator.end(); it++) + { + if (it->first == "Charge") continue; + char string[MAX_LENGTH]; + strcpy(string, it->first.c_str()); + class master *master_ptr = master_bsearch_primary(string); + if (master_ptr == NULL) continue; + if (master_ptr->type != AQ) continue; + accumulator.add(master_ptr->elt->name, 1); + } + // print list + for (it = accumulator.begin(); it != accumulator.end(); it++) + { + class master *master_ptr = master_bsearch(it->first.c_str()); + if (master_ptr == NULL) continue; + if (master_ptr->type != AQ) continue; + if (master_ptr->primary == 0) continue; + if (it->first == "Charge") continue; + if (it->first == "O") continue; + if (it->first == "H") continue; + list_c.push_back(it->first); + } + return(list_c.size()); +} +size_t Phreeqc::list_EquilibriumPhases(std::list &list_pp) +/* +* Find all elements in any class definition +*/ +{ + std::set accumulator; + // pure phases + { + std::map::const_iterator cit = Rxn_pp_assemblage_map.begin(); + for (; cit != Rxn_pp_assemblage_map.end(); cit++) + { + cxxPPassemblage entity = cit->second; + std::set pp = entity.GetPhases(this); + std::set::iterator ppit = pp.begin(); + for (; ppit != pp.end(); ppit++) + { + accumulator.insert(*ppit); + } + } + } + list_pp.clear(); + std::set::iterator it = accumulator.begin(); + for (; it != accumulator.end(); it++) + { + list_pp.insert(list_pp.end(),*it); + } + return(list_pp.size()); +} +size_t Phreeqc::list_GasComponents(std::list &list_gc) +/* +* Find all elements in any class definition +*/ +{ + std::set accumulator; + // pure phases + { + std::map::const_iterator cit = Rxn_gas_phase_map.begin(); + for (; cit != Rxn_gas_phase_map.end(); cit++) + { + cxxGasPhase entity = cit->second; + std::vector &gc = entity.Get_gas_comps(); + for (size_t i = 0; i < gc.size(); i++) + { + int j; + phase * p = phase_bsearch(gc[i].Get_phase_name().c_str(), &j, 0); + accumulator.insert(p->name); + } + } + } + list_gc.clear(); + std::set::iterator it = accumulator.begin(); + for (; it != accumulator.end(); it++) + { + list_gc.insert(list_gc.end(), *it); + } + return(list_gc.size()); +} +size_t Phreeqc::list_KineticReactions(std::list &list_kr) +/* +* Find all kinetic reactions +*/ +{ + std::set accumulator; + // Kinetics + { + std::map::const_iterator cit = Rxn_kinetics_map.begin(); + for (; cit != Rxn_kinetics_map.end(); cit++) + { + cxxKinetics entity = cit->second; + for (size_t i = 0; i < entity.Get_kinetics_comps().size(); i++) + { + std::string ratename = entity.Get_kinetics_comps()[i].Get_rate_name(); + int j; + rate *r = rate_search(ratename.c_str(), &j); + if (r != NULL) + { + accumulator.insert(r->name); + } + } + } + } + list_kr.clear(); + std::set::iterator it = accumulator.begin(); + for (; it != accumulator.end(); it++) + { + list_kr.insert(list_kr.end(), *it); + } + return(list_kr.size()); +} +size_t Phreeqc::list_SolidSolutions(std::list &list_comps, std::list &list_names) +/* +* Find all elements in any class definition +*/ +{ + std::vector< std::set > ss_sets; + std::vector ss_names; + // solid solutions + std::map::const_iterator cit = Rxn_ss_assemblage_map.begin(); + // Fill vectors, ss names and related set of component names + for (; cit != Rxn_ss_assemblage_map.end(); cit++) + { + cxxSSassemblage entity = cit->second; + std::map &SSs = entity.Get_SSs(); + std::map::iterator ssit = SSs.begin(); + for (; ssit != SSs.end(); ssit++) + { + std::string ssname = ssit->second.Get_name(); + std::set accumulator_phases; + for (size_t i = 0; i < ssit->second.Get_ss_comps().size(); i++) + { + std::string pname = ssit->second.Get_ss_comps()[i].Get_name(); + int j; + phase * p = phase_bsearch(pname.c_str(), &j, 0); + accumulator_phases.insert(p->name); + } + ss_names.push_back(ssname); + ss_sets.push_back(accumulator_phases); + } + } + // need to merge into exclusive sets of solid solution components + bool repeat = true; + while (repeat) + { + repeat = false; + for (int i = 0; i < (int) ss_sets.size() - 1; i++) + { + for (int j = i + 1; j < (int) ss_sets.size(); j++) + { + // locate any common component + std::set::iterator it = ss_sets[j].begin(); + for (; it != ss_sets[j].end(); it++) + { + if (ss_sets[i].find(*it) != ss_sets[i].end()) + { + repeat = true; + break; + } + } + // merge sets and clear second set + if (repeat) + { + for (it = ss_sets[j].begin(); it != ss_sets[j].end(); it++) + { + ss_sets[i].insert(*it); + } + ss_sets[j].clear(); + break; + } + } + if (repeat) break; + } + } + list_comps.clear(); + list_names.clear(); + // Write lists + for (size_t i = 0; i < ss_sets.size(); i++) + { + std::set::iterator it = ss_sets[i].begin(); + for (; it != ss_sets[i].end(); it++) + { + list_names.push_back(ss_names[i]); + list_comps.push_back(*it); + } + } + return(list_comps.size()); +} +size_t Phreeqc::list_Surfaces(std::list &list_surftype, std::list &list_surfname) +/* +* Find all surface types and surfaces +*/ +{ + std::set > accumulator; + // Surfaces + { + std::map::const_iterator cit = Rxn_surface_map.begin(); + for (; cit != Rxn_surface_map.end(); cit++) + { + cxxSurface entity = cit->second; + std::vector &scomps = entity.Get_surface_comps(); + //std::vector &scharges = entity.Get_surface_charges(); + for (size_t i = 0; i < scomps.size(); i++) + { + std::pair p(scomps[i].Get_master_element(), scomps[i].Get_charge_name()); + accumulator.insert(p); + } + } + } + list_surftype.clear(); + list_surfname.clear(); + std::set >::iterator it = accumulator.begin(); + for (; it != accumulator.end(); it++) + { + list_surftype.push_back(it->first); + list_surfname.push_back(it->second); + } + return(list_surfname.size()); +} +size_t Phreeqc::list_Exchangers(std::list &list_exname) +/* +* Find all exchangers +*/ +{ + std::set accumulator; + // Exchangers + std::map::const_iterator cit = Rxn_exchange_map.begin(); + for (; cit != Rxn_exchange_map.end(); cit++) + { + cxxExchange entity = cit->second; + std::vector &ecomps = entity.Get_exchange_comps(); + for (size_t i = 0; i < ecomps.size(); i++) + { + std::string exname = ""; + cxxNameDouble nd = ecomps[i].Get_totals(); + cxxNameDouble::iterator it = nd.begin(); + for (; it != nd.end(); it++) + { + class master *m = master_bsearch(it->first.c_str()); + if (m != NULL) + { + if (m->type == EX) + { + exname = it->first; + break; + } + } + } + if (exname != "") + { + accumulator.insert(exname); + } + } + } + list_exname.clear(); + std::set< std::string>::iterator it = accumulator.begin(); + for (; it != accumulator.end(); it++) + { + list_exname.push_back(*it); + } + return(list_exname.size()); +} +Phreeqc::Phreeqc(PHRQ_io *io) +{ + user_print = NULL; + sformatf_buffer = NULL; + basic_interpreter = NULL; + count_elts = 0; + aphi = NULL; + // phrq_io + if (io) + { + this->phrq_io = io; + } + else + { + this->phrq_io = &this->ioInstance; + } + // auto PHRQ_io ioInstance; + + // initialize data members + init(); + +#if defined(SWIG) || defined(SWIG_IPHREEQC) + basicCallback = NULL; +#endif +} +void Phreeqc::init(void) +{ + same_model = FALSE; + current_tc = NAN; + current_pa = NAN; + current_mu = NAN; + mu_terms_in_logk = true; + current_A = 0.0; + current_x = 0.0; + fix_current = 0.0; + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ +/* + * last model + */ + last_model.force_prep = true; + last_model.gas_phase_type = cxxGasPhase::GP_UNKNOWN; + last_model.gas_phase.clear(); + last_model.ss_assemblage.clear(); + last_model.pp_assemblage.clear(); + last_model.add_formula.clear(); + last_model.si.clear(); + last_model.dl_type = cxxSurface::NO_DL; + last_model.surface_type = cxxSurface::UNKNOWN_DL; + + current_selected_output = NULL; + current_user_punch = NULL; + high_precision = false; + MIN_LM = -30.0; /* minimum log molality allowed before molality set to zero */ + LOG_ZERO_MOLALITY = -30; /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ + MIN_RELATED_LOG_ACTIVITY = -30; + MIN_TOTAL = 1e-25; + MIN_TOTAL_SS = MIN_TOTAL/100; + MIN_RELATED_SURFACE = MIN_TOTAL*100; + // auto Rxn_temperature_map; + // auto Rxn_pressure_map; + + /* ---------------------------------------------------------------------- + * Surface + * --------------------------------------------------------------------- */ + g_iterations = -1; + G_TOL = 1e-8; + // auto Rxn_surface_map; + // auto charge_group_map; + change_surf_count = 0; + change_surf = NULL; + /* ---------------------------------------------------------------------- + * Exchange + * ---------------------------------------------------------------------- */ + // auto Rxn_exchange_map; + + /* ---------------------------------------------------------------------- + * Kinetics + * ---------------------------------------------------------------------- */ + // auto Rxn_kinetics_map; + + /*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ + save_init(-1); // set initial save values + + /*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ + count_inverse = 0; + /*---------------------------------------------------------------------- + * Mix + *---------------------------------------------------------------------- */ + // auto Rxn_mix_map; + // auto Dispersion_mix_map; + // auto Rxn_solution_mix_map; + // auto Rxn_exchange_mix_map; + // auto Rxn_gas_phase_mix_map; + // auto Rxn_kinetics_mix_map; + // auto Rxn_pp_assemblage_mix_map; + // auto Rxn_ss_assemblage_mix_map; + // auto Rxn_surface_mix_map; + /*---------------------------------------------------------------------- + * Irreversible reaction + *---------------------------------------------------------------------- */ + run_cells_one_step = false; + // auto Rxn_reaction_map; + /*---------------------------------------------------------------------- + * Gas phase + *---------------------------------------------------------------------- */ + // auto Rxn_gas_phase_map; + /*---------------------------------------------------------------------- + * Solid solution + *---------------------------------------------------------------------- */ + // auto Rxn_ss_assemblage_map; + /*---------------------------------------------------------------------- + * Pure-phase assemblage + *---------------------------------------------------------------------- */ + // auto Rxn_pp_assemblage_map; + /*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ + /*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ + + /*---------------------------------------------------------------------- + * Solution + *---------------------------------------------------------------------- */ + // auto Rxn_solution_map; + // auto unnumbered_solutions; + save_species = false; + /*---------------------------------------------------------------------- + * Global solution + *---------------------------------------------------------------------- */ + new_x = FALSE; + tc_x = 0; + tk_x = 0; + patm_x = 1; + last_patm_x = 1; + potV_x = 0; + numerical_fixed_volume = false; + force_numerical_fixed_volume = false; + //switch_numerical = false; + ph_x = 0; + solution_pe_x = 0; + mu_x = 0; + ah2o_x = 1.0; + density_x = 0; + total_h_x = 0; + total_o_x = 0; + cb_x = 0; + total_ions_x = 0; + mass_water_aq_x = 0; + mass_water_surfaces_x = 0; + mass_water_bulk_x = 0; + // auto pe_x + // auto isotopes_x + // auto default_pe_x + dl_type_x = cxxSurface::NO_DL; + total_carbon = 0; + total_co2 = 0; + total_alkalinity = 0; + gfw_water = 0; + step_x = 0; + kin_time_x = 0; + /*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ + count_cells = 1; + count_shifts = 1; + ishift = 1; + bcon_first = bcon_last = 3; + correct_disp = FALSE; + tempr = 2.0; + timest = 0.0; + simul_tr = 0; + diffc = 0.3e-9; + heat_diffc = -0.1; + cell = 0; + mcd_substeps = 1.0; + print_modulus = 1; + punch_modulus = 1; + dump_in = FALSE; + dump_modulus = 0; + transport_warnings = TRUE; + old_cells = 0; + max_cells = 0; + all_cells = 0; + multi_Dflag = FALSE; + interlayer_Dflag = FALSE; + implicit = FALSE; + max_mixf = 1.0; + min_dif_LM = -30.0; + default_Dw = 0; + correct_Dw = 0; + multi_Dpor = 0; + interlayer_Dpor = 0.1; + multi_Dpor_lim = 0; + interlayer_Dpor_lim = 0; + multi_Dn = 0; + interlayer_tortf = 100.0; + cell_no = 0; + fix_current = 0.0; + /*---------------------------------------------------------------------- + * Advection data + *---------------------------------------------------------------------- */ + count_ad_cells = 1; + count_ad_shifts = 1; + print_ad_modulus = 1; + punch_ad_modulus = 1; + advection_kin_time = 0.0; + advection_kin_time_defined = FALSE; + advection_warnings = TRUE; + /*---------------------------------------------------------------------- + * Tidy data + *---------------------------------------------------------------------- */ + new_model = TRUE; + new_exchange = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_copy = FALSE; + new_pitzer = FALSE; + /*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ + element_h_one = NULL; + /*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ + count_elts = 0; + /*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ + s_h2o = NULL; + s_hplus = NULL; + s_h3oplus = NULL; + s_eminus = NULL; + s_co3 = NULL; + s_h2 = NULL; + s_o2 = NULL; + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + + /*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + + /*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ + count_unknowns = 0; + max_unknowns = 0; + ah2o_unknown = NULL; + alkalinity_unknown = NULL; + carbon_unknown = NULL; + charge_balance_unknown = NULL; + exchange_unknown = NULL; + mass_hydrogen_unknown = NULL; + mass_oxygen_unknown = NULL; + mb_unknown = NULL; + mu_unknown = NULL; + pe_unknown = NULL; + ph_unknown = NULL; + pure_phase_unknown = NULL; + solution_phase_boundary_unknown = NULL; + surface_unknown = NULL; + gas_unknown = NULL; + ss_unknown = NULL; + // auto gas_unknowns; + /*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ + // struct trxn; + for (int i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = 0; + } + for (int i = 0; i < 3; i++) + { + trxn.dz[i] = 0; + } + count_trxn = 0; + + /* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ + pr.all = TRUE; + pr.initial_solutions = TRUE; + pr.initial_exchangers = TRUE; + pr.reactions = TRUE; + pr.gas_phase = TRUE; + pr.ss_assemblage = TRUE; + pr.pp_assemblage = TRUE; + pr.surface = TRUE; + pr.exchange = TRUE; + pr.kinetics = TRUE; + pr.totals = TRUE; + pr.eh = TRUE; + pr.species = TRUE; + pr.saturation_indices = TRUE; + pr.irrev = TRUE; + pr.mix = TRUE; + pr.reaction = TRUE; + pr.use = TRUE; + pr.logfile = FALSE; + pr.punch = TRUE; + pr.status = TRUE; + pr.inverse = TRUE; + pr.dump = TRUE; + pr.user_print = TRUE; + pr.headings = TRUE; + pr.user_graph = TRUE; + pr.echo_input = TRUE; + pr.warnings = 100; + pr.initial_isotopes = TRUE; + pr.isotope_ratios = TRUE; + pr.isotope_alphas = TRUE; + pr.hdf = FALSE; + pr.alkalinity = FALSE; + status_on = true; +#ifdef NPP + status_interval = 40; +#else + status_interval = 250; +#endif + status_timer = clock(); + count_warnings = 0; + /* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ + rate_m = 0; + rate_m0 = 0; + rate_time = 0; + rate_kin_time = 1.0; + rate_sim_time_start = 0; + rate_sim_time_end = 0; + rate_sim_time = 0; + rate_moles = 0; + initial_total_time = 0; + // auto rate_p + count_rate_p = 0; + /* ---------------------------------------------------------------------- + * USER PRINT COMMANDS + * ---------------------------------------------------------------------- */ + //user_print = NULL; + n_user_punch_index = 0; + fpunchf_user_s_warning = 0; + fpunchf_user_buffer[0] = 0; + +#if defined PHREEQ98 + class rate *user_graph; + char **user_graph_headings; + int user_graph_count_headings; +#endif +#if defined MULTICHART + // auto chart_handler; + chart_handler.Set_io(phrq_io); +#endif + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + error_string = NULL; + simulation = 0; + state = INITIALIZE; + reaction_step = 0; + transport_step = 0; + transport_start = 0; + advection_step = 0; + stop_program = FALSE; + incremental_reactions = FALSE; + count_strings = 0; + input_error = 0; + next_keyword = Keywords::KEY_NONE; + parse_error = 0; + paren_count = 0; + iterations = 0; + gamma_iterations = 0; + density_iterations = 0; + run_reactions_iterations= 0; + overall_iterations = 0; + max_line = MAX_LINE; + line = NULL; + line_save = NULL; + LOG_10 = log(10.0); + debug_model = FALSE; + debug_prep = FALSE; + debug_set = FALSE; + debug_mass_action = FALSE; + debug_mass_balance = FALSE; + debug_diffuse_layer = FALSE; + debug_inverse = FALSE; +#ifdef USE_LONG_DOUBLE + /* from float.h, sets tolerance for cl1 routine */ + inv_tol_default = pow((long double) 10, (long double) -LDBL_DIG + 5); +#else + inv_tol_default = pow((double) 10, (double) -DBL_DIG + 5); +#endif + itmax = 100; + max_tries = 1000; +#ifdef USE_LONG_DOUBLE + /* from float.h, sets tolerance for cl1 routine */ + ineq_tol = pow((long double) 10, (long double) -LDBL_DIG); +#elif NPP +// appt: + ineq_tol = pow((double) 10, (double) -DBL_DIG + 2); +#else + ineq_tol = pow((double) 10, (double) -DBL_DIG); +#endif + convergence_tolerance = 1e-8; + step_size = 100.; + pe_step_size = 10.; + step_size_now = step_size; + pe_step_size_now = pe_step_size; + pp_scale = 1.0; + pp_column_scale = 1.0; + diagonal_scale = FALSE; + mass_water_switch = FALSE; + delay_mass_water = FALSE; + equi_delay = 0; + dampen_ah2o = false; + censor = 0.0; + aqueous_only = 0; + negative_concentrations = FALSE; + calculating_deriv = FALSE; + numerical_deriv = FALSE; + count_total_steps = 0; + phast = FALSE; + output_newline = true; + //selected_output_file_name = NULL; + dump_file_name = NULL; + remove_unstable_phases = FALSE; + // auto screen_string; + spread_length = 10; + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + initial_solution_isotopes = FALSE; + + phreeqc_mpi_myself = 0; + first_read_input = TRUE; + print_density = 0; + print_viscosity = 0; + cell_pore_volume = 0; + cell_volume = 0; + cell_porosity = 0; + cell_saturation = 0; + sys_tot = 0; + + V_solutes = 0.0; + viscos = 0.0; + viscos_0 = 0.0; + viscos_0_25 = 0.0; + rho_0 = 0.0; + kappa_0 = 0.0; + p_sat = 0.0; + eps_r = EPSILON; + DH_A = 0.0; + DH_B = 0.0; + DH_Av = 0.0; + QBrn = 0.0; + ZBrn = 0.0; + dgdP = 0.0; + + need_temp_msg = 0; + solution_mass = 0; + solution_volume = 0; + /* phqalloc.cpp ------------------------------- */ + s_pTail = NULL; + /* Basic */ + //basic_interpreter = NULL; + basic_callback_ptr = NULL; + basic_callback_cookie = NULL; + basic_fortran_callback_ptr = NULL; + +#ifdef SKIP + /* dw.cpp ------------------------------- */ + /* COMMON /QQQQ/ */ + Q0 = 0; + Q5 = 0; + GASCON = 0.461522e0; + TZ = 647.073e0; + AA = 1.e0; + Z = 0; + DZ = 0; + Y = 0; + G1 = 11.e0; + G2 = 44.333333333333e0; + GF = 3.5e0; + B1 = 0; + B2 = 0; + B1T = 0; + B2T = 0; + B1TT = 0; + B2TT = 0; +#endif + /* gases.cpp ------------------------------- */ + a_aa_sum = 0; + b2 = 0; + b_sum = 0; + R_TK = 0; + /* input.cpp ------------------------------- */ + check_line_return = 0; + reading_db = FALSE; + /* integrate.cpp ------------------------------- */ + midpoint_sv = 0; + z_global = 0; + xd_global = 0; + alpha_global = 0; + /* integrate.cpp ------------------------------- */ + max_row_count = 50; + max_column_count = 50; + carbon = FALSE; + count_rows = 0; + count_optimize = 0; + col_phases = 0; + col_redox = 0; + col_epsilon = 0; + col_ph = 0; + col_water = 0; + col_isotopes = 0; + col_phase_isotopes = 0; + row_mb = 0; + row_fract = 0; + row_charge = 0; + row_carbon = 0; + row_isotopes = 0; + row_epsilon = 0; + row_isotope_epsilon = 0; + row_water = 0; + klmd = 0; + nklmd = 0; + n2d = 0; + kode = 0; + iter = 0; + toler = 0; + error = 0; + max_pct = 0; + scaled_error = 0; + master_alk = NULL; + max_good = 0; + max_bad = 0; + max_minimal = 0; + count_good = 0; + count_bad = 0; + count_minimal = 0; + count_calls = 0; + soln_bits = 0; + phase_bits = 0; + current_bits = 0; + temp_bits = 0; + netpath_file = NULL; + count_inverse_models = 0; + count_pat_solutions = 0; + for (int i = 0; i < 32; i++) + { + min_position[i] = 0; + max_position[i] = 0; + now[i] = 0; + } + /* kinetics.cpp ------------------------------- */ + count_pp = count_pg = count_ss = 0; + cvode_kinetics_ptr = NULL; + cvode_test = FALSE; + cvode_error = FALSE; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = NULL; + kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save= NULL; + cvode_ss_assemblage_save= NULL; + set_and_run_attempt = 0; + /* model.cpp ------------------------------- */ + gas_in = FALSE; + min_value = 1e-10; + + /* phrq_io_output.cpp ------------------------------- */ + forward_output_to_log = 0; + /* phreeqc_files.cpp ------------------------------- */ +#ifdef NPP + default_data_base = "c:\\phreeqc\\database\\phreeqc.dat"; +#else + default_data_base = "phreeqc.dat"; +#endif + /* Pitzer */ + pitzer_model = FALSE; + sit_model = FALSE; + pitzer_pe = FALSE; + full_pitzer = FALSE; + always_full_pitzer = FALSE; + ICON = TRUE; + IC = -1; + COSMOT = 0; + AW = 0; + VP = 0; + DW0 = 0; + // auto pitz_param_map + use_etheta = TRUE; + OTEMP = -100.; + OPRESS = -100.; + A0 = 0; + cations = NULL; + anions = NULL; + neutrals = NULL; + count_cations = 0; + count_anions = 0; + count_neutrals = 0; + MAXCATIONS = 0; + FIRSTANION = 0; + MAXNEUTRAL = 0; + mcb0 = NULL; + mcb1 = NULL; + mcc0 = NULL; + for (int i = 0; i < 23; i++) + { + BK[i] = 0.0; + DK[i] = 0.0; + } + dummy = 0; + /* print.cpp ------------------------------- */ + if (sformatf_buffer != NULL) + { + sformatf_buffer = (char*)free_check_null(sformatf_buffer); + } + sformatf_buffer = (char *) PHRQ_calloc(256 , sizeof(char)); + if (sformatf_buffer == NULL) + malloc_error(); + sformatf_buffer_size = 256; + /* read.cpp */ + prev_next_char = NULL; +#if defined PHREEQ98 + int shifts_as_points; +#endif + /* read_class.cxx */ + // auto dump_info + // auto delete_info + // auto run_info + run_info.Set_io(phrq_io); + /* readtr.cpp */ + // auto dump_file_name_cpp; + /* sit.cpp ------------------------------- */ + + sit_A0 = 0; + sit_count_cations = 0; + sit_count_anions = 0; + sit_count_neutrals = 0; + sit_MAXCATIONS = 0; + sit_FIRSTANION = 0; + sit_MAXNEUTRAL = 0; + /* tidy.cpp ------------------------------- */ + a0 = 0; + a1 = 0; + kc = 0; + kb = 0; + /* tally.cpp ------------------------------- */ + t_buffer = NULL; + tally_count_component = 0; + //tally_table = NULL; + count_tally_table_columns = 0; + count_tally_table_rows = 0; + /* transport.cpp ------------------------------- */ + sol_D = NULL; + sol_D_dbg = NULL; + J_ij = NULL; + J_ij_il = NULL; + J_ij_count_spec = 0; + m_s = NULL; + count_m_s = 0; + tot1_h = 0; + tot1_o = 0; + tot2_h = 0; + tot2_o = 0; + diffc_max = 0; + diffc_tr = 0; + J_ij_sum = 0; + transp_surf = FALSE; + heat_mix_array = NULL; + temp1 = NULL; + temp2 = NULL; + nmix = 0; + heat_nmix = 0; + heat_mix_f_imm = 0; + heat_mix_f_m = 0; + warn_MCD_X = 0; + warn_fixed_Surf = 0; + /* utilities.cpp ------------------------------- */ + spinner = 0; + // keycount; + keycount.resize(Keywords::KEY_COUNT_KEYWORDS); + for (int i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + { + keycount[i] = 0; + } + + return; +} +/*-----------------------------------------------------*/ +Phreeqc::Phreeqc(const Phreeqc &src) +{ + user_print = NULL; + sformatf_buffer = NULL; + basic_interpreter = NULL; + count_elts = 0; + aphi = NULL; + //this->phrq_io = src.phrq_io; + this->phrq_io = &this->ioInstance; + this->init(); + this->initialize(); + InternalCopy(&src); +} +void +Phreeqc::InternalCopy(const Phreeqc* pSrc) +{ + // phrq_io + //this->phrq_io = new PHRQ_io; + same_model = FALSE; + current_tc = pSrc->current_tc; + current_pa = pSrc->current_pa; + current_mu = pSrc->current_mu; + mu_terms_in_logk = pSrc->mu_terms_in_logk; + + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ + //last_model, accept init + high_precision = pSrc->high_precision; + // Maps + Rxn_temperature_map = pSrc->Rxn_temperature_map; + Rxn_pressure_map = pSrc->Rxn_pressure_map; + g_iterations = -1; + G_TOL = pSrc->G_TOL; + Rxn_surface_map = pSrc->Rxn_surface_map; + change_surf_count = pSrc->change_surf_count; + change_surf = change_surf_alloc(change_surf_count + 1); + for (int ii = 0; ii < change_surf_count; ii++) + { + change_surf[ii].comp_name = string_hsave(pSrc->change_surf[ii].comp_name); + change_surf[ii].fraction = pSrc->change_surf[ii].fraction; + change_surf[ii].new_comp_name = string_hsave(pSrc->change_surf[ii].new_comp_name); + change_surf[ii].new_Dw = pSrc->change_surf[ii].new_Dw; + change_surf[ii].cell_no = pSrc->change_surf[ii].cell_no; + change_surf[ii].next = pSrc->change_surf[ii].next; + } + Rxn_exchange_map = pSrc->Rxn_exchange_map; + Rxn_kinetics_map = pSrc->Rxn_kinetics_map; + use_kinetics_limiter = pSrc->use_kinetics_limiter; + save_values = pSrc->save_values; + save = pSrc->save; + //class copier copy_solution; + //class copier copy_pp_assemblage; + //class copier copy_exchange; + //class copier copy_surface; + //class copier copy_ss_assemblage; + //class copier copy_gas_phase; + //class copier copy_kinetics; + //class copier copy_mix; + //class copier copy_reaction; + //class copier copy_temperature; + //class copier copy_pressure; + // Inverse not implemented + //std::vector inverse; + count_inverse = 0; + // Mix + Rxn_mix_map = pSrc->Rxn_mix_map; + Dispersion_mix_map = pSrc->Dispersion_mix_map; + Rxn_solution_mix_map = pSrc->Rxn_solution_mix_map; + Rxn_exchange_mix_map = pSrc->Rxn_exchange_mix_map; + Rxn_gas_phase_mix_map = pSrc->Rxn_gas_phase_mix_map; + Rxn_kinetics_mix_map = pSrc->Rxn_kinetics_mix_map; + Rxn_pp_assemblage_mix_map = pSrc->Rxn_pp_assemblage_mix_map; + Rxn_ss_assemblage_mix_map = pSrc->Rxn_ss_assemblage_mix_map; + Rxn_surface_mix_map = pSrc->Rxn_surface_mix_map; + //List new definitions + //std::set Rxn_new_exchange; + //std::set Rxn_new_gas_phase; + //std::set Rxn_new_kinetics; // not used + //std::set Rxn_new_mix; // not used + //std::set Rxn_new_pp_assemblage; + //std::set Rxn_new_pressure; // not used + //std::set Rxn_new_reaction; // not used + //std::set Rxn_new_solution; + //std::set Rxn_new_ss_assemblage; + //std::set Rxn_new_surface; + //std::set Rxn_new_temperature; // not used + Rxn_reaction_map = pSrc->Rxn_reaction_map; + Rxn_gas_phase_map = pSrc->Rxn_gas_phase_map; + Rxn_ss_assemblage_map = pSrc->Rxn_ss_assemblage_map; + Rxn_pp_assemblage_map = pSrc->Rxn_pp_assemblage_map; + + std::vector species_list; + // will be rebuilt + //std::vector sum_jacob0; + //std::vector sum_mb1; + //std::vector sum_jacob1; + //std::vector sum_mb2; + //std::vector sum_jacob2; + //std::vector sum_delta; + // Solution + Rxn_solution_map = pSrc->Rxn_solution_map; + unnumbered_solutions = pSrc->unnumbered_solutions; + save_species = pSrc->save_species; + // Global solution + title_x = pSrc->title_x; + last_title_x = pSrc->last_title_x; + //new_x = FALSE; + description_x = pSrc->description_x; + //new_x = FALSE; + description_x = pSrc->description_x; + //tc_x = 0; + //tk_x = 0; + //patm_x = 1; + //last_patm_x = 1; + //numerical_fixed_volume = false; + //force_numerical_fixed_volume = false; + //ph_x = 0; + //solution_pe_x = 0; + //mu_x = 0; + //ah2o_x = 1.0; + //density_x = 0; + //total_h_x = 0; + //total_o_x = 0; + //cb_x = 0; + //total_ions_x = 0; + //mass_water_aq_x = 0; + //mass_water_surfaces_x = 0; + //mass_water_bulk_x = 0; + //units_x + //pe_x + //isotopes_x + //default_pe_x + //dl_type_x = cxxSurface::NO_DL; + //total_carbon = 0; + //total_co2 = 0; + //total_alkalinity = 0; + gfw_water = pSrc->gfw_water; + //step_x = 0; + //kin_time_x = 0; + // Transport data + count_cells = pSrc->count_cells; + count_shifts = pSrc->count_shifts; + ishift = pSrc->ishift; + bcon_first = pSrc->bcon_first; + bcon_last = pSrc->bcon_last; + correct_disp = pSrc->correct_disp; + tempr = pSrc->tempr; + timest = pSrc->timest; + simul_tr = pSrc->simul_tr; + diffc = pSrc->diffc; + heat_diffc = pSrc->heat_diffc; + cell = pSrc->cell; + mcd_substeps = pSrc->mcd_substeps; + stag_data = pSrc->stag_data; + print_modulus = pSrc->print_modulus; + punch_modulus = pSrc->punch_modulus; + dump_in = pSrc->dump_in; + dump_modulus = pSrc->dump_modulus; + transport_warnings = pSrc->transport_warnings; + // cell_data + cell_data = pSrc->cell_data; + old_cells = pSrc->old_cells; + max_cells = pSrc->max_cells; + if (stag_data.count_stag > 0) + { + max_cells = (max_cells - 2) / (1 + stag_data.count_stag); + } + all_cells = pSrc->all_cells; + max_cells = pSrc->max_cells; + multi_Dflag = pSrc->multi_Dflag; + interlayer_Dflag = pSrc->interlayer_Dflag; + implicit = pSrc->implicit; + max_mixf = pSrc->max_mixf; + min_dif_LM = pSrc->min_dif_LM; + default_Dw = pSrc->default_Dw; + correct_Dw = pSrc->correct_Dw; + multi_Dpor = pSrc->multi_Dpor; + interlayer_Dpor = pSrc->interlayer_Dpor; + multi_Dpor_lim = pSrc->multi_Dpor_lim; + interlayer_Dpor_lim = pSrc->interlayer_Dpor_lim; + multi_Dn = pSrc->multi_Dn; + interlayer_tortf = pSrc->interlayer_tortf; + cell_no = pSrc->cell_no; + mixrun = pSrc->mixrun; + // Advection data + count_ad_cells = pSrc->count_ad_cells; + count_ad_shifts = pSrc->count_ad_shifts; + print_ad_modulus = pSrc->print_ad_modulus; + punch_ad_modulus = pSrc->punch_ad_modulus; + advection_punch = pSrc->advection_punch; + advection_print = pSrc->advection_print; + advection_kin_time = pSrc->advection_kin_time; + advection_kin_time_defined = pSrc->advection_kin_time_defined; + advection_warnings = pSrc->advection_warnings; + // Tidy data + new_model = TRUE; + new_exchange = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_copy = FALSE; + new_pitzer = FALSE; + // Elements + for (int i = 0; i < (int)pSrc->elements.size(); i++) + { + const char* ptr = string_hsave(pSrc->elements[i]->name); + class element* elt_ptr = element_store(ptr); + elt_ptr->gfw = pSrc->elements[i]->gfw; + } + element_h_one = element_store("H(1)"); + // Element List + count_elts = 0; + // Reaction + run_cells_one_step = pSrc->run_cells_one_step; + //// logk + //logk.clear(); + //for (size_t i = 0; i < pSrc->logk.size(); i++) + //{ + // class logk* tlk = new class logk; + // *tlk = *pSrc->logk[i]; + // tlk->name = string_hsave(pSrc->logk[i]->name); + // logk.push_back(tlk); + //} + for (int i = 0; i < (int)pSrc->logk.size(); i++) + { + class logk* logk_ptr = logk_store(pSrc->logk[i]->name, FALSE); + //memcpy(logk_ptr, pSrc->logk[i], sizeof(class logk)); + *logk_ptr = *pSrc->logk[i]; + logk_ptr->name = string_hsave(pSrc->logk[i]->name); + logk_ptr->add_logk.resize(pSrc->logk[i]->add_logk.size()); + for (size_t j = 0; j < logk_ptr->add_logk.size(); j++) + { + logk_ptr->add_logk[j].coef = pSrc->logk[i]->add_logk[j].coef; + logk_ptr->add_logk[j].name = string_hsave(pSrc->logk[i]->add_logk[j].name); + } + } + // s, species + for (int i = 0; i < (int)pSrc->s.size(); i++) + { + class species* s_ptr = s_store(pSrc->s[i]->name, pSrc->s[i]->z, FALSE); + //memcpy(s_ptr, pSrc->s[i], sizeof(class species)); + *s_ptr = *pSrc->s[i]; + // fix up all pointers + s_ptr->name = string_hsave(pSrc->s[i]->name); + s_ptr->mole_balance = NULL; + if (pSrc->s[i]->mole_balance != NULL) + { + s_ptr->mole_balance = string_hsave(pSrc->s[i]->mole_balance); + } + s_ptr->primary = NULL; + s_ptr->secondary = NULL; + s_ptr->add_logk.resize(pSrc->s[i]->add_logk.size()); + for (size_t j = 0; j < s_ptr->add_logk.size(); j++) + { + s_ptr->add_logk[j].coef = pSrc->s[i]->add_logk[j].coef; + s_ptr->add_logk[j].name = string_hsave(pSrc->s[i]->add_logk[j].name); + } + //next_elt + s_ptr->next_elt = elt_list_internal_copy(pSrc->s[i]->next_elt); + s_ptr->next_secondary = elt_list_internal_copy(pSrc->s[i]->next_secondary); + s_ptr->next_sys_total = elt_list_internal_copy(pSrc->s[i]->next_sys_total); + //rxn + s_ptr->rxn = CReaction_internal_copy(pSrc->s[i]->rxn); + s_ptr->rxn_s = CReaction_internal_copy(pSrc->s[i]->rxn_s); + s_ptr->rxn_x = CReaction_internal_copy(pSrc->s[i]->rxn_x); + } + s_diff_layer = pSrc->s_diff_layer; + //s_x will be built + s_h2o = s_search("H2O"); + s_hplus = s_search("H+"); + s_h3oplus = s_search("H3O+"); + s_eminus = s_search("e-"); + s_co3 = s_search("CO3-2"); + s_h2 = s_search("H2"); + s_o2 = s_search("O2"); + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + for (int i = 0; i < (int)pSrc->phases.size(); i++) + { + class phase* phase_ptr = phase_store(pSrc->phases[i]->name); + //memcpy(phase_ptr, pSrc->phases[i], sizeof(class phase)); + *phase_ptr = *pSrc->phases[i]; + // clean up pointers + phase_ptr->name = string_hsave(pSrc->phases[i]->name); + phase_ptr->formula = string_hsave(pSrc->phases[i]->formula); + //add_logk + phase_ptr->add_logk.resize(pSrc->phases[i]->add_logk.size()); + for (size_t j = 0; j < phase_ptr->add_logk.size(); j++) + { + phase_ptr->add_logk[j].coef = pSrc->phases[i]->add_logk[j].coef; + phase_ptr->add_logk[j].name = string_hsave(pSrc->phases[i]->add_logk[j].name); + } + //next_elt + phase_ptr->next_elt = elt_list_internal_copy(pSrc->phases[i]->next_elt); + phase_ptr->next_sys_total = elt_list_internal_copy(pSrc->phases[i]->next_sys_total); + //rxn + phase_ptr->rxn = CReaction_internal_copy(pSrc->phases[i]->rxn); + phase_ptr->rxn_s = CReaction_internal_copy(pSrc->phases[i]->rxn_s); + phase_ptr->rxn_x = CReaction_internal_copy(pSrc->phases[i]->rxn_x); + } + // Master species + for (size_t i = 0; i < pSrc->master.size(); i++) + { + master.resize(i + 1); + master[i] = new class master; + //memcpy(master[i], pSrc->master[i], sizeof(class master)); + *master[i] = *pSrc->master[i]; + // clean up pointers + master[i]->gfw_formula = string_hsave(pSrc->master[i]->gfw_formula); + master[i]->elt = element_store(pSrc->master[i]->elt->name); + master[i]->unknown = NULL; + master[i]->s = s_store(pSrc->master[i]->s->name, pSrc->master[i]->s->z, FALSE); + //rxn_primary + master[i]->rxn_primary = CReaction_internal_copy(pSrc->master[i]->rxn_primary); + master[i]->rxn_secondary = CReaction_internal_copy(pSrc->master[i]->rxn_secondary); + } + // Unknowns will be built + //x = NULL; + //count_unknowns = 0; + //max_unknowns = 0; + //ah2o_unknown = NULL; + //alkalinity_unknown = NULL; + //carbon_unknown = NULL; + //charge_balance_unknown = NULL; + //exchange_unknown = NULL; + //mass_hydrogen_unknown = NULL; + //mass_oxygen_unknown = NULL; + //mb_unknown = NULL; + //mu_unknown = NULL; + //pe_unknown = NULL; + //ph_unknown = NULL; + //pure_phase_unknown = NULL; + //solution_phase_boundary_unknown = NULL; + //surface_unknown = NULL; + //gas_unknown = NULL; + //ss_unknown = NULL; + //gas_unknowns; + //mb_unknowns + // Reaction work space + // class reaction_temp trxn; + count_trxn = 0; + // Print + pr = pSrc->pr; + status_on = pSrc->status_on; + status_interval = pSrc->status_interval; + status_timer = clock(); + status_string.clear(); + count_warnings = 0; + // RATES + //rates = pSrc->rates; + for (size_t i = 0; i < pSrc->rates.size(); i++) + { + rates.push_back(*rate_copy(&pSrc->rates[i])); + } + //rate_m = 0; + //rate_m0 = 0; + //rate_time = 0; + //rate_kin_time = 1.0; + //rate_sim_time_start = 0; + //rate_sim_time_end = 0; + //rate_sim_time = 0; + //rate_moles = 0; + initial_total_time = pSrc->initial_total_time; + //rate_p + count_rate_p = 0; + // User print + user_print = rate_copy(pSrc->user_print); + // For now, User Punch is NOT copied + n_user_punch_index = pSrc->n_user_punch_index; + fpunchf_user_s_warning = pSrc->fpunchf_user_s_warning; + //fpunchf_user_buffer[0] = 0; +#if defined MULTICHART + // auto chart_handler; + chart_handler.Set_io(phrq_io); +#endif + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + error_string = NULL; + simulation = pSrc->simulation; + //state = INITIALIZE; + //reaction_step = 0; + //transport_step = 0; + //transport_start = 0; + //advection_step = 0; + //stop_program = FALSE; + incremental_reactions = pSrc->incremental_reactions; + // Constants + MIN_LM = pSrc->MIN_LM; /* minimum log molality allowed before molality set to zero */ + LOG_ZERO_MOLALITY = pSrc->LOG_ZERO_MOLALITY; /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ + MIN_RELATED_LOG_ACTIVITY = pSrc->MIN_RELATED_LOG_ACTIVITY; + MIN_TOTAL = pSrc->MIN_TOTAL; + MIN_TOTAL_SS = pSrc->MIN_TOTAL_SS; + MIN_RELATED_SURFACE = pSrc->MIN_RELATED_SURFACE; + simulation = pSrc->simulation; + //my_array, + //delta, + //residual + input_error = 0; + next_keyword = Keywords::KEY_NONE; + parse_error = 0; + paren_count = 0; + iterations = 0; + gamma_iterations = 0; + density_iterations = 0; + run_reactions_iterations = 0; + overall_iterations = 0; + free_check_null(line); + free_check_null(line_save); + max_line = pSrc->max_line; + line = (char*)PHRQ_malloc(max_line * sizeof(char)); + line_save = (char*)PHRQ_malloc(max_line * sizeof(char)); + LOG_10 = pSrc->LOG_10; + // Debug + debug_model = pSrc->debug_model; + debug_prep = pSrc->debug_prep; + debug_set = pSrc->debug_set; + debug_diffuse_layer = pSrc->debug_diffuse_layer; + debug_inverse = pSrc->debug_inverse; + // + inv_tol_default = pSrc->inv_tol_default; + itmax = pSrc->itmax; + max_tries = pSrc->max_tries; + ineq_tol = pSrc->ineq_tol; + convergence_tolerance = pSrc->convergence_tolerance; + step_size = pSrc->step_size; + pe_step_size = pSrc->pe_step_size; + step_size_now = step_size; + pe_step_size_now = pe_step_size; + pp_scale = pSrc->pp_scale; + pp_column_scale = pSrc->pp_column_scale; + diagonal_scale = pSrc->diagonal_scale; + mass_water_switch = pSrc->mass_water_switch; + delay_mass_water = pSrc->delay_mass_water; + equi_delay = pSrc->equi_delay; + dampen_ah2o = pSrc->dampen_ah2o; + censor = pSrc->censor; + aqueous_only = pSrc->aqueous_only; + negative_concentrations = pSrc->negative_concentrations; + calculating_deriv = pSrc->calculating_deriv; + numerical_deriv = pSrc->numerical_deriv; + count_total_steps = 0; + phast = FALSE; + output_newline = true; + // llnl + a_llnl = pSrc->a_llnl; + b_llnl = pSrc->b_llnl; + bdot_llnl = pSrc->bdot_llnl; + llnl_temp = pSrc->llnl_temp; + llnl_adh = pSrc->llnl_adh; + llnl_bdh = pSrc->llnl_bdh; + llnl_bdot = pSrc->llnl_bdot; + llnl_co2_coefs = pSrc->llnl_co2_coefs; + + // Not implemented for now + SelectedOutput_map = pSrc->SelectedOutput_map; + { + std::map::iterator it = SelectedOutput_map.begin(); + for (; it != SelectedOutput_map.end(); it++) + { + //phrq_io->punch_open(it->second.Get_file_name().c_str()); + //it->second.Set_punch_ostream(phrq_io->Get_punch_ostream()); + //phrq_io->Set_punch_ostream(NULL); + it->second.Set_punch_ostream(NULL); + } + } + SelectedOutput_map.clear(); + + UserPunch_map = pSrc->UserPunch_map; + std::map::iterator it = UserPunch_map.begin(); + for (; it != UserPunch_map.end(); it++) + { + class rate* rate_new = new class rate; + rate_new = rate_copy(it->second.Get_rate()); + it->second.Set_rate(rate_new); + it->second.Set_PhreeqcPtr(this); + } + + remove_unstable_phases = FALSE; + //screen_string; + spread_length = pSrc->spread_length; + //maps set by store below + //std::map strings_map; + //std::map elements_map; + //std::map species_map; + //std::map phases_map; + //std::map logk_map; + //std::map master_isotope_map; + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + for (int i = 0; i < (int)pSrc->master_isotope.size(); i++) + { + class master_isotope* master_isotope_ptr = master_isotope_store(pSrc->master_isotope[i]->name, FALSE); + // memcpy(master_isotope_ptr, pSrc->master_isotope[i], sizeof(class master_isotope)); + *master_isotope_ptr = *pSrc->master_isotope[i]; + master_isotope_ptr->name = string_hsave(pSrc->master_isotope[i]->name); + int n; + master_isotope_ptr->master = NULL; + if (pSrc->master_isotope[i]->master) + { + master_isotope_ptr->master = master_search(pSrc->master_isotope[i]->master->elt->name, &n); + } + if (master_isotope_ptr->master == NULL) + { + //error_msg("Error in copy constructor for master_isotope.", STOP); + } + master_isotope_ptr->elt = NULL; + if (pSrc->master_isotope[i]->elt) + { + master_isotope_ptr->elt = element_store(pSrc->master_isotope[i]->elt->name); + } + master_isotope_ptr->units = NULL; + if (pSrc->master_isotope[i]->units) + { + master_isotope_ptr->units = string_hsave(pSrc->master_isotope[i]->units); + } + } + initial_solution_isotopes = pSrc->initial_solution_isotopes; + // Calculate values + for (int i = 0; i < pSrc->calculate_value.size(); i++) + { + class calculate_value* calculate_value_ptr = calculate_value_store(pSrc->calculate_value[i]->name, FALSE); + calculate_value_ptr->value = pSrc->calculate_value[i]->value; + calculate_value[i]->commands = pSrc->calculate_value[i]->commands; + } + // More isotopes + for (int i = 0; i < (int)pSrc->isotope_ratio.size(); i++) + { + class isotope_ratio* isotope_ratio_ptr = isotope_ratio_store(pSrc->isotope_ratio[i]->name, FALSE); + isotope_ratio_ptr->name = string_hsave(pSrc->isotope_ratio[i]->name); + isotope_ratio_ptr->isotope_name = string_hsave(pSrc->isotope_ratio[i]->isotope_name); + isotope_ratio_ptr->ratio = pSrc->isotope_ratio[i]->ratio; + isotope_ratio_ptr->converted_ratio = pSrc->isotope_ratio[i]->converted_ratio; + } + //std::map isotope_ratio_map; + for (int i = 0; i < (int)pSrc->isotope_alpha.size(); i++) + { + class isotope_alpha* isotope_alpha_ptr = isotope_alpha_store(pSrc->isotope_alpha[i]->name, FALSE); + isotope_alpha_ptr->named_logk = string_hsave(pSrc->isotope_alpha[i]->named_logk); + isotope_alpha_ptr->value = pSrc->isotope_alpha[i]->value; + } + //std::map isotope_alpha_map; + // Misc + phreeqc_mpi_myself = 0; + first_read_input = pSrc->first_read_input; + user_database = pSrc->user_database; + //have_punch_name = pSrc->have_punch_name; + print_density = pSrc->print_density; + print_viscosity = pSrc->print_viscosity; + viscos = pSrc->viscos; + viscos_0 = pSrc->viscos_0; + viscos_0_25 = pSrc->viscos_0_25; // viscosity of the solution, of pure water, of pure water at 25 C + cell_pore_volume = pSrc->cell_pore_volume;; + cell_porosity = pSrc->cell_porosity; + cell_volume = pSrc->cell_volume; + cell_saturation = pSrc->cell_saturation; + sys.clear(); + sys_tot = pSrc->sys_tot; + // solution properties + V_solutes = pSrc->V_solutes; + viscos = pSrc->viscos; + viscos_0 = pSrc->viscos_0; + viscos_0_25 = pSrc->viscos_0_25; + rho_0 = pSrc->rho_0; + kappa_0 = pSrc->kappa_0; + p_sat = pSrc->p_sat; + eps_r = pSrc->eps_r; + DH_A = pSrc->DH_A; + DH_B = pSrc->DH_B; + DH_Av = pSrc->DH_Av; + QBrn = pSrc->QBrn; + ZBrn = pSrc->ZBrn; + dgdP = pSrc->dgdP; + // + need_temp_msg = pSrc->need_temp_msg; + solution_mass = pSrc->solution_mass; + solution_volume = pSrc->solution_volume; + s_pTail = NULL; + //basic_interpreter = NULL; + /* cl1.cpp ------------------------------- */ + //std::vector x_arg, res_arg, scratch; + // gases.cpp + a_aa_sum = pSrc->a_aa_sum; + b2 = pSrc->b2; + b_sum = pSrc->b_sum; + R_TK = pSrc->R_TK; + /* input.cpp ------------------------------- */ + check_line_return = 0; + reading_db = FALSE; + /* integrate.cpp ------------------------------- */ + midpoint_sv = pSrc->midpoint_sv; + z_global = pSrc->z_global; + xd_global = pSrc->xd_global; + alpha_global = pSrc->alpha_global; + /* inverse.cpp ------------------------------- */ /* integrate.cpp ------------------------------- */ + max_row_count = pSrc->max_row_count; + max_column_count = pSrc->max_column_count; + carbon = pSrc->carbon; + //std::vector col_name, row_name; + count_rows = pSrc->count_rows; + count_optimize = pSrc->count_optimize; + col_phases = pSrc->col_phases; + col_redox = pSrc->col_redox; + col_epsilon = pSrc->col_epsilon; + col_ph = pSrc->col_ph; + col_water = pSrc->col_water; + col_isotopes = pSrc->col_isotopes; + col_phase_isotopes = pSrc->col_phase_isotopes; + row_mb = pSrc->row_mb; + row_fract = pSrc->row_fract; + row_charge = pSrc->row_charge; + row_carbon = pSrc->row_carbon; + row_isotopes = pSrc->row_isotopes; + row_epsilon = pSrc->row_epsilon; + row_isotope_epsilon = pSrc->row_isotope_epsilon; + row_water = pSrc->row_water; + //std::vector inv_zero, array1, inv_res, inv_delta1, delta2, + // delta3, inv_cu, delta_save; + //std::vector min_delta, max_delta; + //std::vector inv_iu, inv_is; + klmd = pSrc->klmd; + nklmd = pSrc->nklmd; + n2d = pSrc->n2d; + kode = pSrc->kode; + iter = pSrc->iter; + toler = pSrc->toler; + error = pSrc->error; + max_pct = pSrc->max_pct; + scaled_error = pSrc->scaled_error; + master_alk = NULL; + //std::vector row_back, col_back; + //std::vector good, bad, minimal; + max_good = pSrc->max_good; + max_bad = pSrc->max_bad; + max_minimal = pSrc->max_minimal; + count_good = pSrc->count_good; + count_bad = pSrc->count_bad; + count_minimal = pSrc->count_minimal; + count_calls = pSrc->count_calls; + soln_bits = pSrc->soln_bits; + phase_bits = pSrc->phase_bits; + current_bits = pSrc->current_bits; + temp_bits = pSrc->temp_bits; + netpath_file = NULL; + count_inverse_models = pSrc->count_inverse_models; + count_pat_solutions = pSrc->count_pat_solutions; + for (int i = 0; i < 32; i++) + { + min_position[i] = pSrc->min_position[i]; + max_position[i] = pSrc->max_position[i]; + now[i] = pSrc->now[i]; + } + //std::vector inverse_heading_names; + /* kinetics.cpp ------------------------------- */ + count_pp = count_pg = count_ss = 0; + cvode_kinetics_ptr = NULL; + cvode_test = FALSE; + cvode_error = FALSE; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = NULL; + kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save = NULL; + cvode_ss_assemblage_save = NULL; + //std::vector m_temp, m_original, rk_moles, x0_moles; + set_and_run_attempt = 0; + /* model.cpp ------------------------------- */ + gas_in = FALSE; + min_value = 1e-10; + //std::vector normal, ineq_array, res, cu, zero, delta1; + //std::vector iu, is, back_eq; + /* phrq_io_output.cpp ------------------------------- */ + forward_output_to_log = pSrc->forward_output_to_log; + /* phreeqc_files.cpp ------------------------------- */ + default_data_base = pSrc->default_data_base; + // Pitzer + pitzer_model = pSrc->pitzer_model; + sit_model = pSrc->sit_model; + pitzer_pe = pSrc->pitzer_pe; + + + full_pitzer = pSrc->full_pitzer; + always_full_pitzer = pSrc->always_full_pitzer; + ICON = pSrc->ICON; + IC = pSrc->IC; + COSMOT = pSrc->COSMOT; + AW = pSrc->AW; + VP = pSrc->VP; + DW0 = pSrc->DW0; + for (int i = 0; i < (int)pSrc->pitz_params.size(); i++) + { + pitz_param_store(pSrc->pitz_params[i]); + } + + //pitz_param_map = pSrc->pitz_param_map; created by store + for (int i = 0; i < (int)pSrc->theta_params.size(); i++) + { + size_t count_theta_params = theta_params.size(); + theta_params.resize(count_theta_params + 1); + theta_params[count_theta_params] = new class theta_param; + *theta_params[count_theta_params] = *pSrc->theta_params[i]; + } + use_etheta = pSrc->use_etheta; + OTEMP = pSrc->OTEMP; + OPRESS = pSrc->OPRESS; + A0 = pSrc->A0; + aphi = pitz_param_copy(pSrc->aphi); + // will be rebuilt + spec = pSrc->spec; + cations = pSrc->cations; + anions = pSrc->anions; + neutrals = pSrc->neutrals; + count_cations = pSrc->count_cations; + count_anions = pSrc->count_anions; + count_neutrals = pSrc->count_neutrals; + MAXCATIONS = pSrc->MAXCATIONS; + FIRSTANION = pSrc->FIRSTANION; + MAXNEUTRAL = pSrc->MAXNEUTRAL; + mcb0 = pSrc->mcb0; + mcb1 = pSrc->mcb1; + mcc0 = pSrc->mcc0; + IPRSNT = pSrc->IPRSNT; + M = pSrc->M; + LGAMMA = pSrc->LGAMMA; + for (int i = 0; i < 23; i++) + { + BK[i] = pSrc->BK[i]; + DK[i] = pSrc->DK[i]; + } + dummy = 0; + /* print.cpp ------------------------------- */ + /* + sformatf_buffer = (char *) PHRQ_malloc(256 * sizeof(char)); + if (sformatf_buffer == NULL) + malloc_error(); + sformatf_buffer_size = 256; + */ + /* read.cpp */ + prev_next_char = NULL; +#if defined PHREEQ98 + int shifts_as_points; +#endif + /* read_class.cxx */ + // auto dump_info + // auto delete_info + // auto run_info + /* + run_info.Set_io(phrq_io); + */ + /* readtr.cpp */ + // auto dump_file_name_cpp; + /* sit.cpp ------------------------------- */ + for (int i = 0; i < (int)pSrc->sit_params.size(); i++) + { + sit_param_store(pSrc->sit_params[i]); + } + //sit_param_map = pSrc->sit_param_map; // filled by store + sit_A0 = pSrc->sit_A0; + sit_count_cations = pSrc->sit_count_cations; + sit_count_anions = pSrc->sit_count_anions; + sit_count_neutrals = pSrc->sit_count_neutrals; + sit_MAXCATIONS = pSrc->sit_MAXCATIONS; + sit_FIRSTANION = pSrc->sit_FIRSTANION; + sit_MAXNEUTRAL = pSrc->sit_MAXNEUTRAL; + sit_IPRSNT = pSrc->sit_IPRSNT; + sit_M = pSrc->sit_M; + sit_LGAMMA = pSrc->sit_LGAMMA; + s_list = pSrc->s_list; + cation_list = pSrc->cation_list; + neutral_list = pSrc->neutral_list; + anion_list = pSrc->anion_list; + ion_list = pSrc->ion_list; + param_list = pSrc->param_list; + + /* tidy.cpp ------------------------------- */ + //a0 = 0; + //a1 = 0; + //kc = 0; + //kb = 0; + /* tally.cpp ------------------------------- */ + //t_buffer = NULL; + //tally_count_component = 0; + //tally_table = NULL; + //count_tally_table_columns = 0; + //count_tally_table_rows = 0; + + /* transport.cpp ------------------------------- */ + /* storage is created and freed in tranport.cpp */ + sol_D = NULL; + sol_D_dbg = NULL; + J_ij = NULL; + J_ij_il = NULL; + J_ij_count_spec = pSrc->J_ij_count_spec; + m_s = NULL; + count_m_s = pSrc->count_m_s; + tot1_h = pSrc->tot1_h; + tot1_o = pSrc->tot1_o; + tot2_h = pSrc->tot2_h; + tot2_o = pSrc->tot2_o; + diffc_max = pSrc->diffc_max; + diffc_tr = pSrc->diffc_tr; + J_ij_sum = pSrc->J_ij_sum; + transp_surf = pSrc->transp_surf; + heat_mix_array = NULL; + temp1 = NULL; + temp2 = NULL; + nmix = pSrc->nmix; + heat_nmix = pSrc->heat_nmix; + heat_mix_f_imm = pSrc->heat_mix_f_imm; + heat_mix_f_m = pSrc->heat_mix_f_m; + warn_MCD_X = pSrc->warn_MCD_X; + warn_fixed_Surf = pSrc->warn_fixed_Surf; + current_x = pSrc->current_x; + current_A = pSrc->current_A; + fix_current = pSrc->fix_current; + + /* utilities.cpp ------------------------------- */ + //spinner = 0; + //// keycount; + //for (int i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + //{ + // keycount.push_back(0); + //} + spinner = pSrc->spinner; + gfw_map = pSrc->gfw_map; + //rates_map = pSrc->rates_map; + sum_species_map = pSrc->sum_species_map; + sum_species_map_db = pSrc->sum_species_map_db; + + // make sure new_model gets set + this->keycount[Keywords::KEY_SOLUTION_SPECIES] = 1; + this->tidy_model(); + return; +} +// Operator overloaded using a member function +Phreeqc &Phreeqc::operator=(const Phreeqc &rhs) +{ + if (this == &rhs) // Same object? + return *this; + + // clean up this here + this->clean_up(); + + this->PHRQ_free_all(); + if (this->phrq_io == &this->ioInstance) + { + this->phrq_io->clear_istream(); + this->phrq_io->close_ostreams(); + } + + // copy Phreeqc object to this + //this->phrq_io = rhs.phrq_io; + //this->phrq_io = new PHRQ_io; +#if !defined(R_SO) + this->phrq_io->Set_output_ostream(&std::cout); + this->phrq_io->Set_error_ostream(&std::cerr); +#endif + this->init(); + this->initialize(); + this->InternalCopy(&rhs); + return *this; +} + +int Phreeqc::next_user_number(Keywords::KEYWORDS key) +{ + switch (key) + { + case Keywords::KEY_REACTION_TEMPERATURE: + return Utilities::Rxn_next_user_number(Rxn_temperature_map); + break; + case Keywords::KEY_REACTION_PRESSURE: + return Utilities::Rxn_next_user_number(Rxn_pressure_map); + break; + case Keywords::KEY_SURFACE: + return Utilities::Rxn_next_user_number(Rxn_surface_map); + break; + case Keywords::KEY_EXCHANGE: + return Utilities::Rxn_next_user_number(Rxn_exchange_map); + break; + case Keywords::KEY_KINETICS: + return Utilities::Rxn_next_user_number(Rxn_kinetics_map); + break; + case Keywords::KEY_MIX: + return Utilities::Rxn_next_user_number(Rxn_mix_map); + break; + case Keywords::KEY_REACTION: + return Utilities::Rxn_next_user_number(Rxn_reaction_map); + break; + case Keywords::KEY_GAS_PHASE: + return Utilities::Rxn_next_user_number(Rxn_gas_phase_map); + break; + case Keywords::KEY_SOLID_SOLUTIONS: + return Utilities::Rxn_next_user_number(Rxn_ss_assemblage_map); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: + return Utilities::Rxn_next_user_number(Rxn_pp_assemblage_map); + break; + case Keywords::KEY_SOLUTION: + return Utilities::Rxn_next_user_number(Rxn_solution_map); + break; + default: + assert(false); + return -999; + } +} diff --git a/phreeqcpp/Phreeqc.h b/phreeqcpp/Phreeqc.h new file mode 100644 index 00000000..7e4e0c7d --- /dev/null +++ b/phreeqcpp/Phreeqc.h @@ -0,0 +1,2079 @@ +#ifndef _INC_PHREEQC_H +#define _INC_PHREEQC_H +#if defined(WIN32) +# if defined(PHREEQCI_GUI) +# ifndef WINVER +# define WINVER 0x0400 +# endif +# include +# endif +# include +# if defined(PHREEQCI_GUI) +# include "../../resource.h" +# endif +#endif +#if defined(WIN32_MEMORY_DEBUG) +#define _CRTDBG_MAP_ALLOC +#include +#endif +#ifndef boolean +typedef unsigned char boolean; +#endif +/* ---------------------------------------------------------------------- +* INCLUDE FILES +* ---------------------------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phrqtype.h" +#include "cvdense.h" +#include "runner.h" +#include "dumper.h" +#include "PHRQ_io.h" +#include "SelectedOutput.h" +#include "UserPunch.h" +#ifdef MULTICHART +#include "ChartHandler.h" +#endif +#include "Keywords.h" +#include "Pressure.h" +#include "cxxMix.h" +#include "Use.h" +#include "Surface.h" +#ifdef SWIG_SHARED_OBJ +#include "thread.h" +#endif + +class cxxNameDouble; +class cxxKinetics; +class cxxKineticsComp; +class cxxExchange; +class cxxExchComp; +class cxxGasPhase; +class cxxTemperature; +class cxxPPassemblage; +class cxxPPassemblageComp; +class cxxReaction; +class cxxSolution; +class cxxISolutionComp; +class cxxSolutionIsotope; +class cxxSSassemblage; +class cxxSS; +class cxxStorageBin; +class PBasic; + +#include "global_structures.h" + +class Phreeqc +{ +public: + Phreeqc(PHRQ_io* io = NULL); + Phreeqc(const Phreeqc& src); + void InternalCopy(const Phreeqc* pSrc); + Phreeqc& operator=(const Phreeqc& rhs); + ~Phreeqc(void); + +public: + // + // Phreeqc class methods + // + + // advection.cpp ------------------------------- + int advection(void); + + // basicsubs.cpp ------------------------------- + int basic_compile(const char* commands, void** lnbase, void** vbase, void** lpbase); + int basic_run(char* commands, void* lnbase, void* vbase, void* lpbase); + void basic_free(void); +#ifdef IPHREEQC_NO_FORTRAN_MODULE + double basic_callback(double x1, double x2, char* str); +#else + double basic_callback(double x1, double x2, const char* str); +#endif + void register_basic_callback(double (*fcn)(double x1, double x2, const char* str, void* cookie), void* cookie1); +#ifdef IPHREEQC_NO_FORTRAN_MODULE + void register_fortran_basic_callback(double (*fcn)(double* x1, double* x2, char* str, size_t l)); +#else + void register_fortran_basic_callback(double (*fcn)(double* x1, double* x2, const char* str, int l)); +#endif + + LDBLE activity(const char* species_name); + LDBLE activity_coefficient(const char* species_name); + LDBLE log_activity_coefficient(const char* species_name); + LDBLE aqueous_vm(const char* species_name); + LDBLE phase_vm(const char* phase_name); + LDBLE diff_c(const char* species_name); + LDBLE setdiff_c(const char* species_name, double d); + LDBLE flux_mcd(const char* species_name, int option); + LDBLE sa_declercq(double type, double sa, double d, double m, double m0, double gfw); + LDBLE calc_SC(void); + /* VP: Density Start */ + LDBLE calc_dens(void); + /* VP: Density End */ + LDBLE calc_logk_n(const char* name); + LDBLE calc_logk_p(const char* name); + LDBLE calc_logk_s(const char* name); + LDBLE calc_deltah_s(const char* name); + LDBLE calc_deltah_p(const char* name); + LDBLE dh_a0(const char* name); + LDBLE dh_bdot(const char* name); + LDBLE calc_surface_charge(const char* surface_name); + LDBLE calc_t_sc(const char* name); + LDBLE diff_layer_total(const char* total_name, const char* surface_name); + LDBLE edl_species(const char* surf_name, LDBLE* count, char*** names, LDBLE** moles, LDBLE* area, LDBLE* thickness); + int get_edl_species(cxxSurfaceCharge& charge_ref); + LDBLE equi_phase(const char* phase_name); + LDBLE equi_phase_delta(const char* phase_name); + LDBLE equivalent_fraction(const char* name, LDBLE* eq, std::string& elt_name); + LDBLE find_gas_comp(const char* gas_comp_name); + LDBLE find_gas_p(void); + LDBLE find_gas_vm(void); + LDBLE find_misc1(const char* ss_name); + LDBLE find_misc2(const char* ss_name); + LDBLE find_ss_comp(const char* ss_comp_name); + LDBLE get_calculate_value(const char* name); + char* iso_unit(const char* total_name); + LDBLE iso_value(const char* total_name); + LDBLE kinetics_moles(const char* kinetics_name); + LDBLE kinetics_moles_delta(const char* kinetics_name); + LDBLE log_activity(const char* species_name); + LDBLE log_molality(const char* species_name); + LDBLE molality(const char* species_name); + LDBLE pressure(void); + LDBLE pr_pressure(const char* phase_name); + LDBLE pr_phi(const char* phase_name); + LDBLE saturation_ratio(const char* phase_name); + int saturation_index(const char* phase_name, LDBLE* iap, LDBLE* si); + int solution_number(void); + LDBLE solution_sum_secondary(const char* total_name); + LDBLE sum_match_gases(const char* stemplate, const char* name); + LDBLE sum_match_species(const char* stemplate, const char* name); + LDBLE sum_match_ss(const char* stemplate, const char* name); + int match_elts_in_species(const char* name, const char* stemplate); + int extract_bracket(const char** string, char* bracket_string); + LDBLE surf_total(const char* total_name, const char* surface_name); + LDBLE surf_total_no_redox(const char* total_name, const char* surface_name); + static int system_species_compare(const void* ptr1, const void* ptr2); + static int system_species_compare_name(const void* ptr1, const void* ptr2); + LDBLE system_total(const char* total_name, LDBLE* count, char*** names, + char*** types, LDBLE** moles, int i); + std::string kinetics_formula(std::string kinetics_name, cxxNameDouble& stoichiometry); + std::string phase_formula(std::string phase_name, cxxNameDouble& stoichiometry); + std::string species_formula(std::string phase_name, cxxNameDouble& stoichiometry); + LDBLE list_ss(std::string ss_name, cxxNameDouble& composition); + int system_total_elements(void); + int system_total_si(void); + int system_total_aq(void); + int system_total_ex(void); + int system_total_surf(void); + int system_total_gas(void); + int system_total_equi(void); + int system_total_kin(void); + int system_total_ss(void); + int system_total_elt(const char* total_name); + int system_total_elt_secondary(const char* total_name); + LDBLE total(const char* total_name); + LDBLE total_mole(const char* total_name); + int system_total_solids(cxxExchange* exchange_ptr, + cxxPPassemblage* pp_assemblage_ptr, + cxxGasPhase* gas_phase_ptr, + cxxSSassemblage* ss_assemblage_ptr, + cxxSurface* surface_ptr); + + static LDBLE f_rho(LDBLE rho_old, void* cookie); + static LDBLE f_Vm(LDBLE v1, void* cookie); + LDBLE calc_solution_volume(void); + + // cl1.cpp ------------------------------- + int cl1(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE* q, + int* kode, LDBLE toler, + int* iter, LDBLE* x, LDBLE* res, LDBLE* error, + LDBLE* cu, int* iu, int* s, int check); + void cl1_space(int check, int n2d, int klm, int nklmd); + + // cl1mp.cpp ------------------------------- + int cl1mp(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE* q_arg, + int* kode, LDBLE toler, + int* iter, LDBLE* x_arg, LDBLE* res_arg, LDBLE* error, + LDBLE* cu_arg, int* iu, int* s, int check, LDBLE censor_arg); + + // class_main.cpp ------------------------------- + int write_banner(void); + + /* default.cpp */ +public: + //int close_input_files(void); + //int close_output_files(void); + //static int istream_getc(void* cookie); + int process_file_names(int argc, char* argv[], std::istream** db_cookie, + std::istream** input_cookie, int log); + + /* PHRQ_io_output.cpp */ + void screen_msg(const char* str); + + void echo_msg(const char* err_str); + int warning_msg(const char* err_str); + void set_forward_output_to_log(int value); + int get_forward_output_to_log(void); + + // dump_ostream + bool dump_open(const char* file_name); + void dump_flush(void); + void dump_close(void); + void dump_msg(const char* str); + + // log_ostream + bool log_open(const char* file_name); + void log_flush(void); + void log_close(void); + void log_msg(const char* str); + + // error_ostream + bool error_open(const char* file_name); + void error_flush(void); + void error_close(void); + void error_msg(const char* str, bool stop = false); + + // output_ostream + bool output_open(const char* file_name); + void output_flush(void); + void output_close(void); + void output_msg(const char* str); + + // punch_ostream + bool punch_open(const char* file_name, int n_user); + void punch_flush(void); + void punch_close(void); + void punch_msg(const char* str); + + void fpunchf_heading(const char* name); + void fpunchf(const char* name, const char* format, double d); + void fpunchf(const char* name, const char* format, char* d); + void fpunchf(const char* name, const char* format, int d); + void fpunchf_user(int user_index, const char* format, double d); + void fpunchf_user(int user_index, const char* format, char* d); + int fpunchf_end_row(const char* format); + // input.cpp ------------------------------- + int reading_database(void); + void set_reading_database(int reading_database); + int check_line(const char* string, int allow_empty, int allow_eof, + int allow_keyword, int print); + int check_line_impl(const char* string, int allow_empty, + int allow_eof, int allow_keyword, int print); + int get_line(void); + //int get_logical_line(void* cookie, int* l); + int read_database(void); + int run_simulations(void); + + // integrate.cpp ------------------------------- + int calc_all_g(void); + int calc_init_g(void); + int initial_surface_water(void); + int sum_diffuse_layer(cxxSurfaceCharge* surface_charge_ptr1); + int calc_all_donnan(void); + int calc_init_donnan(void); + LDBLE g_function(LDBLE x_value); + LDBLE midpnt(LDBLE x1, LDBLE x2, int n); + void polint(LDBLE* xa, LDBLE* ya, int n, LDBLE xv, LDBLE* yv, + LDBLE* dy); + LDBLE qromb_midpnt(cxxSurfaceCharge* charge_ptr, LDBLE x1, LDBLE x2); + LDBLE calc_psi_avg(cxxSurfaceCharge* charge_ptr, LDBLE surf_chrg_eq); + + // inverse.cpp ------------------------------- + int inverse_models(void); + int add_to_file(const char* filename, const char* string); + int bit_print(unsigned long bits, int l); + int carbon_derivs(class inverse* inv_ptr); + int check_isotopes(class inverse* inv_ptr); + int check_solns(class inverse* inv_ptr); + bool set_isotope_unknowns(class inverse* inv_ptrs); + cxxSolutionIsotope* get_isotope(cxxSolution* solution_ptr, const char* elt); + LDBLE get_inv_total(cxxSolution* solution_ptr, const char* elt); + int isotope_balance_equation(class inverse* inv_ptr, int row, int n); + int post_mortem(void); + bool test_cl1_solution(void); + unsigned long get_bits(unsigned long bits, int position, int number); + unsigned long minimal_solve(class inverse* inv_ptr, + unsigned long minimal_bits); + void dump_netpath(class inverse* inv_ptr); + int dump_netpath_pat(class inverse* inv_ptr); + int next_set_phases(class inverse* inv_ptr, int first_of_model_size, + int model_size); + int phase_isotope_inequalities(class inverse* inv_ptr); + int print_model(class inverse* inv_ptr); + int punch_model_heading(class inverse* inv_ptr); + int punch_model(class inverse* inv_ptr); + void print_isotope(FILE* netpath_file, cxxSolution* solution_ptr, + const char* elt, const char* string); + void print_total(FILE* netpath_file, cxxSolution* solution_ptr, + const char* elt, const char* string); + void print_total_multi(FILE* netpath_file, cxxSolution* solution_ptr, + const char* string, const char* elt0, + const char* elt1, const char* elt2, const char* elt3, + const char* elt4); + + void print_total_pat(FILE* netpath_file, const char* elt, + const char* string); + int range(class inverse* inv_ptr, unsigned long cur_bits); + int save_bad(unsigned long bits); + int save_good(unsigned long bits); + int save_minimal(unsigned long bits); + unsigned long set_bit(unsigned long bits, int position, int value); + int setup_inverse(class inverse* inv_ptr); + int set_initial_solution(int n_user_old, int n_user_new); + int set_ph_c(class inverse* inv_ptr, + int i, cxxSolution* soln_ptr_orig, int n_user_new, + LDBLE d_alk, LDBLE ph_factor, LDBLE alk_factor); + int shrink(class inverse* inv_ptr, LDBLE* array_in, + LDBLE* array_out, int* k, int* l, int* m, int* n, + unsigned long cur_bits, LDBLE* delta_l, int* col_back_l, + int* row_back_l); + int solve_inverse(class inverse* inv_ptr); + int solve_with_mask(class inverse* inv_ptr, unsigned long cur_bits); + int subset_bad(unsigned long bits); + int subset_minimal(unsigned long bits); + int superset_minimal(unsigned long bits); + int write_optimize_names(class inverse* inv_ptr); + + // isotopes.cpp ------------------------------- + int add_isotopes(cxxSolution& solution_ptr); + int calculate_values(void); + int calculate_isotope_moles(class element* elt_ptr, + cxxSolution* solution_ptr, LDBLE total_moles); + LDBLE convert_isotope(class master_isotope* master_isotope_ptr, LDBLE ratio); + int from_pcil(class master_isotope* master_isotope_ptr); + int from_permil(class master_isotope* master_isotope_ptr, LDBLE major_total); + int from_pct(class master_isotope* master_isotope_ptr, LDBLE major_total); + int from_tu(class master_isotope* master_isotope_ptr); + class calculate_value* calculate_value_alloc(void); + int calculate_value_free(class calculate_value* calculate_value_ptr); + class calculate_value* calculate_value_search(const char* name); + class calculate_value* calculate_value_store(const char* name, + int replace_if_found); + class isotope_alpha* isotope_alpha_alloc(void); + class isotope_alpha* isotope_alpha_search(const char* name); + class isotope_alpha* isotope_alpha_store(const char* name, + int replace_if_found); + class isotope_ratio* isotope_ratio_alloc(void); + class isotope_ratio* isotope_ratio_search(const char* name); + class isotope_ratio* isotope_ratio_store(const char* name, + int replace_if_found); + class master_isotope* master_isotope_store(const char* name, + int replace_if_found); + class master_isotope* master_isotope_alloc(void); + class master_isotope* master_isotope_search(const char* name); + int print_initial_solution_isotopes(void); + int print_isotope_ratios(void); + int print_isotope_alphas(void); + int punch_isotopes(void); + int punch_calculate_values(void); + int read_calculate_values(void); + int read_isotopes(void); + int read_isotope_ratios(void); + int read_isotope_alphas(void); + int calculate_value_init(class calculate_value* calculate_value_ptr); + int isotope_alpha_init(class isotope_alpha* isotope_alpha_ptr); + int isotope_ratio_init(class isotope_ratio* isotope_ratio_ptr); + int master_isotope_init(class master_isotope* master_isotope_ptr); + + // kinetics.cpp ------------------------------- + void cvode_init(void); + bool cvode_update_reactants(int i, int nsaver, bool save_it); + int run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction); + int set_and_run(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction); + int set_and_run_wrapper(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction); + int set_advection(int i, int use_mix, int use_kinetics, int nsaver); + int free_cvode(void); +public: + static void f(integertype N, realtype t, N_Vector y, N_Vector ydot, + void* f_data); + static void Jac(integertype N, DenseMat J, RhsFn f, void* f_data, realtype t, + N_Vector y, N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void* jac_data, long int* nfePtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); + + int calc_final_kinetic_reaction(cxxKinetics* kinetics_ptr); + int calc_kinetic_reaction(cxxKinetics* kinetics_ptr, + LDBLE time_step); + bool limit_rates(cxxKinetics* kinetics_ptr); + int rk_kinetics(int i, LDBLE kin_time, int use_mix, int nsaver, + LDBLE step_fraction); + int set_reaction(int i, int use_mix, int use_kinetics); + int set_transport(int i, int use_mix, int use_kinetics, int nsaver); + int store_get_equi_reactants(int k, int kin_end); + + // mainsubs.cpp ------------------------------- + std::ifstream* open_input_stream(std::string query, std::string& default_name, std::ios_base::openmode mode, bool batch); + std::ofstream* open_output_stream(std::string query, std::string& default_name, std::ios_base::openmode mode, bool batch); + int copy_entities(void); + void do_mixes(void); + void initialize(void); + int initial_exchangers(int print); + int initial_gas_phases(int print); + int initial_solutions(int print); + int step_save_exch(int n_user); + int step_save_surf(int n_user); + int initial_surfaces(int print); + int reactions(void); + int saver(void); + int xsolution_save(int k_user); + int xexchange_save(int n_user); + int xgas_save(int n_user); + int xpp_assemblage_save(int n_user); + int xss_assemblage_save(int n_user); + int xsurface_save(int n_user); + int do_initialize(void); + int do_status(void); + void save_init(int i); + int copy_use(int i); + int set_use(void); + + // model.cpp ------------------------------- + int check_residuals(void); + int free_model_allocs(void); + int ineq(int kode); + int model(void); + int jacobian_sums(void); + int mb_gases(void); + int mb_ss(void); + int mb_sums(void); + int molalities(int allow_overflow); + int reset(void); + int residuals(void); + int set(int initial); + int sum_species(void); + int surface_model(void); + LDBLE ss_root(LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, + LDBLE xbaq); + LDBLE ss_halve(LDBLE a0, LDBLE a1, LDBLE x0, LDBLE x1, LDBLE kc, + LDBLE kb, LDBLE xcaq, LDBLE xbaq); + LDBLE ss_f(LDBLE xb, LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, + LDBLE xcaq, LDBLE xbaq); + int numerical_jacobian(void); + void set_inert_moles(void); + void unset_inert_moles(void); +#ifdef SLNQ + int add_trivial_eqns(int rows, int cols, LDBLE* matrix); + //int slnq(int n, LDBLE * a, LDBLE * delta, int ncols, int print); +#endif + int calc_gas_pressures(void); + int calc_fixed_volume_gas_pressures(void); + int calc_ss_fractions(void); + int gammas(LDBLE mu); + int gammas_a_f(int i); + int initial_guesses(void); + int revise_guesses(void); + int ss_binary(cxxSS* ss_ptr); + int ss_ideal(cxxSS* ss_ptr); + + // parse.cpp ------------------------------- + int check_eqn(int association); + int get_charge(char* charge, LDBLE* z); + int get_elt(const char** t_ptr, std::string& element, int* i); + int get_elts_in_species(const char** t_ptr, LDBLE coef); + int get_num(const char** t_ptr, LDBLE* num); + int get_secondary_in_species(const char** t_ptr, LDBLE coef); + int parse_eq(char* eqn, std::vector& new_elt_list, int association); + int get_coef(LDBLE* coef, const char** eqnaddr); + int get_secondary(const char** t_ptr, char* element, int* i); + int get_species(const char** ptr); + + // phqalloc.cpp ------------------------------- +public: +#if !defined(NDEBUG) + void* PHRQ_malloc(size_t, const char*, int); + void* PHRQ_calloc(size_t, size_t, const char*, int); + void* PHRQ_realloc(void*, size_t, const char*, int); +#else + void* PHRQ_malloc(size_t); + void* PHRQ_calloc(size_t, size_t); + void* PHRQ_realloc(void*, size_t); +#endif + void PHRQ_free(void* ptr); + void PHRQ_free_all(void); + +public: + + // pitzer.cpp ------------------------------- + class pitz_param* pitz_param_read(char* string, int n); + void pitz_param_store(class pitz_param* pzp_ptr); + void sit_param_store(class pitz_param* pzp_ptr); + class pitz_param* pitz_param_copy(const class pitz_param* src); + class theta_param* theta_param_search(LDBLE zj, LDBLE zk); + void pitzer_make_lists(void); + int gammas_pz(bool exch_a_f); + int model_pz(void); + int pitzer(void); + int pitzer_clean_up(void); + int pitzer_init(void); + int pitzer_tidy(void); + int read_pitzer(void); + int set_pz(int initial); + int calc_pitz_param(class pitz_param* pz_ptr, LDBLE TK, LDBLE TR); + int check_gammas_pz(void); + int ISPEC(const char* name); + LDBLE G(LDBLE Y); + LDBLE GP(LDBLE Y); + int ETHETAS(LDBLE ZJ, LDBLE ZK, LDBLE I, LDBLE* etheta, + LDBLE* ethetap); + void ETHETA_PARAMS(LDBLE X, LDBLE& JAY, LDBLE& JPRIME); + int pitzer_initial_guesses(void); + int pitzer_revise_guesses(void); + int PTEMP(LDBLE TK); + int jacobian_pz(void); + + // prep.cpp ------------------------------- + int add_potential_factor(void); + int add_cd_music_factors(int n); + int add_surface_charge_balance(void); + int add_cd_music_charge_balances(int i); + int build_gas_phase(void); + int build_fixed_volume_gas(void); + int build_jacobian_sums(int k); + int build_mb_sums(void); + int build_min_exch(void); + int build_model(void); + int build_pure_phases(void); + int build_ss_assemblage(void); + int build_solution_phase_boundaries(void); + int build_species_list(int n); + int build_min_surface(void); + LDBLE calc_lk_phase(phase* p_ptr, LDBLE TK, LDBLE pa); + LDBLE calc_PR(std::vector phase_ptrs, LDBLE P, LDBLE TK, LDBLE V_m); + LDBLE calc_PR(); + int calc_vm(LDBLE tc, LDBLE pa); + int clear(void); + int convert_units(cxxSolution* solution_ptr); + class unknown* find_surface_charge_unknown(std::string& str_ptr, int plane); + std::vector get_list_master_ptrs(const char* cptr, class master* master_ptr); + int inout(void); + int is_special(class species* spec); + int mb_for_species_aq(int n); + int mb_for_species_ex(int n); + int mb_for_species_surf(int n); + int quick_setup(void); + int resetup_master(void); + int save_model(void); + int setup_exchange(void); + int setup_gas_phase(void); + int setup_fixed_volume_gas(void); + int setup_master_rxn(const std::vector& master_ptr_list, + const std::string& pe_rxn); + int setup_pure_phases(void); + int adjust_setup_pure_phases(void); + int setup_related_surface(void); + int setup_ss_assemblage(void); + int setup_solution(void); + int adjust_setup_solution(void); + int setup_surface(void); + int setup_unknowns(void); + int store_dn(int k, LDBLE* source, int row, LDBLE coef_in, + LDBLE* gamma_source); + int store_jacob(LDBLE* source, LDBLE* target, LDBLE coef); + int store_jacob0(int row, int column, LDBLE coef); + int store_mb(LDBLE* source, LDBLE* target, LDBLE coef); + int store_mb_unknowns(class unknown* unknown_ptr, LDBLE* LDBLE_ptr, + LDBLE coef, LDBLE* gamma_ptr); + int store_sum_deltas(LDBLE* source, LDBLE* target, LDBLE coef); + int tidy_redox(void); + int write_mb_eqn_x(void); + int write_mb_for_species_list(int n); + int write_mass_action_eqn_x(int stop); + + int check_same_model(void); + int k_temp(LDBLE tc, LDBLE pa); + LDBLE k_calc(LDBLE* logk, LDBLE tempk, LDBLE presPa); + int prep(void); + int reprep(void); + int rewrite_master_to_secondary(class master* master_ptr1, + class master* master_ptr2); + int switch_bases(void); + int write_phase_sys_total(int n); + + // print.cpp ------------------------------- + char* sformatf(const char* format, ...); + int array_print(LDBLE* array_l, int row_count, int column_count, + int max_column_count); + int set_pr_in_false(void); + int print_all(void); + int print_exchange(void); + int print_gas_phase(void); + int print_master_reactions(void); + int print_species(void); + int print_surface(void); + int print_user_print(void); + int punch_all(void); + int print_alkalinity(void); + int print_diffuse_layer(cxxSurfaceCharge* surface_charge_ptr); + int print_eh(void); + int print_reaction(void); + int print_kinetics(void); + int print_mix(void); + int print_pp_assemblage(void); + int print_ss_assemblage(void); + int print_saturation_indices(void); + int print_surface_cd_music(void); + int print_totals(void); + int print_using(void); + int punch_gas_phase(void); + int punch_identifiers(void); + int punch_kinetics(void); + int punch_molalities(void); + int punch_activities(void); + int punch_pp_assemblage(void); + int punch_ss_assemblage(void); + int punch_saturation_indices(void); + int punch_totals(void); + int punch_user_punch(void); +#if defined MULTICHART + int punch_user_graph(void); +#endif + + // read.cpp ------------------------------- + int read_input(void); + int* read_list_ints_range(const char** ptr, int* count_ints, int positive, + int* int_list); + int read_log_k_only(const char* cptr, LDBLE* log_k); + int read_t_c_only(const char* cptr, LDBLE* t_c); + int read_p_c_only(const char* cptr, LDBLE* p_c); + int read_omega_only(const char* cptr, LDBLE* omega); + int read_number_description(const char* cptr, int* n_user, int* n_user_end, + char** description, int allow_negative = FALSE); + int check_key(const char* str); + int check_units(std::string& tot_units, bool alkalinity, bool check_compatibility, + const char* default_units, bool print); + int find_option(const char* item, int* n, const char** list, int count_list, + int exact); + int get_option(const char** opt_list, int count_opt_list, const char** next_char); + int get_true_false(const char* string, int default_value); + + int add_psi_master_species(char* token); + int read_advection(void); + int read_analytical_expression_only(const char* cptr, LDBLE* log_k); + /* VP: Density Start */ + int read_millero_abcdef(const char* cptr, LDBLE* abcdef); + /* VP: Density End */ + int read_viscosity_parms(const char* cptr, LDBLE* Jones_Dole); + int read_copy(void); + int read_debug(void); + int read_delta_h_only(const char* cptr, LDBLE* delta_h, + DELTA_H_UNIT* units); + int read_aq_species_vm_parms(const char* cptr, LDBLE* delta_v); + int read_vm_only(const char* cptr, LDBLE* delta_v, + DELTA_V_UNIT* units); + int read_phase_vm(const char* cptr, LDBLE* delta_v, + DELTA_V_UNIT* units); + int read_llnl_aqueous_model_parameters(void); + int read_exchange(void); + int read_exchange_master_species(void); + int read_exchange_species(void); + int read_gas_phase(void); + int read_incremental_reactions(void); + int read_inverse(void); + int read_inv_balances(class inverse* inverse_ptr, const char* next_char); + int read_inv_isotopes(class inverse* inverse_ptr, const char* cptr); + int read_inv_phases(class inverse* inverse_ptr, const char* next_char); + int read_kinetics(void); + bool read_vector_doubles(const char** ptr, std::vector& v); + bool read_vector_ints(const char** cptr, std::vector& v, int positive); + bool read_vector_t_f(const char** ptr, std::vector& v); + int read_master_species(void); + int read_mix(void); + int read_entity_mix(std::map& mix_map); + //int read_solution_mix(void); + int read_named_logk(void); + int read_phases(void); + int read_print(void); + int read_pp_assemblage(void); + int read_rates(void); + int read_reaction(void); + int read_reaction_reactants(cxxReaction* reaction_ptr); + int read_reaction_steps(cxxReaction* reaction_ptr); + int read_solid_solutions(void); + int read_temperature(void); + //int read_reaction_temps(struct temperature* temperature_ptr); + int read_reaction_pressure(void); + int read_reaction_pressure_raw(void); + int read_save(void); + int read_selected_output(void); + int read_solution(void); + int read_species(void); + int read_surface(void); + int read_surface_master_species(void); + int read_surface_species(void); + int read_use(void); + int read_title(void); + int read_user_print(void); + int read_user_punch(void); +#if defined MULTICHART + int read_user_graph_handler(); +#endif + int next_keyword_or_option(const char** opt_list, int count_opt_list); + int cleanup_after_parser(CParser& parser); + + // ReadClass.cxx + int read_dump(void); + int read_delete(void); + int read_run_cells(void); + int streamify_to_next_keyword(std::istringstream& lines); + int dump_entities(void); + int delete_entities(void); + int run_as_cells(void); + void dump_ostream(std::ostream& os); + + // readtr.cpp ------------------------------- + int read_transport(void); + int dump(void); + //int dump_exchange(int k); + //int dump_gas_phase(int k); + //int dump_kinetics(int k); + //int dump_mix(int k); + //int dump_pp_assemblage(int k); + //int dump_reaction(int k); + //int dump_ss_assemblage(int k); + //int dump_solution(int k); + //int dump_surface(int k); + int dump_cpp(void); + int read_line_LDBLEs(const char* next_char, LDBLE** d, int* count_d, + int* count_alloc); + + // sit.cpp ------------------------------- + int gammas_sit(void); + int model_sit(void); + int sit(void); + int sit_clean_up(void); + int sit_init(void); + int sit_tidy(void); + int read_sit(void); + int set_sit(int initial); + int calc_sit_param(class pitz_param* pz_ptr, LDBLE TK, LDBLE TR); + int check_gammas_sit(void); + int sit_ISPEC(const char* name); + /*int DH_AB (LDBLE TK, LDBLE *A, LDBLE *B);*/ + int sit_initial_guesses(void); + int sit_revise_guesses(void); + int PTEMP_SIT(LDBLE tk); + void sit_make_lists(void); + int jacobian_sit(void); + + // spread.cpp ------------------------------- + int read_solution_spread(void); + int copy_token_tab(std::string& token, const char** cptr); + int get_option_string(const char** opt_list, int count_opt_list, + const char** next_char); + int spread_row_free(class spread_row* spread_row_ptr); + int spread_row_to_solution(class spread_row* heading, + class spread_row* units, + class spread_row* data, + class defaults defaults); + class spread_row* string_to_spread_row(char* string); +#ifdef PHREEQCI_GUI + void add_row(class spread_row* spread_row_ptr); + void free_spread(void); + class spread_row* copy_row(class spread_row* spread_row_ptr); +#endif + + // step.cpp ------------------------------- + int step(LDBLE step_fraction); + int xsolution_zero(void); + int add_exchange(cxxExchange* exchange_ptr); + int add_gas_phase(cxxGasPhase* gas_phase_ptr); + int add_kinetics(cxxKinetics* kinetics_ptr); + int add_mix(cxxMix* mix_ptr); + int add_pp_assemblage(cxxPPassemblage* pp_assemblage_ptr); + int add_reaction(cxxReaction* reaction_ptr, int step_number, LDBLE step_fraction); + int add_ss_assemblage(cxxSSassemblage* ss_assemblage_ptr); + int add_solution(cxxSolution* solution_ptr, LDBLE extensive, + LDBLE intensive); + int add_surface(cxxSurface* surface_ptr); + int check_pp_assemblage(cxxPPassemblage* pp_assemblage_ptr); + int gas_phase_check(cxxGasPhase* gas_phase_ptr); + int pp_assemblage_check(cxxPPassemblage* pp_assemblage_ptr); + int reaction_calc(cxxReaction* reaction_ptr); + int solution_check(void); + int ss_assemblage_check(cxxSSassemblage* ss_assemblage_ptr); + + // structures.cpp ------------------------------- + int clean_up(void); + int reinitialize(void); + + int copier_add(class copier* copier_ptr, int n_user, int start, int end); + int copier_clear(class copier* copier_ptr); + // + CReaction CReaction_internal_copy(CReaction& rxn_ref); + double rxn_find_coef(CReaction& r_ptr, const char* str); + // + static int element_compare(const void* ptr1, const void* ptr2); + class element* element_store(const char* element); + // + int add_elt_list(const cxxNameDouble& nd, LDBLE coef); + int add_elt_list(const std::vector& el, double coef); + int change_hydrogen_in_elt_list(LDBLE charge); + int elt_list_combine(void); + static int elt_list_compare(const void* ptr1, const void* ptr2); + std::vector elt_list_internal_copy(const std::vector& el); + std::vector elt_list_vsave(void); + cxxNameDouble elt_list_NameDouble(void); + // + enum entity_type get_entity_enum(char* name); + // + class inverse* inverse_alloc(void); + int inverse_delete(int i); + static int inverse_isotope_compare(const void* ptr1, const void* ptr2); + class inverse* inverse_search(int n_user, int* n); + int inverse_sort(void); + // + class logk* logk_alloc(void); + int logk_copy2orig(class logk* logk_ptr); + class logk* logk_store(const char* name, int replace_if_found); + class logk* logk_search(const char* name); + // + class master* master_alloc(void); + static int master_compare(const void* ptr1, const void* ptr2); + int master_delete(const char* cptr); + class master* master_bsearch(const char* cptr); + class master* master_bsearch_primary(const char* cptr); + class master* master_bsearch_secondary(const char* cptr); + class master* master_search(const char* cptr, int* n); + class master* surface_get_psi_master(const char* name, int plane); + // + class phase* phase_bsearch(const char* cptr, int* j, int print); + static int phase_compare(const void* ptr1, const void* ptr2); + int phase_delete(int i); + class phase* phase_store(const char* name); + // + class rate* rate_bsearch(const char* cptr, int* j); + int rate_free(class rate* rate_ptr); + class rate* rate_copy(const class rate* rate_ptr); + class rate* rate_search(const char* name, int* n); + int rate_sort(void); + // + static int s_compare(const void* ptr1, const void* ptr2); + int s_delete(int i); + class species* s_search(const char* name); + class species* s_store(const char* name, LDBLE z, int replace_if_found); + // + static int isotope_compare(const void* ptr1, const void* ptr2); + // + static int species_list_compare_alk(const void* ptr1, const void* ptr2); + static int species_list_compare_master(const void* ptr1, const void* ptr2); + int species_list_sort(void); + // + struct Change_Surf* change_surf_alloc(int count); + // + int system_duplicate(int i, int save_old); + // + // + bool phase_rxn_to_trxn(class phase* phase_ptr, CReaction& rxn_ptr); + bool trxn_add(CReaction& r_ptr, double coef, bool combine); + bool trxn_add_phase(CReaction& r_ref, double coef, bool combine); + int trxn_combine(void); + static int trxn_compare(const void* ptr1, const void* ptr2); + bool trxn_copy(CReaction& rxn_ref); + LDBLE trxn_find_coef(const char* str, int start); + int trxn_multiply(LDBLE coef); + int trxn_print(void); + int trxn_reverse_k(void); + int trxn_sort(void); + int trxn_swap(const char* token); + + class unknown* unknown_alloc(void); + int unknown_delete(int i); + int unknown_free(class unknown* unknown_ptr); + int entity_exists(const char* name, int n_user); + static int inverse_compare(const void* ptr1, const void* ptr2); + int inverse_free(class inverse* inverse_ptr); + int logk_init(class logk* logk_ptr); + static int master_compare_string(const void* ptr1, const void* ptr2); + int master_free(class master* master_ptr); + class phase* phase_alloc(void); + static int phase_compare_string(const void* ptr1, const void* ptr2); + int phase_free(class phase* phase_ptr); + int phase_init(class phase* phase_ptr); + static int rate_compare(const void* ptr1, const void* ptr2); + static int rate_compare_string(const void* ptr1, const void* ptr2); + class species* s_alloc(void); + int s_free(class species* s_ptr); + int s_init(class species* s_ptr); + static int species_list_compare(const void* ptr1, const void* ptr2); + + void Use2cxxStorageBin(cxxStorageBin& sb); + void phreeqc2cxxStorageBin(cxxStorageBin& sb); + void phreeqc2cxxStorageBin(cxxStorageBin& sb, int n); + void cxxStorageBin2phreeqc(cxxStorageBin& sb, int n); + void cxxStorageBin2phreeqc(cxxStorageBin& sb); + + /* tally.cpp */ + void add_all_components_tally(void); + int build_tally_table(void); + int calc_dummy_kinetic_reaction_tally(cxxKinetics* kinetics_ptr); + int diff_tally_table(void); + int extend_tally_table(void); + int free_tally_table(void); + int fill_tally_table(int* n_user, int index_conservative, int n_buffer); + int get_tally_table_rows_columns(int* rows, int* columns); + int get_tally_table_column_heading(int column, int* type, char* string); + int get_tally_table_row_heading(int column, char* string); + int store_tally_table(LDBLE* array, int row_dim, int col_dim, + LDBLE fill_factor); + int zero_tally_table(void); + int elt_list_to_tally_table(class tally_buffer* buffer_ptr); + int master_to_tally_table(class tally_buffer* buffer_ptr); + int get_all_components(void); + int print_tally_table(void); + int set_reaction_moles(int n_user, LDBLE moles); + int set_reaction_temperature(int n_user, LDBLE tc); + int set_kinetics_time(int n_user, LDBLE step); + + // tidy.cpp ------------------------------- + int add_other_logk(LDBLE* source_k, std::vector& add_logk); + int add_logks(class logk* logk_ptr, int repeats); + LDBLE halve(LDBLE f(LDBLE x, void*), LDBLE x0, LDBLE x1, LDBLE tol); + int replace_solids_gases(void); + int ss_prep(LDBLE t, cxxSS* ss_ptr, int print); + int select_log_k_expression(LDBLE* source_k, LDBLE* target_k); + int slnq(int n, LDBLE* a, LDBLE* delta, int ncols, int print); +public: + int tidy_punch(void); + int tidy_model(void); + int check_species_input(void); + LDBLE coef_in_master(class master* master_ptr); + int reset_last_model(void); + int rewrite_eqn_to_primary(void); + int rewrite_eqn_to_secondary(void); + int species_rxn_to_trxn(class species* s_ptr); + int tidy_logk(void); + int tidy_exchange(void); + int tidy_min_exchange(void); + int update_min_exchange(void); + int tidy_kin_exchange(void); + int update_kin_exchange(void); + int tidy_gas_phase(void); + int tidy_inverse(void); + int tidy_isotopes(void); + int tidy_isotope_ratios(void); + int tidy_isotope_alphas(void); + int tidy_kin_surface(void); + int update_kin_surface(void); + int tidy_master_isotope(void); + int tidy_min_surface(void); + int update_min_surface(void); + int tidy_phases(void); + int tidy_pp_assemblage(void); + int tidy_solutions(void); + int tidy_ss_assemblage(void); + int tidy_species(void); + int tidy_surface(void); + int scan(LDBLE f(LDBLE x, void*), LDBLE* xx0, LDBLE* xx1); + static LDBLE f_spinodal(LDBLE x, void*); + int solve_misc(LDBLE* xxc1, LDBLE* xxc2, LDBLE tol); + int ss_calc_a0_a1(cxxSS* ss_ptr); + + // transport.cpp ------------------------------- + int transport(void); + void print_punch(int i, boolean active); + int set_initial_moles(int i); + cxxSurface sum_surface_comp(cxxSurface* source1, LDBLE f1, + cxxSurface* source2, std::string charge_name, LDBLE f2, + LDBLE new_Dw); + int reformat_surf(const char* comp_name, LDBLE fraction, const char* new_comp_name, + LDBLE new_Dw, int cell); + LDBLE viscosity(void); + LDBLE calc_vm_Cl(void); + int multi_D(LDBLE DDt, int mobile_cell, int stagnant); + LDBLE find_J(int icell, int jcell, LDBLE mixf, LDBLE DDt, int stagnant); + void calc_b_ij(int icell, int jcell, int k, LDBLE b_i, LDBLE b_j, LDBLE g_i, LDBLE g_j, LDBLE free_i, LDBLE free_j, int stagnant); + void diffuse_implicit(LDBLE DDt, int stagnant); + int fill_spec(int cell_no, int ref_cell); + LDBLE moles_from_redox_states(cxxSolution* sptr, const char* name); + LDBLE moles_from_donnan_layer(cxxSurface* sptr, const char* name, LDBLE moles_needed); + LDBLE add_MCD_moles(LDBLE moles, LDBLE min_mol, int i, cxxSolution* sptr, const char* name); + int fill_m_s(class J_ij* J_ij, int J_ij_count_spec, int i, int stagnant); + static int sort_species_name(const void* ptr1, const void* ptr2); + int disp_surf(LDBLE stagkin_time); + int diff_stag_surf(int mobile_cell); + int check_surfaces(cxxSurface* surface_ptr1, cxxSurface* surface_ptr2); + cxxSurface mobile_surface_copy(cxxSurface* surface_old_ptr, + int n_user_new, + bool move_old); + void transport_cleanup(void); + int init_mix(void); + int init_heat_mix(int nmix); + int heat_mix(int heat_nmix); + int mix_stag(int i, LDBLE stagkin_time, int punch, + LDBLE step_fraction_kin); + + // utilities.cpp ------------------------------- +public: + double calc_alk(CReaction& rxn_ptr); + double calc_delta_v(CReaction& r_ref, bool phase); + LDBLE calc_dielectrics(LDBLE tc, LDBLE pa); + LDBLE calc_rho_0(LDBLE tc, LDBLE pa); + int compute_gfw(const char* string, LDBLE* gfw); + static int copy_token(char* token_ptr, const char** ptr, int* length); + static int copy_token(std::string& token, const char** ptr); + int dup_print(const char* cptr, int emphasis); + int equal(LDBLE a, LDBLE b, LDBLE eps); + void* free_check_null(void* ptr); + int get_token(const char** eqnaddr, std::string& string, LDBLE* z, int* l); + int islegit(const char c); + void malloc_error(void); + int parse_couple(char* token); + int print_centered(const char* string); + static int replace(const char* str1, const char* str2, char* str); + static void replace(std::string &stds, const char* str1, const char* str2); + static bool replace(const char* str1, const char* str2, std::string& str); + static int strcmp_nocase(const char* str1, const char* str2); + static int strcmp_nocase_arg1(const char* str1, const char* str2); + static void str_tolower(std::string& name); + void space(void** ptr, int i, int* max, int struct_size); + void squeeze_white(char* s_l); + int status(int count, const char* str, bool kinetics = false); + void str_tolower(char* str); + void str_toupper(char* str); +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + char* _string_duplicate(const char* token, const char* szFileName, int nLine); +#else + char* string_duplicate(const char* token); +#endif + const char* string_hsave(const char* str); + void strings_map_clear(); +protected: + char* string_pad(const char* str, int i); + static int string_trim(char* str); + static int string_trim_right(char* str); + static int string_trim_left(char* str); + static void string_trim(std::string& str); + static void string_trim_left(std::string& str); + static void string_trim_right(std::string& str); + static LDBLE under(LDBLE xval); + int get_input_errors(void); + int isamong(char c, const char* s_l); +public: + int main_method(int argc, char* argv[]); + void set_phast(int); + int next_user_number(Keywords::KEYWORDS key); + size_t list_components(std::list& list_c); + size_t list_EquilibriumPhases(std::list& list_pp); + size_t list_GasComponents(std::list& list_gc); + size_t list_KineticReactions(std::list& list_kr); + size_t list_SolidSolutions(std::list& list_comps, std::list& list_names); + size_t list_Surfaces(std::list& surftype, std::list& surf); + size_t list_Exchangers(std::list& ex); + PHRQ_io* Get_phrq_io(void) { return this->phrq_io; } + void Set_run_cells_one_step(const bool tf) { this->run_cells_one_step = tf; } + + + std::map& Get_Rxn_solution_map() { return this->Rxn_solution_map; } + std::map& Get_Rxn_exchange_map() { return this->Rxn_exchange_map; } + std::map& Get_Rxn_gas_phase_map() { return this->Rxn_gas_phase_map; } + std::map& Get_Rxn_kinetics_map() { return this->Rxn_kinetics_map; } + std::map& Get_Rxn_pp_assemblage_map() { return this->Rxn_pp_assemblage_map; } + std::map& Get_Rxn_ss_assemblage_map() { return this->Rxn_ss_assemblage_map; } + std::map& Get_Rxn_surface_map() { return this->Rxn_surface_map; } + std::map& Get_Rxn_mix_map() { return this->Rxn_mix_map; } + std::map& Get_Rxn_reaction_map() { return this->Rxn_reaction_map; } + std::map& Get_Rxn_temperature_map() { return this->Rxn_temperature_map; } + std::map& Get_Rxn_pressure_map() { return this->Rxn_pressure_map; } + +protected: + void init(void); + + // + //Data members + // +protected: + PHRQ_io* phrq_io; + PHRQ_io ioInstance; + int same_model; + + LDBLE current_tc; + LDBLE current_pa; + LDBLE current_mu; + bool mu_terms_in_logk; + + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ + + Model last_model; + //struct punch punch; + bool high_precision; + + /* ---------------------------------------------------------------------- + * Temperatures + * ---------------------------------------------------------------------- */ + + std::map Rxn_temperature_map; + + /* ---------------------------------------------------------------------- + * Pressures + * ---------------------------------------------------------------------- */ + std::map Rxn_pressure_map; + + /* ---------------------------------------------------------------------- + * Surface + * --------------------------------------------------------------------- */ + + int g_iterations; + LDBLE G_TOL; + std::map Rxn_surface_map; + std::map charge_group_map; + int change_surf_count; + struct Change_Surf* change_surf; + + /* ---------------------------------------------------------------------- + * Exchange + * ---------------------------------------------------------------------- */ + std::map Rxn_exchange_map; + + /* ---------------------------------------------------------------------- + * Kinetics + * ---------------------------------------------------------------------- */ + std::map Rxn_kinetics_map; + bool use_kinetics_limiter; + + /*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ + std::map save_values; + class save save; + + /*---------------------------------------------------------------------- + * Use + *---------------------------------------------------------------------- */ + cxxUse use; + + /*---------------------------------------------------------------------- + * Copy + *---------------------------------------------------------------------- */ + class copier copy_solution; + class copier copy_pp_assemblage; + class copier copy_exchange; + class copier copy_surface; + class copier copy_ss_assemblage; + class copier copy_gas_phase; + class copier copy_kinetics; + class copier copy_mix; + class copier copy_reaction; + class copier copy_temperature; + class copier copy_pressure; + + /*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ + std::vector inverse; + int count_inverse; + + /*---------------------------------------------------------------------- + * Mix + *---------------------------------------------------------------------- */ + std::map Rxn_mix_map; + std::map Dispersion_mix_map; + std::map Rxn_solution_mix_map; + std::map Rxn_exchange_mix_map; + std::map Rxn_gas_phase_mix_map; + std::map Rxn_kinetics_mix_map; + std::map Rxn_pp_assemblage_mix_map; + std::map Rxn_ss_assemblage_mix_map; + std::map Rxn_surface_mix_map; + /* + * List new definitions + */ + std::set Rxn_new_exchange; + std::set Rxn_new_gas_phase; + std::set Rxn_new_kinetics; // not used + std::set Rxn_new_mix; // not used + std::set Rxn_new_pp_assemblage; + std::set Rxn_new_pressure; // not used + std::set Rxn_new_reaction; // not used + std::set Rxn_new_solution; + std::set Rxn_new_ss_assemblage; + std::set Rxn_new_surface; + std::set Rxn_new_temperature; // not used + /*---------------------------------------------------------------------- + * Irreversible reaction + *---------------------------------------------------------------------- */ + std::map Rxn_reaction_map; + + /*---------------------------------------------------------------------- + * Gas phase + *---------------------------------------------------------------------- */ + std::map Rxn_gas_phase_map; + + /*---------------------------------------------------------------------- + * Solid solution + *---------------------------------------------------------------------- */ + std::map Rxn_ss_assemblage_map; + + /*---------------------------------------------------------------------- + * Pure-phase assemblage + *---------------------------------------------------------------------- */ + std::map Rxn_pp_assemblage_map; + + /*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ + std::vector species_list; + + /*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ + std::vector sum_jacob0; /* array of pointers to targets and coefficients for array */ + + std::vector sum_mb1; /* array of pointers to sources and targets for mass + balance summations with coef = 1.0 */ + std::vector sum_jacob1; /* array of pointers to sources and targets for array + equations with coef = 1.0 */ + std::vector sum_mb2; /* array of coefficients and pointers to sources and + targets for mass balance summations with coef != 1.0 */ + std::vector sum_jacob2; /* array of coefficients and pointers to sources and + targets, coef != 1.0 */ + std::vector sum_delta; /* array of pointers to sources, targets and coefficients for + summing deltas for mass balance equations */ + /*---------------------------------------------------------------------- + * Solution + *---------------------------------------------------------------------- */ + std::map Rxn_solution_map; + std::vector unnumbered_solutions; + bool save_species; + + /*---------------------------------------------------------------------- + * Global solution + *---------------------------------------------------------------------- */ + std::string title_x; + std::string last_title_x; + int new_x; + std::string description_x; + LDBLE tc_x; + LDBLE tk_x; + LDBLE patm_x; + LDBLE last_patm_x; + LDBLE potV_x; + bool numerical_fixed_volume; + bool force_numerical_fixed_volume; + //bool switch_numerical; + LDBLE ph_x; + LDBLE solution_pe_x; + LDBLE mu_x; + LDBLE ah2o_x; + LDBLE density_x; + LDBLE total_h_x; + LDBLE total_o_x; + LDBLE cb_x; + LDBLE total_ions_x; + LDBLE mass_water_aq_x; + LDBLE mass_water_surfaces_x; + LDBLE mass_water_bulk_x; + std::string units_x; + std::map < std::string, CReaction > pe_x; + std::map isotopes_x; + std::string default_pe_x; + cxxSurface::DIFFUSE_LAYER_TYPE dl_type_x; + LDBLE total_carbon; + LDBLE total_co2; + LDBLE total_alkalinity; + LDBLE gfw_water; + LDBLE step_x; + LDBLE kin_time_x; + + /*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ + int count_cells; + int count_shifts; + int ishift; + int bcon_first; + int bcon_last; + int correct_disp; + LDBLE tempr; + LDBLE timest; + int simul_tr; + LDBLE diffc; + LDBLE heat_diffc; + int cell; + LDBLE mcd_substeps; + class stag_data stag_data; + int print_modulus; + int punch_modulus; + int dump_in; + int dump_modulus; + int transport_warnings; + std::vector cell_data; + int old_cells, max_cells, all_cells; + int multi_Dflag; /* signals calc'n of multicomponent diffusion */ + int interlayer_Dflag; /* multicomponent diffusion and diffusion through interlayer porosity */ + int implicit; /* implicit calculation of diffusion */ + LDBLE max_mixf; /* the maximum value of the implicit mixfactor = De * Dt / (Dx^2) */ + LDBLE min_dif_LM; /* the minimal log10(molality) for including a species in multicomponent diffusion */ + LDBLE default_Dw; /* default species diffusion coefficient in water at 25oC, m2/s */ + int correct_Dw; /* if true, Dw is adapted in calc_SC */ + LDBLE multi_Dpor; /* uniform porosity of free porewater in solid medium */ + LDBLE interlayer_Dpor; /* uniform porosity of interlayer space of montmorillonite in solid medium */ + LDBLE multi_Dpor_lim; /* limiting free porewater porosity where transport stops */ + LDBLE interlayer_Dpor_lim; /* limiting interlayer porosity where transport stops */ + LDBLE multi_Dn; /* exponent to calculate pore water diffusion coefficient, + Dp = Dw * (multi_Dpor)^multi_Dn */ + LDBLE interlayer_tortf; /* tortuosity_factor in interlayer porosity, + Dpil = Dw / interlayer_tortf */ + + int cell_no, mixrun; + /*---------------------------------------------------------------------- + * Advection data + *---------------------------------------------------------------------- */ + int count_ad_cells; + int count_ad_shifts; + int print_ad_modulus; + int punch_ad_modulus; + std::vector advection_print, advection_punch; + LDBLE advection_kin_time; + LDBLE advection_kin_time_defined; + int advection_warnings; + + /*---------------------------------------------------------------------- + * Tidy data + *---------------------------------------------------------------------- */ + int new_model, new_exchange, new_pp_assemblage, new_surface, + new_reaction, new_temperature, new_mix, new_solution, new_gas_phase, + new_inverse, new_punch, new_ss_assemblage, new_kinetics, new_copy, + new_pitzer; + + /*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ + std::vector elements; + class element* element_h_one; + + /*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ + std::vector elt_list; + size_t count_elts; /* number of elements in elt_list = position of next */ + /*---------------------------------------------------------------------- + * Reaction + *---------------------------------------------------------------------- */ + bool run_cells_one_step; + /*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ + std::vector logk; + + std::string moles_per_kilogram_string; + + std::vector s; + std::vector< std::map < std::string, cxxSpeciesDL > > s_diff_layer; + std::vector s_x; + + class species* s_h2o; + class species* s_hplus; + class species* s_h3oplus; + class species* s_eminus; + class species* s_co3; + class species* s_h2; + class species* s_o2; + + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + std::vector phases; + + /*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + std::vector master; + + /*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ + std::vector x; + size_t count_unknowns; + size_t sit_aqueous_unknowns; + size_t max_unknowns; + + class unknown* ah2o_unknown; + class unknown* alkalinity_unknown; + class unknown* carbon_unknown; + class unknown* charge_balance_unknown; + class unknown* exchange_unknown; + class unknown* mass_hydrogen_unknown; + class unknown* mass_oxygen_unknown; + class unknown* mb_unknown; + class unknown* mu_unknown; + class unknown* pe_unknown; + class unknown* ph_unknown; + class unknown* pure_phase_unknown; + class unknown* solution_phase_boundary_unknown; + class unknown* surface_unknown; + class unknown* gas_unknown; + class unknown* ss_unknown; + std::vector gas_unknowns; + + /*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ + class reaction_temp trxn; /* structure array of working space while reading equations + species names are in "temp_strings" */ + size_t count_trxn; /* number of reactants in trxn = position of next */ + + std::vector mb_unknowns; + + /* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ + class prints pr; + bool status_on; + clock_t status_interval; + clock_t status_timer; + std::string status_string; + int count_warnings; + + /* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ + std::vector rates; + LDBLE rate_m, rate_m0, rate_time, rate_kin_time, rate_sim_time_start, + rate_sim_time_end, rate_sim_time, rate_moles, initial_total_time; + std::vector rate_p; + int count_rate_p; + + /* ---------------------------------------------------------------------- + * USER PRINT COMMANDS + * ---------------------------------------------------------------------- */ + class rate* user_print; + int n_user_punch_index; + + int fpunchf_user_s_warning; + char fpunchf_user_buffer[80]; + +#if defined MULTICHART + ChartHandler chart_handler; +public: + ChartHandler& Get_chart_handler(void) + { + return chart_handler; + } + const ChartHandler& Get_chart_handler(void)const + { + return chart_handler; + } +protected: +#endif + + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + const char* error_string; + int simulation; + int state; + int reaction_step; + int transport_step; + int transport_start; + int advection_step; + int stop_program; + int incremental_reactions; + + double MIN_LM; + double LOG_ZERO_MOLALITY; + double MIN_TOTAL; + double MIN_TOTAL_SS; + double MIN_RELATED_SURFACE; + double MIN_RELATED_LOG_ACTIVITY; + + int count_strings; + int max_strings; + + std::vector my_array, delta, residual; + + int input_error; + + Keywords::KEYWORDS next_keyword; + int parse_error; + int paren_count; + int iterations; + int gamma_iterations; + size_t density_iterations; + int run_reactions_iterations; + int overall_iterations; + + int max_line; + char* line; + char* line_save; + + LDBLE LOG_10; + + int debug_model; + int debug_prep; + int debug_mass_action; + int debug_mass_balance; + int debug_set; + int debug_diffuse_layer; + int debug_inverse; + + LDBLE inv_tol_default; + int itmax; + int max_tries; + LDBLE ineq_tol; + LDBLE convergence_tolerance; + LDBLE step_size; + LDBLE pe_step_size; + LDBLE step_size_now; + LDBLE pe_step_size_now; + LDBLE pp_scale; + LDBLE pp_column_scale; + int diagonal_scale; /* 0 not used, 1 used */ + int mass_water_switch; + int delay_mass_water; + int equi_delay; + bool dampen_ah2o; + LDBLE censor; + int aqueous_only; + int negative_concentrations; + int calculating_deriv; + int numerical_deriv; + + int count_total_steps; + int phast; + bool output_newline; + inline void Set_output_newline(bool tf) { this->output_newline = tf; } + inline bool Get_output_newline() { return this->output_newline; } + double a_llnl, b_llnl, bdot_llnl; + std::vector llnl_temp, llnl_adh, llnl_bdh, llnl_bdot, llnl_co2_coefs; + + //char *selected_output_file_name; + std::map SelectedOutput_map; + SelectedOutput* current_selected_output; + + std::map UserPunch_map; + UserPunch* current_user_punch; + + char* dump_file_name; + int remove_unstable_phases; + std::string screen_string; +#ifdef PHREEQCI_GUI + class spread_sheet g_spread_sheet; +#endif + int spread_length; + + /* ---------------------------------------------------------------------- */ + /* + * Map definitions + */ + + std::map strings_map; + std::map elements_map; + std::map species_map; + std::map phases_map; + std::map logk_map; + std::map master_isotope_map; + +#if defined(PHREEQCI_GUI) +#include "../../phreeqci_gui.h" +#endif /* defined(PHREEQCI_GUI) */ + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + std::vector master_isotope; + int initial_solution_isotopes; + std::vector calculate_value; + std::map calculate_value_map; + std::vector isotope_ratio; + std::map isotope_ratio_map; + std::vector isotope_alpha; + std::map isotope_alpha_map; + int phreeqc_mpi_myself; + int first_read_input; + std::string user_database; + + //int have_punch_name; + /* VP: Density Start */ + int print_density; + /* VP: Density End */ + + int print_viscosity; + LDBLE viscos, viscos_0, viscos_0_25; // viscosity of the solution, of pure water, of pure water at 25 C + LDBLE cell_pore_volume; + LDBLE cell_porosity; + LDBLE cell_volume; + LDBLE cell_saturation; + std::vector sys; + LDBLE sys_tot; + + LDBLE V_solutes, rho_0, rho_0_sat, kappa_0, p_sat/*, ah2o_x0*/; + LDBLE SC; // specific conductance mS/cm + LDBLE eps_r; // relative dielectric permittivity + LDBLE DH_A, DH_B, DH_Av; // Debye-Hueckel A, B and Av + LDBLE QBrn; // Born function d(ln(eps_r))/dP / eps_r * 41.84004, for supcrt calc'n of molal volume + LDBLE ZBrn; // Born function (-1/eps_r + 1) * 41.84004, for supcrt calc'n of molal volume + LDBLE dgdP; // dg / dP, pressure derivative of g-function, for supcrt calc'n of molal volume + + int need_temp_msg; + LDBLE solution_mass, solution_volume; + + /* phqalloc.cpp ------------------------------- */ + PHRQMemHeader* s_pTail; + + /* Basic */ + PBasic* basic_interpreter; + + double (*basic_callback_ptr) (double x1, double x2, const char* str, void* cookie); + void* basic_callback_cookie; +#ifdef IPHREEQC_NO_FORTRAN_MODULE + double (*basic_fortran_callback_ptr) (double* x1, double* x2, char* str, size_t l); +#else + double (*basic_fortran_callback_ptr) (double* x1, double* x2, const char* str, int l); +#endif +#if defined(SWIG) || defined(SWIG_IPHREEQC) + class BasicCallback* basicCallback; + void SetCallback(BasicCallback* cb) { basicCallback = cb; } +#endif + + /* cl1.cpp ------------------------------- */ + std::vector x_arg, res_arg, scratch; + /* gases.cpp ------------------------------- */ + LDBLE a_aa_sum, b2, b_sum, R_TK; + + /* input.cpp ------------------------------- */ + int check_line_return; + int reading_db; + + /* integrate.cpp ------------------------------- */ + LDBLE midpoint_sv; + LDBLE z_global, xd_global, alpha_global; + + /* inverse.cpp ------------------------------- */ + size_t max_row_count, max_column_count; + int carbon; + std::vector col_name, row_name; + size_t count_rows, count_optimize; + size_t col_phases, col_redox, col_epsilon, col_ph, col_water, + col_isotopes, col_phase_isotopes; + size_t row_mb, row_fract, row_charge, row_carbon, row_isotopes, + row_epsilon, row_isotope_epsilon, row_water; + std::vector inv_zero, array1, inv_res, inv_delta1, delta2, + delta3, inv_cu, delta_save; + std::vector min_delta, max_delta; + std::vector inv_iu, inv_is; + size_t klmd, nklmd, n2d; + int kode, iter; + LDBLE toler, error, max_pct, scaled_error; + class master* master_alk; + std::vector row_back, col_back; + std::vector good, bad, minimal; + size_t max_good, max_bad, max_minimal; + int count_good, count_bad, count_minimal, count_calls; + unsigned long soln_bits, phase_bits, current_bits, temp_bits; + FILE* netpath_file; + int count_inverse_models, count_pat_solutions; + int min_position[32], max_position[32], now[32]; + std::vector inverse_heading_names; + + /* kinetics.cpp ------------------------------- */ +public: + int count_pp, count_pg, count_ss; + void* cvode_kinetics_ptr; + int cvode_test; + int cvode_error; + int cvode_n_user; + int cvode_n_reactions; + realtype cvode_step_fraction; + realtype cvode_rate_sim_time; + realtype cvode_rate_sim_time_start; + realtype cvode_last_good_time; + realtype cvode_prev_good_time; + N_Vector cvode_last_good_y; + N_Vector cvode_prev_good_y; + M_Env kinetics_machEnv; + N_Vector kinetics_y, kinetics_abstol; + void* kinetics_cvode_mem; + cxxSSassemblage* cvode_ss_assemblage_save; + cxxPPassemblage* cvode_pp_assemblage_save; +protected: + std::vector m_temp, m_original, rk_moles, x0_moles; + int set_and_run_attempt; + + /* model.cpp ------------------------------- */ + int gas_in; + LDBLE min_value; + std::vector normal, ineq_array, res, cu, zero, delta1; + std::vector iu, is, back_eq; + + /* phrq_io_output.cpp ------------------------------- */ + int forward_output_to_log; + + /* phreeqc_files.cpp ------------------------------- */ + std::string default_data_base; + /* Pitzer */ + int pitzer_model, sit_model, pitzer_pe; + int full_pitzer, always_full_pitzer, ICON, IC; + LDBLE COSMOT; + LDBLE AW; + LDBLE VP, DW0; + std::vector pitz_params; + std::map< std::string, size_t > pitz_param_map; + std::vector theta_params; + int use_etheta; + LDBLE OTEMP, OPRESS; + LDBLE A0; + class pitz_param* aphi/* = NULL*/; + std::vector spec; + class species** cations, ** anions, ** neutrals; // pointers to spec + int count_cations, count_anions, count_neutrals; + int MAXCATIONS, FIRSTANION, MAXNEUTRAL; + class pitz_param* mcb0, * mcb1, * mcc0; + std::vector IPRSNT; + std::vector M, LGAMMA; + LDBLE BK[23], DK[23]; + + LDBLE dummy; + + /* print.cpp ------------------------------- */ + + /* read.cpp */ + const char* prev_next_char; +#if defined PHREEQ98 + int shifts_as_points; +#endif + + /* read_class.cxx */ + dumper dump_info; + StorageBinList delete_info; + runner run_info; + char* sformatf_buffer; + size_t sformatf_buffer_size; + + /* readtr.cpp */ + std::string dump_file_name_cpp; + + /* sit.cpp ------------------------------- */ + std::vector sit_params; + std::map< std::string, size_t > sit_param_map; + LDBLE sit_A0; + int sit_count_cations, sit_count_anions, sit_count_neutrals; + int sit_MAXCATIONS, sit_FIRSTANION, sit_MAXNEUTRAL; + std::vector sit_IPRSNT; + std::vector sit_M, sit_LGAMMA; + std::vector s_list, cation_list, neutral_list, anion_list, ion_list, param_list; + + /* tidy.cpp ------------------------------- */ + LDBLE a0, a1, kc, kb; + + /* tally.cpp ------------------------------- */ + class tally_buffer* t_buffer; + size_t tally_count_component; + //class tally* tally_table; + std::vector tally_table; + size_t count_tally_table_columns; + size_t count_tally_table_rows; + + /* transport.cpp ------------------------------- */ + class sol_D* sol_D; + class sol_D* sol_D_dbg; + class J_ij* J_ij, * J_ij_il; + int J_ij_count_spec; + + class M_S* m_s; + int count_m_s; + LDBLE tot1_h, tot1_o, tot2_h, tot2_o; + LDBLE diffc_max, diffc_tr, J_ij_sum; + int transp_surf; + LDBLE* heat_mix_array; + LDBLE* temp1, * temp2; + int nmix, heat_nmix; + LDBLE heat_mix_f_imm, heat_mix_f_m; + int warn_MCD_X, warn_fixed_Surf; + LDBLE current_x, current_A, fix_current; // current: coulomb / s, Ampere, fixed current (Ampere) + + /* utilities.cpp ------------------------------- */ + int spinner; + std::map gfw_map; + std::map rates_map; + + /* new after release of Version 3 */ + std::map > sum_species_map; + std::map > sum_species_map_db; + + friend class PBasic; + friend class ChartObject; + friend class IPhreeqc; + friend class TestIPhreeqc; + friend class TestSelectedOutput; + friend class IPhreeqcMMS; + friend class IPhreeqcPhast; + friend class PhreeqcRM; + + std::vector keycount; // used to mark keywords that have been read + +public: + static const class const_iso iso_defaults[]; + static const int count_iso_defaults; +}; +#endif /* _INC_PHREEQC_H */ + +#ifndef _INC_ISFINITE_H +#define _INC_ISFINITE_H +/********************************* +isfinite handling +(Note: Should NOT be guarded) +**********************************/ + +#if defined (PHREEQ98) || defined (_MSC_VER) +# define HAVE_FINITE +# define finite _finite +#else /*defined (PHREEQ98) || defined (_MSC_VER)*/ +# if defined(DJGPP) +# define HAVE_FINITE +# endif +#endif /*defined (PHREEQ98) || defined (_MSC_VER)*/ + +#if defined(HAVE_ISFINITE) +# if __GNUC__ && (__cplusplus >= 201103L) +# define PHR_ISFINITE(x) std::isfinite(x) +# else +# define PHR_ISFINITE(x) isfinite(x) +# endif +#elif defined(HAVE_FINITE) +# define PHR_ISFINITE(x) finite(x) +#elif defined(HAVE_ISNAN) +# define PHR_ISFINITE(x) ( ((x) == 0.0) || ((!isnan(x)) && ((x) != (2.0 * (x)))) ) +#else +# define PHR_ISFINITE(x) ( ((x) == 0.0) || (((x) == (x)) && ((x) != (2.0 * (x)))) ) +#endif +#endif // _INC_ISFINITE_H + +#ifndef _INC_UTILITIES_NAMESPACE_H +#define _INC_UTILITIES_NAMESPACE_H +namespace Utilities +{ + LDBLE get_nan(void); + + // operations on maps of entities (Solution, Exchange, ...) + template < typename T > + void Rxn_dump_raw(const T& b, std::ostream& s_oss, unsigned int indent) + { + typename T::const_iterator it; + for (it = b.begin(); it != b.end(); ++it) + { + // Adding logic to dump only non-negative entities + if (it->second.Get_n_user() >= 0) + { + it->second.dump_raw(s_oss, indent); + } + } + return; + } + + template < typename T > + void Rxn_dump_raw_range(const T& b, std::ostream& s_oss, int start, int end, unsigned int indent) + { + typename T::const_iterator it; + for (int i = start; i <= end; i++) + { + if (i < 0) continue; + it = b.find(i); + if (it != b.end()) + { + it->second.dump_raw(s_oss, indent); + } + } + return; + } + + template < typename T > + T* Rxn_find(std::map < int, T >& b, int i) + { + if (b.find(i) != b.end()) + { + return (&(b.find(i)->second)); + } + else + { + return (NULL); + } + } + + template < typename T > + int Rxn_next_user_number(std::map < int, T >& b) + { + int ret = 0; + if (b.size() != 0) + { + ret = b.rbegin()->first + 1; + } + return ret; + } + + template < typename T > + T* Rxn_copy(std::map < int, T >& b, int i, int j) + { + typename std::map < int, T >::iterator it; + it = b.find(i); + if (it != b.end()) + { + b[j] = it->second; + it = b.find(j); + it->second.Set_n_user(j); + it->second.Set_n_user_end(j); + return &(it->second); + } + else + { + return (NULL); + } + } + + template < typename T > + void Rxn_copies(std::map < int, T >& b, int n_user, int n_user_end) + { + if (n_user_end <= n_user) return; + typename std::map < int, T >::iterator it; + it = b.find(n_user); + if (it != b.end()) + { + for (int j = n_user + 1; j <= n_user_end; j++) + { + b[j] = it->second; + it = b.find(j); + it->second.Set_n_user(j); + it->second.Set_n_user_end(j); + } + } + } + template < typename T > + int Rxn_read_raw(std::map < int, T >& m, std::set < int >& s, Phreeqc* phreeqc_cookie) + { + typename std::map < int, T >::iterator it; + assert(!phreeqc_cookie->reading_database()); + + T entity(phreeqc_cookie->Get_phrq_io()); + + CParser parser(phreeqc_cookie->Get_phrq_io()); + entity.read_raw(parser); + + // Store + if (entity.Get_base_error_count() == 0) + { + m[entity.Get_n_user()] = entity; + } + + // Make copies if necessary + Utilities::Rxn_copies(m, entity.Get_n_user(), entity.Get_n_user_end()); + for (int i = entity.Get_n_user(); i <= entity.Get_n_user_end(); i++) + { + s.insert(i); + } + return phreeqc_cookie->cleanup_after_parser(parser); + } + + template < typename T > + int Rxn_read_modify(std::map < int, T >& m, std::set < int >& s, Phreeqc* phreeqc_cookie) + { + typename std::map < int, T >::iterator it; + + CParser parser(phreeqc_cookie->Get_phrq_io()); + + std::string key_name; + std::string::iterator b = parser.line().begin(); + std::string::iterator e = parser.line().end(); + CParser::copy_token(key_name, b, e); + + cxxNumKeyword nk; + nk.read_number_description(parser); + T* entity_ptr = Utilities::Rxn_find(m, nk.Get_n_user()); + if (!entity_ptr) + { + std::ostringstream errstr; + errstr << "Could not find " << key_name << " " << nk.Get_n_user() << ", ignoring modify data.\n"; + phreeqc_cookie->warning_msg(errstr.str().c_str()); + //phreeqc_cookie->error_msg(errstr.str().c_str(), PHRQ_io::OT_STOP); + + // Don't throw, read data into dummy entity, then ignore + T entity; + entity_ptr = &entity; + entity_ptr->read_raw(parser, false); + return phreeqc_cookie->cleanup_after_parser(parser); + } + + entity_ptr->read_raw(parser, false); + entity_ptr->Set_n_user(nk.Get_n_user()); + entity_ptr->Set_n_user_end(nk.Get_n_user_end()); + entity_ptr->Set_description(nk.Get_description()); + s.insert(entity_ptr->Get_n_user()); + + return phreeqc_cookie->cleanup_after_parser(parser); + } + + template < typename T > + int SB_read_modify(std::map < int, T >& m, CParser& parser) + { + typename std::map < int, T >::iterator it; + + std::string key_name; + std::string::iterator b = parser.line().begin(); + std::string::iterator e = parser.line().end(); + CParser::copy_token(key_name, b, e); + + cxxNumKeyword nk; + nk.read_number_description(parser); + T* entity_ptr = Utilities::Rxn_find(m, nk.Get_n_user()); + if (!entity_ptr) + { + std::ostringstream errstr; + errstr << "Could not find " << key_name << " " << nk.Get_n_user() << ", ignoring modify data.\n"; + //io->warning_msg(errstr.str().c_str()); + + // Don't throw, read data into dummy entity, then ignore + T entity; + entity_ptr = &entity; + entity_ptr->read_raw(parser, false); + return FALSE; + } + + entity_ptr->read_raw(parser, false); + entity_ptr->Set_n_user(nk.Get_n_user()); + entity_ptr->Set_n_user_end(nk.Get_n_user_end()); + entity_ptr->Set_description(nk.Get_description()); + + return TRUE; + } + + template < typename T > + void Rxn_mix(std::map & mix_map, std::map < int, T >& entity_map, Phreeqc* phreeqc_cookie) + { + std::map::iterator mix_it; + for (mix_it = mix_map.begin(); mix_it != mix_map.end(); mix_it++) + { + T entity(entity_map, mix_it->second, mix_it->second.Get_n_user(), phreeqc_cookie->Get_phrq_io()); + entity_map[mix_it->second.Get_n_user()] = entity; + Utilities::Rxn_copies(entity_map, mix_it->second.Get_n_user(), mix_it->second.Get_n_user_end()); + } + mix_map.clear(); + } + +} // namespace Utilities + + +#if defined(PHREEQCI_GUI) +void PhreeqcIWait(Phreeqc* phreeqc); +#endif + +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) +#define string_duplicate(s) _string_duplicate(s, __FILE__, __LINE__) +#endif +#if defined(_DEBUG) +char* _string_duplicate(const char* token, const char* szFileName, int nLine); +#endif + +#endif //_INC_UTILITIES_NAMESPACE_H diff --git a/phreeqcpp/PhreeqcKeywords/Keywords.cpp b/phreeqcpp/PhreeqcKeywords/Keywords.cpp new file mode 100644 index 00000000..c92bf0de --- /dev/null +++ b/phreeqcpp/PhreeqcKeywords/Keywords.cpp @@ -0,0 +1,232 @@ +#include "Keywords.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +Keywords::Keywords(void) +{ +} + + +Keywords::~Keywords(void) +{ +} + +Keywords::KEYWORDS Keywords::Keyword_search(std::string key) +{ + std::map::const_iterator it; + it = phreeqc_keywords.find(key); + if (it != Keywords::phreeqc_keywords.end()) + { + return it->second; + } + return Keywords::KEY_NONE; +} + +const std::string & Keywords::Keyword_name_search(Keywords::KEYWORDS key) +{ + std::map::const_iterator it; + it = phreeqc_keyword_names.find(key); + if (it != Keywords::phreeqc_keyword_names.end()) + { + return it->second; + } + it = phreeqc_keyword_names.find(KEY_NONE); + return it->second; +} + +const std::map::value_type temp_keywords[] = { +std::map::value_type("eof", Keywords::KEY_END), +std::map::value_type("end", Keywords::KEY_END), +std::map::value_type("solution_species", Keywords::KEY_SOLUTION_SPECIES), +std::map::value_type("solution_master_species", Keywords::KEY_SOLUTION_MASTER_SPECIES), +std::map::value_type("solution", Keywords::KEY_SOLUTION), +std::map::value_type("phases", Keywords::KEY_PHASES), +std::map::value_type("pure_phases", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("reaction", Keywords::KEY_REACTION), +std::map::value_type("mix", Keywords::KEY_MIX), +std::map::value_type("use", Keywords::KEY_USE), +std::map::value_type("save", Keywords::KEY_SAVE), +std::map::value_type("exchange_species", Keywords::KEY_EXCHANGE_SPECIES), +std::map::value_type("exchange_master_species", Keywords::KEY_EXCHANGE_MASTER_SPECIES), +std::map::value_type("exchange", Keywords::KEY_EXCHANGE), +std::map::value_type("surface_species", Keywords::KEY_SURFACE_SPECIES), +std::map::value_type("surface_master_species", Keywords::KEY_SURFACE_MASTER_SPECIES), +std::map::value_type("surface", Keywords::KEY_SURFACE), +std::map::value_type("reaction_temperature", Keywords::KEY_REACTION_TEMPERATURE), +std::map::value_type("inverse_modeling", Keywords::KEY_INVERSE_MODELING), +std::map::value_type("gas_phase", Keywords::KEY_GAS_PHASE), +std::map::value_type("transport", Keywords::KEY_TRANSPORT), +std::map::value_type("debug", Keywords::KEY_KNOBS), +std::map::value_type("selected_output", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("select_output", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("knobs", Keywords::KEY_KNOBS), +std::map::value_type("print", Keywords::KEY_PRINT), +std::map::value_type("equilibrium_phases", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("equilibria", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("equilibrium", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("pure", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("title", Keywords::KEY_TITLE), +std::map::value_type("comment", Keywords::KEY_TITLE), +std::map::value_type("advection", Keywords::KEY_ADVECTION), +std::map::value_type("kinetics", Keywords::KEY_KINETICS), +std::map::value_type("incremental_reactions", Keywords::KEY_INCREMENTAL_REACTIONS), +std::map::value_type("incremental", Keywords::KEY_INCREMENTAL_REACTIONS), +std::map::value_type("rates", Keywords::KEY_RATES), +std::map::value_type("solution_s", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("user_print", Keywords::KEY_USER_PRINT), +std::map::value_type("user_punch", Keywords::KEY_USER_PUNCH), +std::map::value_type("solid_solutions", Keywords::KEY_SOLID_SOLUTIONS), +std::map::value_type("solid_solution", Keywords::KEY_SOLID_SOLUTIONS), +std::map::value_type("solution_spread", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("spread_solution", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("selected_out", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("select_out", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("user_graph", Keywords::KEY_USER_GRAPH), +std::map::value_type("llnl_aqueous_model_parameters",Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS), +std::map::value_type("llnl_aqueous_model", Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS), +std::map::value_type("database", Keywords::KEY_DATABASE), +std::map::value_type("named_analytical_expression", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_analytical_expressions", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_expressions", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_log_k", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("isotopes", Keywords::KEY_ISOTOPES), +std::map::value_type("calculate_values", Keywords::KEY_CALCULATE_VALUES), +std::map::value_type("isotope_ratios", Keywords::KEY_ISOTOPE_RATIOS), +std::map::value_type("isotope_alphas", Keywords::KEY_ISOTOPE_ALPHAS), +std::map::value_type("copy", Keywords::KEY_COPY), +std::map::value_type("pitzer", Keywords::KEY_PITZER), +std::map::value_type("sit", Keywords::KEY_SIT), +std::map::value_type("equilibrium_phase", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("solution_raw", Keywords::KEY_SOLUTION_RAW), +std::map::value_type("exchange_raw", Keywords::KEY_EXCHANGE_RAW), +std::map::value_type("surface_raw", Keywords::KEY_SURFACE_RAW), +std::map::value_type("equilibrium_phases_raw", Keywords::KEY_EQUILIBRIUM_PHASES_RAW), +std::map::value_type("kinetics_raw", Keywords::KEY_KINETICS_RAW), +std::map::value_type("solid_solutions_raw", Keywords::KEY_SOLID_SOLUTIONS_RAW), +std::map::value_type("gas_phase_raw", Keywords::KEY_GAS_PHASE_RAW), +std::map::value_type("reaction_raw", Keywords::KEY_REACTION_RAW), +std::map::value_type("mix_raw", Keywords::KEY_MIX_RAW), +std::map::value_type("reaction_temperature_raw", Keywords::KEY_REACTION_TEMPERATURE_RAW), +std::map::value_type("dump", Keywords::KEY_DUMP), +std::map::value_type("solution_modify", Keywords::KEY_SOLUTION_MODIFY), +std::map::value_type("equilibrium_phases_modify", Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY), +std::map::value_type("exchange_modify", Keywords::KEY_EXCHANGE_MODIFY), +std::map::value_type("surface_modify", Keywords::KEY_SURFACE_MODIFY), +std::map::value_type("solid_solutions_modify", Keywords::KEY_SOLID_SOLUTIONS_MODIFY), +std::map::value_type("gas_phase_modify", Keywords::KEY_GAS_PHASE_MODIFY), +std::map::value_type("kinetics_modify", Keywords::KEY_KINETICS_MODIFY), +std::map::value_type("delete", Keywords::KEY_DELETE), +std::map::value_type("run_cells", Keywords::KEY_RUN_CELLS), +std::map::value_type("reaction_modify", Keywords::KEY_REACTION_MODIFY), +std::map::value_type("reaction_temperature_modify", Keywords::KEY_REACTION_TEMPERATURE_MODIFY), +std::map::value_type("solid_solution_modify", Keywords::KEY_SOLID_SOLUTIONS_MODIFY), +std::map::value_type("reaction_pressure", Keywords::KEY_REACTION_PRESSURE), +std::map::value_type("reaction_pressures", Keywords::KEY_REACTION_PRESSURE), +std::map::value_type("reaction_pressure_raw", Keywords::KEY_REACTION_PRESSURE_RAW), +std::map::value_type("reaction_pressure_modify", Keywords::KEY_REACTION_PRESSURE_MODIFY), +std::map::value_type("solution_mix", Keywords::KEY_SOLUTION_MIX), +std::map::value_type("mix_solution", Keywords::KEY_SOLUTION_MIX), +std::map::value_type("exchange_mix", Keywords::KEY_EXCHANGE_MIX), +std::map::value_type("mix_exchange", Keywords::KEY_EXCHANGE_MIX), +std::map::value_type("gas_phase_mix", Keywords::KEY_GAS_PHASE_MIX), +std::map::value_type("mix_gas_phase", Keywords::KEY_GAS_PHASE_MIX), +std::map::value_type("kinetics_mix", Keywords::KEY_KINETICS_MIX), +std::map::value_type("mix_kinetics", Keywords::KEY_KINETICS_MIX), +std::map::value_type("equilibrium_phases_mix", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("mix_equilibrium_phases", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("equilibrium_phase_mix", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("mix_equilibrium_phase", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("solid_solutions_mix", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("mix_solid_solutions", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("solid_solution_mix", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("mix_solid_solution", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("surface_mix", Keywords::KEY_SURFACE_MIX), +std::map::value_type("mix_surface", Keywords::KEY_SURFACE_MIX) +}; +const std::map Keywords::phreeqc_keywords(temp_keywords, temp_keywords + sizeof temp_keywords / sizeof temp_keywords[0]); + +const std::map::value_type temp_keyword_names[] = { +std::map::value_type(Keywords::KEY_NONE, "UNKNOWN"), +std::map::value_type(Keywords::KEY_END, "END"), +std::map::value_type(Keywords::KEY_SOLUTION_SPECIES, "SOLUTION_SPECIES"), +std::map::value_type(Keywords::KEY_SOLUTION_MASTER_SPECIES, "SOLUTION_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_SOLUTION, "SOLUTION"), +std::map::value_type(Keywords::KEY_PHASES, "PHASES"), +std::map::value_type(Keywords::KEY_REACTION, "REACTION"), +std::map::value_type(Keywords::KEY_MIX, "MIX"), +std::map::value_type(Keywords::KEY_USE, "USE"), +std::map::value_type(Keywords::KEY_SAVE, "SAVE"), +std::map::value_type(Keywords::KEY_EXCHANGE_SPECIES, "EXCHANGE_SPECIES"), +std::map::value_type(Keywords::KEY_EXCHANGE_MASTER_SPECIES, "EXCHANGE_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_EXCHANGE, "EXCHANGE"), +std::map::value_type(Keywords::KEY_SURFACE_SPECIES, "SURFACE_SPECIES"), +std::map::value_type(Keywords::KEY_SURFACE_MASTER_SPECIES, "SURFACE_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_SURFACE, "SURFACE"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE, "REACTION_TEMPERATURE"), +std::map::value_type(Keywords::KEY_INVERSE_MODELING, "INVERSE_MODELING"), +std::map::value_type(Keywords::KEY_GAS_PHASE, "GAS_PHASE"), +std::map::value_type(Keywords::KEY_TRANSPORT, "TRANSPORT"), +std::map::value_type(Keywords::KEY_SELECTED_OUTPUT, "SELECTED_OUTPUT"), +std::map::value_type(Keywords::KEY_KNOBS, "KNOBS"), +std::map::value_type(Keywords::KEY_PRINT, "PRINT"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES, "EQUILIBRIUM_PHASES"), +std::map::value_type(Keywords::KEY_TITLE, "TITLE"), +std::map::value_type(Keywords::KEY_ADVECTION, "ADVECTION"), +std::map::value_type(Keywords::KEY_KINETICS, "KINETICS"), +std::map::value_type(Keywords::KEY_INCREMENTAL_REACTIONS, "INCREMENTAL_REACTIONS"), +std::map::value_type(Keywords::KEY_RATES, "RATES"), +std::map::value_type(Keywords::KEY_USER_PRINT, "USER_PRINT"), +std::map::value_type(Keywords::KEY_USER_PUNCH, "USER_PUNCH"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS, "SOLID_SOLUTIONS"), +std::map::value_type(Keywords::KEY_SOLUTION_SPREAD, "SOLUTION_SPREAD"), +std::map::value_type(Keywords::KEY_USER_GRAPH, "USER_GRAPH"), +std::map::value_type(Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS,"LLNL_AQUEOUS_MODEL_PARAMETERS"), +std::map::value_type(Keywords::KEY_DATABASE, "DATABASE"), +std::map::value_type(Keywords::KEY_NAMED_EXPRESSIONS, "NAMED_EXPRESSIONS"), +std::map::value_type(Keywords::KEY_ISOTOPES, "ISOTOPES"), +std::map::value_type(Keywords::KEY_CALCULATE_VALUES, "CALCULATE_VALUES"), +std::map::value_type(Keywords::KEY_ISOTOPE_RATIOS, "ISOTOPE_RATIOS"), +std::map::value_type(Keywords::KEY_ISOTOPE_ALPHAS, "ISOTOPE_ALPHAS"), +std::map::value_type(Keywords::KEY_COPY, "COPY"), +std::map::value_type(Keywords::KEY_PITZER, "PITZER"), +std::map::value_type(Keywords::KEY_SIT, "SIT"), +std::map::value_type(Keywords::KEY_SOLUTION_RAW, "SOLUTION_RAW"), +std::map::value_type(Keywords::KEY_EXCHANGE_RAW, "EXCHANGE_RAW"), +std::map::value_type(Keywords::KEY_SURFACE_RAW, "SURFACE_RAW"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES_RAW, "EQUILIBRIUM_PHASES_RAW"), +std::map::value_type(Keywords::KEY_KINETICS_RAW, "KINETICS_RAW"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS_RAW, "SOLID_SOLUTIONS_RAW"), +std::map::value_type(Keywords::KEY_GAS_PHASE_RAW, "GAS_PHASE_RAW"), +std::map::value_type(Keywords::KEY_REACTION_RAW, "REACTION_RAW"), +std::map::value_type(Keywords::KEY_MIX_RAW, "MIX_RAW"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE_RAW, "REACTION_TEMPERATURE_RAW"), +std::map::value_type(Keywords::KEY_DUMP, "DUMP"), +std::map::value_type(Keywords::KEY_SOLUTION_MODIFY, "SOLUTION_MODIFY"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY, "EQUILIBRIUM_PHASES_MODIFY"), +std::map::value_type(Keywords::KEY_EXCHANGE_MODIFY, "EXCHANGE_MODIFY"), +std::map::value_type(Keywords::KEY_SURFACE_MODIFY, "SURFACE_MODIFY"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS_MODIFY, "SOLID_SOLUTIONS_MODIFY"), +std::map::value_type(Keywords::KEY_GAS_PHASE_MODIFY, "GAS_PHASE_MODIFY"), +std::map::value_type(Keywords::KEY_KINETICS_MODIFY, "KINETICS_MODIFY"), +std::map::value_type(Keywords::KEY_DELETE, "DELETE"), +std::map::value_type(Keywords::KEY_RUN_CELLS, "RUN_CELLS"), +std::map::value_type(Keywords::KEY_REACTION_MODIFY, "REACTION_MODIFY"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE_MODIFY, "REACTION_TEMPERATURE_MODIFY"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE, "REACTION_PRESSURE"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE_RAW, "REACTION_PRESSURE_RAW"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE_MODIFY, "REACTION_PRESSURE_MODIFY"), +std::map::value_type(Keywords::KEY_SOLUTION_MIX, "SOLUTION_MIX"), +std::map::value_type(Keywords::KEY_EXCHANGE_MIX, "EXCHANGE_MIX"), +std::map::value_type(Keywords::KEY_GAS_PHASE_MIX, "GAS_PHASE_MIX"), +std::map::value_type(Keywords::KEY_KINETICS_MIX, "KINETICS_MIX"), +std::map::value_type(Keywords::KEY_PPASSEMBLAGE_MIX, "EQUILIBRIUM_PHASES_MIX"), +std::map::value_type(Keywords::KEY_SSASSEMBLAGE_MIX, "SOLID_SOLUTIONS_MIX"), +std::map::value_type(Keywords::KEY_SURFACE_MIX, "SURFACE_MIX") +}; +const std::map Keywords::phreeqc_keyword_names(temp_keyword_names, temp_keyword_names + sizeof temp_keyword_names / sizeof temp_keyword_names[0]); diff --git a/phreeqcpp/PhreeqcKeywords/Keywords.h b/phreeqcpp/PhreeqcKeywords/Keywords.h new file mode 100644 index 00000000..f812fb39 --- /dev/null +++ b/phreeqcpp/PhreeqcKeywords/Keywords.h @@ -0,0 +1,99 @@ +#ifndef _INC_KEYWORDS_H +#define _INC_KEYWORDS_H +#include +#include +class Keywords +{ +public: + enum KEYWORDS + { + KEY_NONE, + KEY_END, + KEY_SOLUTION_SPECIES, + KEY_SOLUTION_MASTER_SPECIES, + KEY_SOLUTION, + KEY_PHASES, + KEY_REACTION, + KEY_MIX, + KEY_USE, + KEY_SAVE, + KEY_EXCHANGE_SPECIES, + KEY_EXCHANGE_MASTER_SPECIES, + KEY_EXCHANGE, + KEY_SURFACE_SPECIES, + KEY_SURFACE_MASTER_SPECIES, + KEY_SURFACE, + KEY_REACTION_TEMPERATURE, + KEY_INVERSE_MODELING, + KEY_GAS_PHASE, + KEY_TRANSPORT, + KEY_SELECTED_OUTPUT, + KEY_KNOBS, + KEY_PRINT, + KEY_EQUILIBRIUM_PHASES, + KEY_TITLE, + KEY_ADVECTION, + KEY_KINETICS, + KEY_INCREMENTAL_REACTIONS, + KEY_RATES, + KEY_USER_PRINT, + KEY_USER_PUNCH, + KEY_SOLID_SOLUTIONS, + KEY_SOLUTION_SPREAD, + KEY_USER_GRAPH, + KEY_LLNL_AQUEOUS_MODEL_PARAMETERS, + KEY_DATABASE, + KEY_NAMED_EXPRESSIONS, + KEY_ISOTOPES, + KEY_CALCULATE_VALUES, + KEY_ISOTOPE_RATIOS, + KEY_ISOTOPE_ALPHAS, + KEY_COPY, + KEY_PITZER, + KEY_SIT, + KEY_SOLUTION_RAW, + KEY_EXCHANGE_RAW, + KEY_SURFACE_RAW, + KEY_EQUILIBRIUM_PHASES_RAW, + KEY_KINETICS_RAW, + KEY_SOLID_SOLUTIONS_RAW, + KEY_GAS_PHASE_RAW, + KEY_REACTION_RAW, + KEY_MIX_RAW, + KEY_REACTION_TEMPERATURE_RAW, + KEY_DUMP, + KEY_SOLUTION_MODIFY, + KEY_EQUILIBRIUM_PHASES_MODIFY, + KEY_EXCHANGE_MODIFY, + KEY_SURFACE_MODIFY, + KEY_SOLID_SOLUTIONS_MODIFY, + KEY_GAS_PHASE_MODIFY, + KEY_KINETICS_MODIFY, + KEY_DELETE, + KEY_RUN_CELLS, + KEY_REACTION_MODIFY, + KEY_REACTION_TEMPERATURE_MODIFY, + KEY_REACTION_PRESSURE, + KEY_REACTION_PRESSURE_RAW, + KEY_REACTION_PRESSURE_MODIFY, + KEY_SOLUTION_MIX, + KEY_EXCHANGE_MIX, + KEY_GAS_PHASE_MIX, + KEY_KINETICS_MIX, + KEY_PPASSEMBLAGE_MIX, + KEY_SSASSEMBLAGE_MIX, + KEY_SURFACE_MIX, + KEY_COUNT_KEYWORDS // must be last in list + }; + + Keywords(void); + ~Keywords(void); + + static KEYWORDS Keyword_search(std::string key); + static const std::string & Keyword_name_search(KEYWORDS key); + + static const std::map phreeqc_keywords; + static const std::map phreeqc_keyword_names; +}; + +#endif // _INC_KEYWORDS_H diff --git a/phreeqcpp/Pressure.cxx b/phreeqcpp/Pressure.cxx new file mode 100644 index 00000000..a5075b0d --- /dev/null +++ b/phreeqcpp/Pressure.cxx @@ -0,0 +1,417 @@ +// Pressure.cxx: implementation of the cxxPressure class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Parser.h" +#include "Pressure.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPressure::cxxPressure(PHRQ_io *io) + // + // default constructor for cxxPressure + // +: cxxNumKeyword(io) +{ + count = 0; + equalIncrements = false; +} + +cxxPressure::~cxxPressure() +{ +} +int +cxxPressure::read(CParser & parser) +{ +/* + * Reads pressure data for reaction steps + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + // Number and description set in read_reaction_pressure + + PHRQ_io::LINE_TYPE lt; + bool done = false; + for (;;) + { + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token, str; + lt = parser.check_line(str, false, true, true, true); + + if (lt == PHRQ_io::LT_EMPTY || + lt == PHRQ_io::LT_KEYWORD || + lt == PHRQ_io::LT_EOF) + { + break; + } + if (lt == PHRQ_io::LT_OPTION) + { + this->error_msg("Expected numeric value for pressures.", PHRQ_io::OT_CONTINUE); + break; + } + + if (done) + { + this->error_msg("Unknown input following equal increment definition.", PHRQ_io::OT_CONTINUE); + continue; + } + + // LT_OK + + for (;;) + { + if (done) break; + // new token + std::string token; + CParser::TOKEN_TYPE k = parser.copy_token(token, next_char); + + // need new line + if (k == CParser::TT_EMPTY) + { + break; + } + + // read a pressure + if (k == CParser::TT_DIGIT) + { + std::istringstream iss(token); + LDBLE d; + if (!(iss >> d)) + { + this->error_msg("Expected numeric value for pressures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->pressures.push_back(d); + } + continue; + } + + // non digit, must be "in" + if (k == CParser::TT_UPPER || k == CParser::TT_LOWER) + { + if (this->pressures.size() != 2) + { + this->error_msg("To define equal increments, exactly two pressures should be defined.", CONTINUE); + } + else + { + int i = parser.copy_token(token, next_char); + if (i == EMPTY) + { + error_msg("To define equal increments, define 'in n steps'.", CONTINUE); + } + else + { + std::istringstream iss(token); + if ((iss >> i) && i > 0) + { + this->equalIncrements = true; + this->count = i; + } + else + { + error_msg("Unknown input for pressure steps.", CONTINUE); + } + } + done = true; + } + if (k == CParser::TT_UNKNOWN) + { + error_msg("Unknown input for pressure steps.", CONTINUE); + } + } + } // tokens + } // lines + return lt; +} + +void +cxxPressure::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_PRESSURE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-count " << this->count << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // Temperature element and attributes + + s_oss << indent1; + s_oss << "-pressures" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->pressures.begin(); + it != this->pressures.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } +} + +void +cxxPressure::read_raw(CParser & parser, bool check) +{ + // clear steps for modify operation, if pressures are read + bool cleared_once = false; + LDBLE d; + CParser::TOKEN_TYPE k; + + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token; + int opt_save; + bool useLastLine(false); + + // Number and description set in read_reaction_pressure_raw + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool equalIncrements_defined(false); + bool count_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in REACTION_PRESSURE_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // pressures + if (!cleared_once) + { + this->pressures.clear(); + cleared_once = true; + } + while ((k = parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pressures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->pressures.push_back(d); + } + } + opt_save = 0; + useLastLine = false; + break; + + case 1: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for equalIncrements.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 2: // count + if (!(parser.get_iss() >> this->count)) + { + this->count = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for count.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + count_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Equal_increments not defined for REACTION_PRESSURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (count_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Count_temps not defined for REACTION_PRESSURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +/* ---------------------------------------------------------------------- */ +LDBLE cxxPressure:: +Pressure_for_step(int step_number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determine pressure of reaction step + */ + LDBLE p_temp; + if (this->pressures.size() == 0) + { + p_temp = 1; + } + else if (this->equalIncrements) + { + if (this->pressures.size() != 2) + { + error_msg("Number of pressures not equal to 2 for equal increments.", 0); + } + if (step_number > this->count) + { + p_temp = this->pressures[1]; + } + else + { + LDBLE denom; + denom = (this->count <= 1) ? 1 : ((LDBLE)this->count - 1); + p_temp = this->pressures[0] + ( this->pressures[1] - this->pressures[0]) * + (((LDBLE)step_number - 1)) / (denom); + } + } + else + { + if (step_number > (int) this->pressures.size()) + { + p_temp = this->pressures[this->pressures.size() - 1]; + } + else + { + p_temp = this->pressures[(size_t)step_number - 1]; + } + + } + + return (p_temp); +} +int cxxPressure:: +Get_count(void) const +{ + if (equalIncrements) + { + return this->count; + } + return (int) this->pressures.size(); +} +void +cxxPressure::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + { + ints.push_back((int) this->pressures.size()); + for (size_t i = 0; i < this->pressures.size(); i++) + { + doubles.push_back(pressures[i]); + } + } + ints.push_back(this->count); + ints.push_back(this->equalIncrements ? 1 : 0); + +} + +void +cxxPressure::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + { + int count = ints[ii++]; + this->pressures.clear(); + for (int i = 0; i < count; i++) + { + this->pressures.push_back(doubles[dd++]); + } + } + this->count = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("pressures"), //0 + std::vector< std::string >::value_type("equal_increments"), //1 + std::vector< std::string >::value_type("count") //2 +}; +const std::vector< std::string > cxxPressure::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Pressure.h b/phreeqcpp/Pressure.h new file mode 100644 index 00000000..377f2039 --- /dev/null +++ b/phreeqcpp/Pressure.h @@ -0,0 +1,43 @@ +#if !defined(PRESSURE_H_INCLUDED) +#define PRESSURE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +class Dictionary; + +class cxxPressure:public cxxNumKeyword +{ + + public: + cxxPressure(PHRQ_io *io=NULL); + ~cxxPressure(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + int read(CParser & parser); + void read_raw(CParser & parser, bool check = false); + LDBLE Pressure_for_step(int step_number); + std::vector & Get_pressures(void) {return pressures;} + const std::vector & Get_pressures(void)const {return pressures;} + int Get_count(void) const; + void Set_count(int i) {count = i;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < LDBLE >pressures; + int count; + bool equalIncrements; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(PRESSURE_H_INCLUDED) diff --git a/phreeqcpp/Reaction.cxx b/phreeqcpp/Reaction.cxx new file mode 100644 index 00000000..511e3719 --- /dev/null +++ b/phreeqcpp/Reaction.cxx @@ -0,0 +1,284 @@ +// Reaction.cxx: implementation of the cxxReaction class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Reaction.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxReaction::cxxReaction(PHRQ_io *io) + // + // default constructor for cxxReaction + // +: cxxNumKeyword(io) +{ + this->Set_units("Mol"); + countSteps = 0; + equalIncrements = false; + reactantList.type = cxxNameDouble::ND_NAME_COEF; + elementList.type = cxxNameDouble::ND_ELT_MOLES; +} +cxxReaction::~cxxReaction() +{ +} + +void +cxxReaction::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Reaction element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-reactant_list" << "\n"; + this->reactantList.dump_raw(s_oss, indent + 2); + + s_oss << indent1; + s_oss << "-steps" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->steps.begin(); + it != this->steps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1; + s_oss << "-count_steps " << this->countSteps << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + s_oss << indent1; + s_oss << "-units " << this->units << "\n"; + + s_oss << indent1 << "# REACTION workspace variables #\n"; + s_oss << indent1; + s_oss << "-element_list" << "\n"; + this->elementList.dump_raw(s_oss, indent + 2); +} + +void +cxxReaction::read_raw(CParser & parser, const bool check) +{ + + int j; + LDBLE d; + CParser::TOKEN_TYPE k; + + // clear steps for modify operation, if steps are read + bool cleared_once = false; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read irrev number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool units_defined(false); + bool equalIncrements_defined(false); + bool countSteps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in IRREV_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // units + j = parser.copy_token(token, next_char); + if (j == CParser::TT_EMPTY) + break; + this->Set_units(token.c_str()); + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + units_defined = true; + break; + + case 1: // reactant_list + if (this->reactantList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg("Expected reactant formula and coefficient.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + useLastLine = false; + break; + + case 2: // element_list + if (this->elementList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg("Expected element formula and coefficient.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + useLastLine = false; + break; + + case 3: // steps + if (!cleared_once) + { + this->steps.clear(); + cleared_once = true; + } + while ((k = + parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for steps.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->steps.push_back(d); + } + } + opt_save = 3; + useLastLine = false; + break; + + case 4: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 5: // countSteps + if (!(parser.get_iss() >> this->countSteps)) + { + this->countSteps = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for countSteps.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + countSteps_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (units_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Units not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Equal_increments not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (countSteps_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Count_steps not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +int cxxReaction:: +Get_reaction_steps(void) const +{ + if (equalIncrements) + { + return this->countSteps; + } + return (int) this->steps.size(); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("units"), //0 + std::vector< std::string >::value_type("reactant_list"), //1 + std::vector< std::string >::value_type("element_list"), //2 + std::vector< std::string >::value_type("steps"), //3 + std::vector< std::string >::value_type("equal_increments"), //4 + std::vector< std::string >::value_type("count_steps") //5 +}; +const std::vector< std::string > cxxReaction::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Reaction.h b/phreeqcpp/Reaction.h new file mode 100644 index 00000000..d03cef64 --- /dev/null +++ b/phreeqcpp/Reaction.h @@ -0,0 +1,57 @@ +#if !defined(REACTION_H_INCLUDED) +#define REACTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "NameDouble.h" + +class cxxReaction:public cxxNumKeyword +{ + + public: + cxxReaction(PHRQ_io *io = NULL); + ~cxxReaction(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check=true); + const cxxNameDouble &Get_elementList(void) const {return this->elementList;} + void Set_elementList(cxxNameDouble nd) {this->elementList = nd;} + cxxNameDouble &Get_reactantList(void) {return this->reactantList;} + const cxxNameDouble &Get_reactantList(void)const {return this->reactantList;} + std::vector < LDBLE > &Get_steps(void) {return this->steps;} + const std::vector < LDBLE > &Get_steps(void)const {return this->steps;} + void Set_steps(std::vector &v) {steps = v;} + int Get_reaction_steps(void) const; + int Get_countSteps(void) const {return this->countSteps;} + void Set_countSteps(int i) {countSteps = i;} + bool Get_equalIncrements(void) const {return this->equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + const std::string &Get_units(void) const {return this->units;} + + void Set_units(const char * s) + { + if (s != NULL) + this->units = std::string(s); + else + this->units.clear(); + } + +protected: + cxxNameDouble reactantList; + cxxNameDouble elementList; + std::vector < LDBLE >steps; + int countSteps; + bool equalIncrements; + std::string units; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(REACTION_H_INCLUDED) diff --git a/phreeqcpp/ReadClass.cxx b/phreeqcpp/ReadClass.cxx new file mode 100644 index 00000000..937d6575 --- /dev/null +++ b/phreeqcpp/ReadClass.cxx @@ -0,0 +1,1150 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include +#include + +#include "Utils.h" +#include "Phreeqc.h" +#include "Parser.h" +#include "Solution.h" +#include "Exchange.h" +#include "Surface.h" +#include "PPassemblage.h" +#include "cxxKinetics.h" +#include "SSassemblage.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "cxxMix.h" +#include "Temperature.h" +#include "dumper.h" +#include "runner.h" +#include "cxxMix.h" +#include "Surface.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_dump(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DUMP data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + dump_info.Read(parser); + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_delete(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DELETE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + //assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + delete_info.Read(parser); + + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_run_cells(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DELETE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + runner r(parser, phrq_io); + run_info = r; + + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +streamify_to_next_keyword(std::istringstream & lines) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads to next keyword or eof + * + * Returns: + * OPTION_KEYWORD + * OPTION_EOF + * + */ + // Handle echo + int save_echo_input = pr.echo_input; + pr.echo_input = FALSE; + + std::string accumulate(line); + accumulate.append("\n"); + int j; + for (;;) + { + j = check_line("Streamify", FALSE, TRUE, TRUE, FALSE); + if (j == EOF) + { /* end of file */ + break; + } + else if (j == KEYWORD) + { /* keyword */ + break; + } + else + { + accumulate.append(line); + accumulate.append("\n"); + } + } + + lines.str(accumulate); + pr.echo_input = save_echo_input; + if (j == EOF) return (OPTION_EOF); + if (j == KEYWORD) return (OPTION_KEYWORD); + + + return (OPTION_ERROR); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_entities(void) +/* ---------------------------------------------------------------------- */ +{ + if (!dump_info.Get_on() || pr.dump == FALSE) + { + return(OK); + } + dump_info.Set_on(false); + if (!dump_info.Get_bool_any()) + { + return(OK); + } + + if (this->phrq_io) + { + std::ios_base::openmode mode = std::ios_base::out; + if (dump_info.Get_append()) + { + mode = std::ios_base::app; + } + if (this->phrq_io->dump_open(dump_info.Get_file_name().c_str(), mode)) + { + dump_ostream(*this->phrq_io->Get_dump_ostream()); + this->phrq_io->dump_close(); + } + else + { + error_string = sformatf( "Unable to open dump file \"%s\"", dump_info.Get_file_name().c_str()); + error_msg(error_string, STOP); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +delete_entities(void) +/* ---------------------------------------------------------------------- */ +{ + if (!delete_info.Get_solution().Get_defined() && + !delete_info.Get_pp_assemblage().Get_defined() && + !delete_info.Get_exchange().Get_defined() && + !delete_info.Get_surface().Get_defined() && + !delete_info.Get_ss_assemblage().Get_defined() && + !delete_info.Get_gas_phase().Get_defined() && + !delete_info.Get_kinetics().Get_defined() && + !delete_info.Get_mix().Get_defined() && + !delete_info.Get_reaction().Get_defined() && + !delete_info.Get_temperature().Get_defined() && + !delete_info.Get_pressure().Get_defined()) + { + return(OK); + } + + // solutions + if (delete_info.Get_solution().Get_defined()) + { + if (delete_info.Get_solution().Get_numbers().size() == 0) + { + Rxn_solution_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_solution().Get_numbers().begin(); it != delete_info.Get_solution().Get_numbers().end(); it++) + { + Rxn_solution_map.erase(*it); + } + } + } + + // pp_assemblages + if (delete_info.Get_pp_assemblage().Get_defined()) + { + if (delete_info.Get_pp_assemblage().Get_numbers().size() == 0) + { + Rxn_pp_assemblage_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_pp_assemblage().Get_numbers().begin(); it != delete_info.Get_pp_assemblage().Get_numbers().end(); it++) + { + Rxn_pp_assemblage_map.erase(*it); + } + } + } + // exchangers + if (delete_info.Get_exchange().Get_defined()) + { + if (delete_info.Get_exchange().Get_numbers().size() == 0) + { + Rxn_exchange_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_exchange().Get_numbers().begin(); it != delete_info.Get_exchange().Get_numbers().end(); it++) + { + Rxn_exchange_map.erase(*it); + } + } + } + // surfaces + if (delete_info.Get_surface().Get_defined()) + { + if (delete_info.Get_surface().Get_numbers().size() == 0) + { + Rxn_surface_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_surface().Get_numbers().begin(); it != delete_info.Get_surface().Get_numbers().end(); it++) + { + Rxn_surface_map.erase(*it); + } + } + } + // ss_assemblages + if (delete_info.Get_ss_assemblage().Get_defined()) + { + if (delete_info.Get_ss_assemblage().Get_numbers().size() == 0) + { + Rxn_ss_assemblage_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_ss_assemblage().Get_numbers().begin(); it != delete_info.Get_ss_assemblage().Get_numbers().end(); it++) + { + Rxn_ss_assemblage_map.erase(*it); + } + } + } + // gas_phases + if (delete_info.Get_gas_phase().Get_defined()) + { + if (delete_info.Get_gas_phase().Get_numbers().size() == 0) + { + Rxn_gas_phase_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_gas_phase().Get_numbers().begin(); it != delete_info.Get_gas_phase().Get_numbers().end(); it++) + { + Rxn_gas_phase_map.erase(*it); + } + } + } + // kinetics + if (delete_info.Get_kinetics().Get_defined()) + { + if (delete_info.Get_kinetics().Get_numbers().size() == 0) + { + Rxn_kinetics_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_kinetics().Get_numbers().begin(); it != delete_info.Get_kinetics().Get_numbers().end(); it++) + { + Rxn_kinetics_map.erase(*it); + } + } + } + // mixes + if (delete_info.Get_mix().Get_defined()) + { + if (delete_info.Get_mix().Get_numbers().size() == 0) + { + Rxn_mix_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_mix().Get_numbers().begin(); it != delete_info.Get_mix().Get_numbers().end(); it++) + { + Rxn_mix_map.erase(*it); + } + } + } + // reactions + if (delete_info.Get_reaction().Get_defined()) + { + if (delete_info.Get_reaction().Get_numbers().size() == 0) + { + Rxn_reaction_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_reaction().Get_numbers().begin(); it != delete_info.Get_reaction().Get_numbers().end(); it++) + { + Rxn_reaction_map.erase(*it); + } + } + } + // temperatures + if (delete_info.Get_temperature().Get_defined()) + { + if (delete_info.Get_temperature().Get_numbers().size() == 0) + { + Rxn_temperature_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_temperature().Get_numbers().begin(); it != delete_info.Get_temperature().Get_numbers().end(); it++) + { + Rxn_temperature_map.erase(*it); + } + } + } + // pressures + if (delete_info.Get_pressure().Get_defined()) + { + if (delete_info.Get_pressure().Get_numbers().size() == 0) + { + Rxn_pressure_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_pressure().Get_numbers().begin(); it != delete_info.Get_pressure().Get_numbers().end(); it++) + { + Rxn_pressure_map.erase(*it); + } + } + } + // Turn off delete until next read + delete_info.SetAll(false); + return (OK); +} +#ifdef USE_OPTIMIZED_BUT_NOT_MUCH +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_as_cells(void) +/* ---------------------------------------------------------------------- */ +{ + class save save_data; + LDBLE kin_time; + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + + state = REACTION; + if (run_info.Get_cells().Get_numbers().size() == 0 || + !(run_info.Get_cells().Get_defined())) return(OK); + + // running cells + run_info.Set_run_cells(true); + + dup_print("Beginning of run as cells.", TRUE); + LDBLE initial_total_time_save; + if (run_info.Get_start_time() != NA) + { + initial_total_time_save = run_info.Get_start_time(); + } + else + { + initial_total_time_save = initial_total_time; + } + + std::set < int >::iterator it = run_info.Get_cells().Get_numbers().begin(); + + for ( ; it != run_info.Get_cells().Get_numbers().end(); it++) + { + int i = *it; + if (i < 0) continue; + initial_total_time = initial_total_time_save; + cxxKinetics *kinetics_ptr = NULL; +/* + * Run reaction step + */ + /* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (cxxReaction *rxn_ptr = Utilities::Rxn_find(Rxn_reaction_map, i)) + { + int count = rxn_ptr->Get_reaction_steps(); + if (count > count_steps) + count_steps = count; + } + if (cxxKinetics *rxn_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i)) + { + kinetics_ptr = rxn_ptr; + if (rxn_ptr->Get_reaction_steps() > count_steps) + count_steps = rxn_ptr->Get_reaction_steps(); + } + if (cxxTemperature *rxn_ptr = Utilities::Rxn_find(Rxn_temperature_map, i)) + { + int count = rxn_ptr->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (cxxPressure *rxn_ptr = Utilities::Rxn_find(Rxn_pressure_map, i)) + { + int count = rxn_ptr->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + count_total_steps = count_steps; + if (count_steps > 1) + { + state = ADVECTION; + set_advection(i, TRUE, TRUE, i); + /* + * save data for saving solutions + */ + memcpy(&save_data, &save, sizeof(class save)); + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (use.Get_kinetics_in() == TRUE) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + if (incremental_reactions == FALSE) + { + /* not incremental reactions */ + kin_time = reaction_step * run_info.Get_time_step() / ((LDBLE) count_steps); + } + else + { + /* incremental reactions */ + kin_time = run_info.Get_time_step() / ((LDBLE) count_steps); + } + } + // runner kin_time not defined + else + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } + /* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + punch_all(); + print_all(); + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } + /* + * save end of reaction + */ + memcpy(&save, &save_data, sizeof(class save)); + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + } + else + // only 1 step, no worries about incremental reactions + { + state = TRANSPORT; + + rate_sim_time_start = 0; + rate_sim_time = 0; + reaction_step = 1; + + sprintf(token, "Reaction step %d.", reaction_step); + + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (kinetics_ptr) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + kin_time = run_info.Get_time_step(); + } + // runner kin_time not defined + else + { + kin_time = kinetics_ptr->Get_steps()[0]; + } + } + /* + * Run reaction step + */ + use_mix = TRUE; + run_reactions(i, kin_time, use_mix, 1.0); + rate_sim_time = kin_time; + saver(); + } + } + initial_total_time += rate_sim_time; + run_info.Get_cells().Set_defined(false); + // not running cells + run_info.Set_run_cells(false); + return (OK); +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_as_cells(void) +/* ---------------------------------------------------------------------- */ +{ + class save save_data; + LDBLE kin_time; + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + + state = REACTION; + if (run_info.Get_cells().Get_numbers().size() == 0 || + !(run_info.Get_cells().Get_defined())) return(OK); + + // running cells + run_info.Set_run_cells(true); + + dup_print("Beginning of run as cells.", TRUE); + LDBLE initial_total_time_save; + if (run_info.Get_start_time() != NA) + { + initial_total_time_save = run_info.Get_start_time(); + } + else + { + initial_total_time_save = initial_total_time; + } + + std::set < int >::iterator it = run_info.Get_cells().Get_numbers().begin(); + + for ( ; it != run_info.Get_cells().Get_numbers().end(); it++) + { + int i = *it; + if (i < 0) continue; + if (Utilities::Rxn_find(Rxn_solution_map, i) == NULL + && Utilities::Rxn_find(Rxn_mix_map, i) == NULL) + continue; + initial_total_time = initial_total_time_save; + set_advection(i, TRUE, TRUE, i); +/* + * Run reaction step + */ + /* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (!this->run_cells_one_step) + { + if (use.Get_reaction_in() == TRUE && use.Get_reaction_ptr() != NULL) + { + int count = use.Get_reaction_ptr()->Get_reaction_steps(); + if (count > count_steps) + count_steps = count; + } + if (use.Get_kinetics_in() == TRUE && use.Get_kinetics_ptr() != NULL) + { + if (use.Get_kinetics_ptr()->Get_reaction_steps() > count_steps) + count_steps = use.Get_kinetics_ptr()->Get_reaction_steps(); + } + if (use.Get_temperature_in() == TRUE && use.Get_temperature_ptr() != NULL) + { + int count = use.Get_temperature_ptr()->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (use.Get_pressure_in() == TRUE && use.Get_pressure_ptr() != NULL) + { + int count = use.Get_pressure_ptr()->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + } + count_total_steps = count_steps; + /* + * save data for saving solutions + */ + // memcpy(&save_data, &save, sizeof(class save)); + save_data = save; + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (use.Get_kinetics_in() == TRUE) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + if (incremental_reactions == FALSE) + { + /* not incremental reactions */ + kin_time = reaction_step * run_info.Get_time_step() / ((LDBLE) count_steps); + } + else + { + /* incremental reactions */ + kin_time = run_info.Get_time_step() / ((LDBLE) count_steps); + } + } + // runner kin_time not defined + else + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } + /* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + if (state != ADVECTION) + { + punch_all(); + print_all(); + } + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } + /* + * save end of reaction + */ + // memcpy(&save, &save_data, sizeof(class save)); + save = save_data; + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + } + initial_total_time += rate_sim_time; + run_info.Get_cells().Set_defined(false); + // not running cells + run_info.Set_run_cells(false); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_ostream(std::ostream& os) +/* ---------------------------------------------------------------------- */ +{ + // solutions + if (dump_info.Get_bool_solution()) + { + if (dump_info.Get_solution().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_solution_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_solution().begin(); it != dump_info.Get_solution().end(); it++) + { + cxxSolution *p = Utilities::Rxn_find(Rxn_solution_map, *it); + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // pp_assemblages + if (dump_info.Get_bool_pp_assemblage()) + { + if (dump_info.Get_pp_assemblage().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_pp_assemblage_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_pp_assemblage().begin(); it != dump_info.Get_pp_assemblage().end(); it++) + { + cxxPPassemblage *p = Utilities::Rxn_find(Rxn_pp_assemblage_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // exchanges + if (dump_info.Get_bool_exchange()) + { + if (dump_info.Get_exchange().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_exchange_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_exchange().begin(); it != dump_info.Get_exchange().end(); it++) + { + cxxExchange *p = Utilities::Rxn_find(Rxn_exchange_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // surfaces + if (dump_info.Get_bool_surface()) + { + if (dump_info.Get_surface().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_surface_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_surface().begin(); it != dump_info.Get_surface().end(); it++) + { + cxxSurface *p = Utilities::Rxn_find(Rxn_surface_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // ss_assemblages + if (dump_info.Get_bool_ss_assemblage()) + { + if (dump_info.Get_ss_assemblage().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_ss_assemblage_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_ss_assemblage().begin(); it != dump_info.Get_ss_assemblage().end(); it++) + { + cxxSSassemblage *p = Utilities::Rxn_find(Rxn_ss_assemblage_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // gas_phases + if (dump_info.Get_bool_gas_phase()) + { + if (dump_info.Get_gas_phase().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_gas_phase_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_gas_phase().begin(); it != dump_info.Get_gas_phase().end(); it++) + { + cxxGasPhase *p = Utilities::Rxn_find(Rxn_gas_phase_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // kinetics + if (dump_info.Get_bool_kinetics()) + { + if (dump_info.Get_kinetics().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_kinetics_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_kinetics().begin(); it != dump_info.Get_kinetics().end(); it++) + { + cxxKinetics *p = Utilities::Rxn_find(Rxn_kinetics_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // mix + if (dump_info.Get_bool_mix()) + { + if (dump_info.Get_mix().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_mix_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_mix().begin(); it != dump_info.Get_mix().end(); it++) + { + cxxMix *p = Utilities::Rxn_find(Rxn_mix_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // reaction + if (dump_info.Get_bool_reaction()) + { + if (dump_info.Get_reaction().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_reaction_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_reaction().begin(); it != dump_info.Get_reaction().end(); it++) + { + cxxReaction *p = Utilities::Rxn_find(Rxn_reaction_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // temperature + if (dump_info.Get_bool_temperature()) + { + if (dump_info.Get_temperature().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_temperature_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_temperature().begin(); it != dump_info.Get_temperature().end(); it++) + { + cxxTemperature *p = Utilities::Rxn_find(Rxn_temperature_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // pressure + if (dump_info.Get_bool_pressure()) + { + if (dump_info.Get_pressure().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_pressure_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_pressure().begin(); it != dump_info.Get_pressure().end(); it++) + { + cxxPressure *p = Utilities::Rxn_find(Rxn_pressure_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // Turn off any reaction calculation + os << "USE mix none" << "\n"; + os << "USE reaction none" << "\n"; + os << "USE reaction_temperature none" << "\n"; + os << "USE reaction_pressure none" << "\n"; + + // Turn off dump until next read + dump_info.SetAll(false); +} +#if defined MULTICHART +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_graph_handler(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads USER_GRAPH_DATA_BLOCK data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + + //For testing, need to read line to get started + std::vector < std::string > vopts; + std::istream::pos_type next_char; + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + assert(!reading_database()); + + bool success = chart_handler.Read(this, parser); + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +#endif diff --git a/phreeqcpp/SS.cxx b/phreeqcpp/SS.cxx new file mode 100644 index 00000000..bee411af --- /dev/null +++ b/phreeqcpp/SS.cxx @@ -0,0 +1,609 @@ +// SSassemblageSS.cxx: implementation of the cxxSS class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Phreeqc.h" +#include "Utils.h" // define first +#include "SS.h" +#include "Dictionary.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSS::cxxSS(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxSS + // +{ + + total_moles = 0; + dn = 0; + a0 = 0; + a1 = 0; + ag0 = 0; + ag1 = 0; + ss_in = false; + miscibility = false; + spinodal = false; + tk = 298.15; + xb1 = 0; + xb2 = 0; + input_case = SS_PARM_NONE; + for (int i = 0; i < 4; i++) + { + p.push_back(0); + } +} +cxxSS::~cxxSS() +{ +} + +void +cxxSS::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + s_oss << indent0 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + s_oss << indent0 << "-component " << this->ss_comps[i].Get_name() << "\n"; + this->ss_comps[i].dump_raw(s_oss, indent + 1); + } + + s_oss << indent0 << "# SOLID_SOLUTION_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent0 << "-tk " << this->tk << "\n"; + s_oss << indent0 << "-input_case " << this->input_case << "\n"; + s_oss << indent0 << "-p " << + p[0] << "\t" << + p[1] << "\t" << + p[2] << "\t" << + p[3] << "\n"; + + s_oss << indent0 << "# solid solution workspace variables #\n"; + s_oss << indent0 << "-ag0 " << this->ag0 << "\n"; + s_oss << indent0 << "-ag1 " << this->ag1 << "\n"; + s_oss << indent0 << "-a0 " << this->a0 << "\n"; + s_oss << indent0 << "-a1 " << this->a1 << "\n"; + s_oss << indent0 << "-xb1 " << this->xb1 << "\n"; + s_oss << indent0 << "-xb2 " << this->xb2 << "\n"; + s_oss << indent0 << "-miscibility " << this->miscibility << "\n"; + s_oss << indent0 << "-spinodal " << this->spinodal << "\n"; + s_oss << indent0 << "-ss_in " << this->ss_in << "\n"; + s_oss << indent0 << "-total_moles " << this->total_moles << "\n"; + s_oss << indent0 << "-dn " << this->dn << "\n"; + s_oss << indent0 << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 1); +} + +void +cxxSS::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool a0_defined(false); + bool a1_defined(false); + bool ag0_defined(false); + bool ag1_defined(false); + bool miscibility_defined(false); + bool xb1_defined(false); + bool xb2_defined(false); + bool useLastLine = false; + + for (size_t i = this->Get_p().size(); i < 4; i++) + { + this->p.push_back(0.0); + } + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, false); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name not used + parser.warning_msg("-ss_name not used, defined in -solid_solution"); + opt_save = CParser::OPT_DEFAULT; + break; + + case 1: // total_moles + if (!(parser.get_iss() >> this->total_moles)) + { + this->total_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_moles.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 2: // a0 + if (!(parser.get_iss() >> this->a0)) + { + this->a0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for a0.", + PHRQ_io::OT_CONTINUE); + } + a0_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 3: // a1 + if (!(parser.get_iss() >> this->a1)) + { + this->a1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for a1.", + PHRQ_io::OT_CONTINUE); + } + a1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // components + case 12: // component + { + if (!(parser.get_iss() >> str)) + { + this->name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSScomp temp_comp(io); + temp_comp.Set_name(str); + cxxSScomp * comp_ptr = this->Find(str.c_str()); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->ss_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->ss_comps[j].Get_name().c_str(), str.c_str()) == 0) + { + this->ss_comps[j] = temp_comp; + } + } + } + else + { + this->ss_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + + opt_save = CParser::OPT_DEFAULT; + break; + + case 5: // miscibility + if (!(parser.get_iss() >> this->miscibility)) + { + this->miscibility = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for miscibility.", + PHRQ_io::OT_CONTINUE); + } + miscibility_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 6: // spinodal + if (!(parser.get_iss() >> this->spinodal)) + { + this->spinodal = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for spinodal.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // tk + if (!(parser.get_iss() >> this->tk)) + { + this->tk = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for tk.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // xb1 + if (!(parser.get_iss() >> this->xb1)) + { + this->xb1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for xb1.", + PHRQ_io::OT_CONTINUE); + } + xb1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // xb2 + if (!(parser.get_iss() >> this->xb2)) + { + this->xb2 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for xb2.", + PHRQ_io::OT_CONTINUE); + } + xb2_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 10: // ag0 + if (!(parser.get_iss() >> this->ag0)) + { + this->ag0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ag0.", + PHRQ_io::OT_CONTINUE); + } + ag0_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ag1 + if (!(parser.get_iss() >> this->ag1)) + { + this->ag1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ag1.", + PHRQ_io::OT_CONTINUE); + } + ag1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 13: // input_case + { + int i; + if (!(parser.get_iss() >> i)) + { + this->input_case = cxxSS::SS_PARM_NONE; + parser.incr_input_error(); + parser.error_msg("Expected integer value for parameter type.", PHRQ_io::OT_CONTINUE); + } + else + { + this->input_case = (cxxSS::SS_PARAMETER_TYPE) i; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + case 14: // p + { + this->p.clear(); + this->p.assign(4, 0.0); + for (int i = 0; i < 4; i++) + { + if (!(parser.get_iss() >> this->p[i])) + parser.error_msg("Expected 4 parameters."); + } + } + break; + + case 15: // ss_in + { + if (!(parser.get_iss() >> this->ss_in)) + { + this->ss_in = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for ss_in.", + PHRQ_io::OT_CONTINUE); + } + } + break; + case 16: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg + ("Expected element name and molality for cxxSS totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 16; + break; + case 17: // dn + if (!(parser.get_iss() >> this->dn)) + { + this->dn = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dn.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (a0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("A0 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (a1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("A1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (ag0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ag0 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (ag1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ag1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (miscibility_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Miscibility not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (xb1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Xb1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (xb2_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Xb2 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSS::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + class phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(ss_comps[i].Get_name().c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, ss_comps[i].Get_moles()); + } + else + { + assert(false); + } + } + return; +} +void +cxxSS::add(const cxxSS & addee_in, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee_in.name.size() == 0) + return; + cxxSS addee = addee_in; + for (size_t j = 0; j < addee.Get_ss_comps().size(); j++) + { + size_t i; + for (i = 0; i < this->ss_comps.size(); i++) + { + //look for component in this + if (Utilities::strcmp_nocase(this->ss_comps[i].Get_name().c_str(), + addee.Get_ss_comps()[j].Get_name().c_str()) == 0) + { + LDBLE d = this->ss_comps[i].Get_initial_moles() + + addee.Get_ss_comps()[j].Get_initial_moles() * extensive; + this->ss_comps[i].Set_initial_moles(d); + d = this->ss_comps[i].Get_moles() + + addee.Get_ss_comps()[j].Get_moles() * extensive; + this->ss_comps[i].Set_moles(d); + d = this->ss_comps[i].Get_init_moles() + + addee.Get_ss_comps()[j].Get_init_moles() * extensive; + this->ss_comps[i].Set_init_moles(d); + d = this->ss_comps[i].Get_delta() + + addee.Get_ss_comps()[j].Get_delta() * extensive; + this->ss_comps[i].Set_delta(d); + break; + } + } + if (i == this->ss_comps.size()) + { + cxxSScomp comp = addee.Get_ss_comps()[j]; + comp.multiply(extensive); + this->Get_ss_comps().push_back(comp); + } + } +} +void +cxxSS::multiply(LDBLE extensive) +{ + size_t i; + for (i = 0; i < this->ss_comps.size(); i++) + { + LDBLE d = this->ss_comps[i].Get_initial_moles() * extensive; + this->ss_comps[i].Set_initial_moles(d); + d = this->ss_comps[i].Get_moles() * extensive; + this->ss_comps[i].Set_moles(d); + d = this->ss_comps[i].Get_init_moles() * extensive; + this->ss_comps[i].Set_init_moles(d); + d = this->ss_comps[i].Get_delta() * extensive; + this->ss_comps[i].Set_delta(d); + } +} +cxxSScomp * +cxxSS::Find(const char * comp_name) +{ + + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + if (ss_comps[i].Get_name() == comp_name) + return &(ss_comps[i]); + } + return NULL; +} +void +cxxSS::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->ag0); + doubles.push_back(this->ag1); + { + ints.push_back((int) ss_comps.size()); + for (size_t i = 0; i < ss_comps.size(); i++) + { + ss_comps[i].Serialize(dictionary, ints, doubles); + } + } + doubles.push_back(this->a0); + doubles.push_back(this->a1); + ints.push_back(this->miscibility ? 1 : 0); + ints.push_back(this->spinodal ? 1 : 0); + doubles.push_back(this->tk); + doubles.push_back(this->xb1); + doubles.push_back(this->xb2); + ints.push_back((int) this->input_case); + { + ints.push_back((int) p.size()); + for (size_t i = 0; i < p.size(); i++) + { + doubles.push_back(p[i]); + } + } + doubles.push_back(this->total_moles); + doubles.push_back(this->dn); + ints.push_back(this->ss_in ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + +} + +void +cxxSS::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->ag0 = doubles[dd++]; + this->ag1 = doubles[dd++]; + { + int count = ints[ii++]; + this->ss_comps.clear(); + for (int i = 0; i < count; i++) + { + cxxSScomp ssc(this->io); + ssc.Deserialize(dictionary, ints, doubles, ii, dd); + this->ss_comps.push_back(ssc); + } + } + this->a0 = doubles[dd++]; + this->a1 = doubles[dd++]; + this->miscibility = (ints[ii++] != 0); + this->spinodal = (ints[ii++] != 0); + this->tk = doubles[dd++]; + this->xb1 = doubles[dd++]; + this->xb2 = doubles[dd++]; + this->input_case = (SS_PARAMETER_TYPE) ints[ii++]; + { + int count = ints[ii++]; + this->p.clear(); + for (int i = 0; i < count; i++) + { + p.push_back(doubles[dd++]); + } + } + this->total_moles = doubles[dd++]; + this->dn = doubles[dd++]; + this->ss_in = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("ss_name"), // 0 + std::vector< std::string >::value_type("total_moles"), // 1 + std::vector< std::string >::value_type("a0"), // 2 + std::vector< std::string >::value_type("a1"), // 3 + std::vector< std::string >::value_type("components"), // 4 + std::vector< std::string >::value_type("miscibility"), // 5 + std::vector< std::string >::value_type("spinodal"), // 6 + std::vector< std::string >::value_type("tk"), // 7 + std::vector< std::string >::value_type("xb1"), // 8 + std::vector< std::string >::value_type("xb2"), // 9 + std::vector< std::string >::value_type("ag0"), // 10 + std::vector< std::string >::value_type("ag1"), // 11 + std::vector< std::string >::value_type("component"), // 12 + std::vector< std::string >::value_type("input_case"), //13 + std::vector< std::string >::value_type("p"), //14 + std::vector< std::string >::value_type("ss_in"), //15 + std::vector< std::string >::value_type("totals"), //16 + std::vector< std::string >::value_type("dn") //17 +}; +const std::vector< std::string > cxxSS::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SS.h b/phreeqcpp/SS.h new file mode 100644 index 00000000..d5e5a30d --- /dev/null +++ b/phreeqcpp/SS.h @@ -0,0 +1,128 @@ +#if !defined(SSASSEMBLAGESS_H_INCLUDED) +#define SSASSEMBLAGESS_H_INCLUDED + +#include // assert +#include +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NameDouble.h" +#include "SScomp.h" + + +class cxxSS: public PHRQ_base +{ + + public: + cxxSS(PHRQ_io *io=NULL); + virtual ~cxxSS(); + + enum SS_PARAMETER_TYPE + { + SS_PARM_NONE = -1, + SS_PARM_A0_A1 = 0, + SS_PARM_GAMMAS = 1, + SS_PARM_DIST_COEF = 2, + SS_PARM_MISCIBILITY = 3, + SS_PARM_SPINODAL = 4, + SS_PARM_CRITICAL = 5, + SS_PARM_ALYOTROPIC = 6, + SS_PARM_DIM_GUGG = 7, + SS_PARM_WALDBAUM = 8, + SS_PARM_MARGULES = 9 + }; + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const + { + return this->name; + } + void Set_name (const std::string & s) {this->name = s;} + void Set_name (const char * s) + { + if (s != NULL) + this->name = std::string(s); + else + this->name.clear(); + } + + void totalize(Phreeqc * phreeqc_ptr); + const cxxNameDouble & Get_totals() const + { + return (this->totals); + }; + + cxxSScomp * Find(const char * comp_name); + + void add(const cxxSS & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + LDBLE Get_total_moles(void) const {return total_moles;} + void Set_total_moles(LDBLE t) {total_moles = t;} + + LDBLE Get_dn(void) const {return (this->dn);} + void Set_dn(LDBLE t) {this->dn = t;} + + LDBLE Get_a0() const {return (this->a0);} + void Set_a0(LDBLE t) {a0 = t;} + LDBLE Get_a1() const {return (this->a1);} + void Set_a1(LDBLE t) {a1 = t;} + LDBLE Get_ag0() const {return (this->ag0);} + void Set_ag0(LDBLE t) {ag0 = t;} + LDBLE Get_ag1() const {return (this->ag1);} + void Set_ag1(LDBLE t) {ag1 = t;} + bool Get_ss_in() const {return (this->ss_in);} + void Set_ss_in(bool t) {ss_in = t;} + bool Get_miscibility() const {return (this->miscibility);} + void Set_miscibility(bool t) {miscibility = t;} + bool Get_spinodal() const {return (this->spinodal);} + void Set_spinodal(bool t) {spinodal = t;} + LDBLE Get_xb1() const {return (this->xb1);} + void Set_xb1(LDBLE t) {xb1 = t;} + LDBLE Get_xb2() const {return (this->xb2);} + void Set_xb2(LDBLE t) {xb2 = t;} + std::vector & Get_ss_comps(void) {return ss_comps;} + const std::vector & Get_ss_comps(void)const {return ss_comps;} + void Set_ss_comps(const std::vector & comps) {ss_comps = comps;} + + LDBLE Get_tk(void)const {return this->tk;} + void Set_tk(LDBLE t) {this->tk = t;} + SS_PARAMETER_TYPE Get_input_case(void)const {return this->input_case;} + void Set_input_case(SS_PARAMETER_TYPE t) {this->input_case = t;} + std::vector & Get_p(void) {return this->p;} + const std::vector & Get_p(void)const {return this->p;} + void Set_p(const std::vector & t) {this->p = t;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + // candidates for SOLID_SOLUTION_MODIFY + LDBLE ag0, ag1; + std::vector ss_comps; + + // SOLID_SOLUTION keyword data + LDBLE a0, a1; + bool miscibility; + bool spinodal; + LDBLE tk, xb1, xb2; + SS_PARAMETER_TYPE input_case; + std::vector p; + + // workspace variable + LDBLE total_moles; + LDBLE dn; + bool ss_in; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(SSASSEMBLAGESS_H_INCLUDED) diff --git a/phreeqcpp/SSassemblage.cxx b/phreeqcpp/SSassemblage.cxx new file mode 100644 index 00000000..6a0be268 --- /dev/null +++ b/phreeqcpp/SSassemblage.cxx @@ -0,0 +1,317 @@ +// SSassemblage.cxx: implementation of the cxxSSassemblage class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SSassemblage.h" +#include "SS.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSSassemblage::cxxSSassemblage(PHRQ_io * io) + // + // default constructor for cxxSSassemblage + // +: cxxNumKeyword(io) +{ + new_def = false; +} + +cxxSSassemblage::cxxSSassemblage(const std::map < int, + cxxSSassemblage > &entities, cxxMix & mix, + int l_n_user, PHRQ_io * io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxSSassemblage *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } + this->new_def = false; +} + +cxxSSassemblage::~cxxSSassemblage() +{ +} + +void +cxxSSassemblage::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // SSassemblage element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SOLID_SOLUTIONS_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + // SSs + for (std::map < std::string, cxxSS >::const_iterator it = + SSs.begin(); it != SSs.end(); ++it) + { + s_oss << indent1; + s_oss << "-solid_solution " << it->first << "\n"; + (*it).second.dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# SOLID_SOLUTION candidate identifiers with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << (this->new_def ? 1 : 0) << "\n"; + + s_oss << indent1 << "# solid solution workspace variables #\n"; + s_oss << indent1; + s_oss << "-SSassemblage_totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} +void +cxxSSassemblage::read_raw(CParser & parser, bool check) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + cxxSS *ss_ptr = NULL; + + // Read SSassemblage number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser. + error_msg("Unknown input in SOLID_SOLUTIONS_RAW or SOLID_SOLUTIONS_MODIFY keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // solid_solution + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for solid solution name.", + PHRQ_io::OT_CONTINUE); + } + cxxSS temp_ss(this->Get_io()); + temp_ss.Set_name(str); + ss_ptr = this->Find(str); + if (ss_ptr) + { + temp_ss = *ss_ptr; + } + temp_ss.read_raw(parser, false); + this->SSs[str] = temp_ss; + } + useLastLine = true; + break; + case 1: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SSassemblage totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + case 2: // new_def + { + int i; + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for new_def.", PHRQ_io::OT_CONTINUE); + } + else + { + this->new_def = (i == 0) ? false : true; + } + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} + +void +cxxSSassemblage::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (std::map < std::string, cxxSS >::iterator it = + SSs.begin(); it != SSs.end(); ++it) + { + (*it).second.totalize(phreeqc_ptr); + this->totals.add_extensive((*it).second.Get_totals(), 1.0); + } + return; +} +void +cxxSSassemblage::add(const cxxSSassemblage & addee, LDBLE extensive) + // + // Add to existing ssassemblage to "this" ssassemblage + // +{ + if (extensive == 0.0) + return; + + for (std::map < std::string, cxxSS >::const_iterator itadd = + addee.SSs.begin(); itadd != addee.SSs.end(); + ++itadd) + { + std::map < std::string, cxxSS >::iterator it = + this->SSs.find((*itadd).first); + if (it != this->SSs.end()) + { + (*it).second.add((*itadd).second, extensive); + } + else + { + cxxSS entity = (*itadd).second; + entity.multiply(extensive); + std::string str(entity.Get_name()); + this->SSs[str] = entity; + } + } +} + +std::vector cxxSSassemblage:: +Vectorize(void) +{ + std::vector ss_v; + std::map::iterator it; + for (it = this->SSs.begin(); it != this->SSs.end(); it++) + { + ss_v.push_back(&(it->second)); + } + return ss_v; +} +cxxSS * cxxSSassemblage:: +Find(const std::string &s) +{ + std::map::iterator it; + it = this->SSs.find(s); + if (it != this->SSs.end()) + return &(it->second); + return NULL; +} +void +cxxSSassemblage::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + /* int n_user; */ + ints.push_back(this->n_user); + { + ints.push_back((int) this->SSs.size()); + std::map < std::string, cxxSS >::iterator it; + for (it = this->SSs.begin(); it != this->SSs.end(); it++) + { + (*it).second.Serialize(dictionary, ints, doubles); + } + } + ints.push_back(this->new_def ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxSSassemblage::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + { + int count = ints[ii++]; + this->SSs.clear(); + for (int n = 0; n < count; n++) + { + cxxSS ssc(this->io); + ssc.Deserialize(dictionary, ints, doubles, ii, dd); + std::string str(ssc.Get_name()); + this->SSs[str] = ssc; + } + } + this->new_def = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("solid_solution"), // 0 + std::vector< std::string >::value_type("ssassemblage_totals"), // 1 + std::vector< std::string >::value_type("new_def") // 2 +}; +const std::vector< std::string > cxxSSassemblage::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SSassemblage.h b/phreeqcpp/SSassemblage.h new file mode 100644 index 00000000..d69a6962 --- /dev/null +++ b/phreeqcpp/SSassemblage.h @@ -0,0 +1,59 @@ +#if !defined(SSASSEMBLAGE_H_INCLUDED) +#define SSASSEMBLAGE_H_INCLUDED + + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "NameDouble.h" +#include "SS.h" + +class cxxSS; + +//#include "cxxMix.h" +class cxxMix; + +class cxxSSassemblage:public cxxNumKeyword +{ + +public: + cxxSSassemblage(PHRQ_io * io = NULL); + cxxSSassemblage(const std::map < int, cxxSSassemblage > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io = NULL); + ~cxxSSassemblage(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + void totalize(Phreeqc * phreeqc_ptr); + + const cxxNameDouble & Get_totals() const {return this->totals;} + std::map < std::string, cxxSS > & Get_SSs(void) {return SSs;} + const std::map < std::string, cxxSS > & Get_SSs(void)const {return SSs;} + void Set_SSs(std::map < std::string, cxxSS > & ss) {SSs = ss;} + bool Get_new_def(void) const {return new_def;} + void Set_new_def(bool tf) {new_def = tf;} + std::vector Vectorize(void); + void add(const cxxSSassemblage & addee, LDBLE extensive); + cxxSS *Find(const std::string &s); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + // SOLID_SOLUTION_MODIFY candidate + std::map < std::string, cxxSS > SSs; + // SOLID_SOLUTION keyword data + bool new_def; + // internal variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SSASSEMBLAGE_H_INCLUDED) diff --git a/phreeqcpp/SScomp.cxx b/phreeqcpp/SScomp.cxx new file mode 100644 index 00000000..5b7b136f --- /dev/null +++ b/phreeqcpp/SScomp.cxx @@ -0,0 +1,297 @@ +// SScomp.cxx: implementation of the cxxSScomp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SScomp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSScomp::cxxSScomp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxSScomp + // +{ + name = ""; + initial_moles = 0; + moles = 0; + init_moles = 0; + delta = 0; + fraction_x = 0; + log10_lambda = 0; + log10_fraction_x = 0; + dn = 0; + dnc = 0; + dnb = 0; +} +cxxSScomp::~cxxSScomp() +{ +} + +void +cxxSScomp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent1 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + + s_oss << indent1 << "# Solid solution workspace variables #\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; + s_oss << indent1 << "-init_moles " << this->init_moles << "\n"; + s_oss << indent1 << "-delta " << this->delta << "\n"; + s_oss << indent1 << "-fraction_x " << this->fraction_x << "\n"; + s_oss << indent1 << "-log10_lambda " << this->log10_lambda << "\n"; + s_oss << indent1 << "-log10_fraction_x " << this->log10_fraction_x << "\n"; + s_oss << indent1 << "-dn " << this->dn << "\n"; + s_oss << indent1 << "-dnc " << this->dnc << "\n"; + s_oss << indent1 << "-dnb " << this->dnb << "\n"; +} + +void +cxxSScomp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool initial_moles_defined(false); + bool moles_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name + parser.error_msg("-Name ignored. Define with -component."); + break; + + case 1: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + initial_moles_defined = true; + break; + + case 2: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 3: // init_moles + if (!(parser.get_iss() >> this->init_moles)) + { + this->init_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for init_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 4: // delta + if (!(parser.get_iss() >> this->delta)) + { + this->delta = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for delta.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // fraction_x + if (!(parser.get_iss() >> this->fraction_x)) + { + this->fraction_x = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for fraction_x.", + PHRQ_io::OT_CONTINUE); + } + break; + + + case 6: // log10_lambda + if (!(parser.get_iss() >> this->log10_lambda)) + { + this->log10_lambda = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for log10_lambda.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 7: // log10_fraction_x + if (!(parser.get_iss() >> this->log10_fraction_x)) + { + this->log10_fraction_x = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for log10_fraction_x.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // dn + if (!(parser.get_iss() >> this->dn)) + { + this->dn = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for dn.", + PHRQ_io::OT_CONTINUE); + } + break; + case 9: // dnc + if (!(parser.get_iss() >> this->dnc)) + { + this->dnc = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dnc.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // dnb + if (!(parser.get_iss() >> this->dnb)) + { + this->dnb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dnb.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (initial_moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Initial_moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSScomp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + this->delta *= extensive; + this->initial_moles *= extensive; +} +void +cxxSScomp::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->moles); + doubles.push_back(this->initial_moles); + doubles.push_back(this->init_moles); + doubles.push_back(this->delta); + doubles.push_back(this->fraction_x); + doubles.push_back(this->log10_lambda); + doubles.push_back(this->log10_fraction_x); + doubles.push_back(this->dn); + doubles.push_back(this->dnc); + doubles.push_back(this->dnb); + +} + +void +cxxSScomp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + + this->name = dictionary.GetWords()[ints[ii++]]; + this->moles = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->init_moles = doubles[dd++]; + this->delta = doubles[dd++]; + this->fraction_x = doubles[dd++]; + this->log10_lambda = doubles[dd++]; + this->log10_fraction_x = doubles[dd++]; + this->dn = doubles[dd++]; + this->dnc = doubles[dd++]; + this->dnb = doubles[dd++]; + +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("initial_moles"), // 1 + std::vector< std::string >::value_type("moles"), // 2 + std::vector< std::string >::value_type("init_moles"), // 3 + std::vector< std::string >::value_type("delta"), // 4 + std::vector< std::string >::value_type("fraction_x"), // 5 + std::vector< std::string >::value_type("log10_lambda"), // 6 + std::vector< std::string >::value_type("log10_fraction_x"), // 7 + std::vector< std::string >::value_type("dn"), // 8 + std::vector< std::string >::value_type("dnc"), // 9 + std::vector< std::string >::value_type("dnb") // 10 +}; +const std::vector< std::string > cxxSScomp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SScomp.h b/phreeqcpp/SScomp.h new file mode 100644 index 00000000..1afa66e5 --- /dev/null +++ b/phreeqcpp/SScomp.h @@ -0,0 +1,66 @@ +#if !defined(SSCOMP_H_INCLUDED) +#define SSCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxSScomp: public PHRQ_base +{ + + public: + cxxSScomp(PHRQ_io *io=NULL); + virtual ~cxxSScomp(); + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const {return this->name;} + void Set_name(std::string s) {this->name = s;} + + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_init_moles() const {return this->init_moles;} + void Set_init_moles(LDBLE t) {this->init_moles = t;} + LDBLE Get_delta() const {return this->delta;} + void Set_delta(LDBLE t) {this->delta = t;} + LDBLE Get_fraction_x() const {return this->fraction_x;} + void Set_fraction_x(LDBLE t) {this->fraction_x = t;} + LDBLE Get_log10_lambda() const {return this->log10_lambda;} + void Set_log10_lambda(LDBLE t) {this->log10_lambda = t;} + LDBLE Get_log10_fraction_x() const {return this->log10_fraction_x;} + void Set_log10_fraction_x(LDBLE t) {this->log10_fraction_x = t;} + LDBLE Get_dn() const {return this->dn;} + void Set_dn(LDBLE t) {this->dn = t;} + LDBLE Get_dnc() const {return this->dnc;} + void Set_dnc(LDBLE t) {this->dnc = t;} + LDBLE Get_dnb() const {return this->dnb;} + void Set_dnb(LDBLE t) {this->dnb = t;} + + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + // SOLID_SOLUTION_MODIFY candidate identifier + LDBLE moles; + + // Solid solution workspace variables + LDBLE initial_moles; + LDBLE init_moles; + LDBLE delta; + LDBLE fraction_x; + LDBLE log10_lambda; + LDBLE log10_fraction_x; + LDBLE dn, dnc, dnb; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SSCOMP_H_INCLUDED) diff --git a/phreeqcpp/SelectedOutput.cpp b/phreeqcpp/SelectedOutput.cpp new file mode 100644 index 00000000..f28b3930 --- /dev/null +++ b/phreeqcpp/SelectedOutput.cpp @@ -0,0 +1,115 @@ +#include "SelectedOutput.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +SelectedOutput::SelectedOutput(int n, PHRQ_io *io) +: cxxNumKeyword(io) +{ + // file_name + this->Set_file_name(n); + + // punch_ostream + this->punch_ostream = NULL; + + // state vars + this->active = true; + this->new_def = false; + this->user_punch_new_def = false; + this->have_punch_name = false; + + // as-is vars + // + this->user_punch = true; + this->high_precision = false; + this->inverse = true; + + this->sim = true; + this->state = true; + this->soln = true; + this->dist = true; + this->time = true; + + this->step = true; + this->ph = true; + this->pe = true; + this->rxn = false; + this->temp = false; + + this->alk = false; + this->mu = false; + this->water = false; + this->charge_balance = false; + this->percent_error = false; + this->new_line = true; + + // as-is set flags + // + this->set_user_punch = false; + this->set_high_precision = false; + this->set_inverse = false; + + this->set_sim = false; + this->set_state = false; + this->set_soln = false; + this->set_dist = false; + this->set_time = false; + + this->set_step = false; + this->set_ph = false; + this->set_pe = false; + this->set_rxn = false; + this->set_temp = false; + + this->set_alk = false; + this->set_mu = false; + this->set_water = false; + this->set_charge_balance = false; + this->set_percent_error = false; + this->set_new_line = false; +} + +SelectedOutput::~SelectedOutput(void) +{ + if (this->punch_ostream != NULL) + { + delete this->punch_ostream; + } + this->punch_ostream = NULL; +} + +void +SelectedOutput::Reset(bool value) +{ + // as-is vars + Set_sim(value); + Set_state(value); + Set_soln(value); + Set_dist(value); + Set_time(value); + + Set_step(value); + Set_ph(value); + Set_pe(value); + Set_rxn(value); + Set_temp(value); + + Set_alk(value); + Set_mu(value); + Set_water(value); + Set_charge_balance(value); + Set_percent_error(value); +} + +void +SelectedOutput::Set_file_name(int n) +{ + std::ostringstream os; + os << "selected_output_" << n << ".sel"; + file_name = os.str(); +} diff --git a/phreeqcpp/SelectedOutput.h b/phreeqcpp/SelectedOutput.h new file mode 100644 index 00000000..0ea5c575 --- /dev/null +++ b/phreeqcpp/SelectedOutput.h @@ -0,0 +1,209 @@ +#if !defined(SELECTEDOUTPUT_H_INCLUDED) +#define SELECTEDOUTPUT_H_INCLUDED +#include // std::string +#include +#include +#include "NumKeyword.h" + +class SelectedOutput:public cxxNumKeyword +{ +public: + SelectedOutput(int n=1, PHRQ_io *io=NULL); + ~SelectedOutput(void); + + void Reset(bool tf); + + // vector getters + inline std::vector< std::pair< std::string, void * > > & Get_totals(void) {return this->totals;} + inline std::vector< std::pair< std::string, void * > > & Get_molalities(void) {return this->molalities;} + inline std::vector< std::pair< std::string, void * > > & Get_activities(void) {return this->activities;} + inline std::vector< std::pair< std::string, void * > > & Get_pure_phases(void) {return this->pure_phases;} + inline std::vector< std::pair< std::string, void * > > & Get_si(void) {return this->si;} + inline std::vector< std::pair< std::string, void * > > & Get_gases(void) {return this->gases;} + inline std::vector< std::pair< std::string, void * > > & Get_s_s(void) {return this->s_s;} + inline std::vector< std::pair< std::string, void * > > & Get_kinetics(void) {return this->kinetics;} + inline std::vector< std::pair< std::string, void * > > & Get_isotopes(void) {return this->isotopes;} + inline std::vector< std::pair< std::string, void * > > & Get_calculate_values(void) {return this->calculate_values;} + + // const vector getters + inline const std::vector< std::pair< std::string, void * > > & Get_totals(void)const {return this->totals;} + inline const std::vector< std::pair< std::string, void * > > & Get_molalities(void)const {return this->molalities;} + inline const std::vector< std::pair< std::string, void * > > & Get_activities(void)const {return this->activities;} + inline const std::vector< std::pair< std::string, void * > > & Get_pure_phases(void)const {return this->pure_phases;} + inline const std::vector< std::pair< std::string, void * > > & Get_si(void)const {return this->si;} + inline const std::vector< std::pair< std::string, void * > > & Get_gases(void)const {return this->gases;} + inline const std::vector< std::pair< std::string, void * > > & Get_s_s(void)const {return this->s_s;} + inline const std::vector< std::pair< std::string, void * > > & Get_kinetics(void)const {return this->kinetics;} + inline const std::vector< std::pair< std::string, void * > > & Get_isotopes(void)const {return this->isotopes;} + inline const std::vector< std::pair< std::string, void * > > & Get_calculate_values(void)const {return this->calculate_values;} + + // file_name getters/setters + void Set_file_name(int i); + inline void Set_file_name(std::string s) {this->file_name = s;} + inline std::string & Get_file_name(void) {return this->file_name;} + inline const std::string & Get_file_name(void)const {return this->file_name;} + + // punch_ostream getters/setters + inline std::ostream* Get_punch_ostream(void) {return this->punch_ostream;} + inline const std::ostream* Get_punch_ostream(void)const {return this->punch_ostream;} + inline void Set_punch_ostream(std::ostream * os) {this->punch_ostream = os;} + + // state var getters + inline bool Get_active(void)const {return this->active;} + inline bool Get_new_def(void)const {return this->new_def;} + inline bool Get_user_punch_new_def(void)const {return this->user_punch_new_def;} + inline bool Get_have_punch_name(void)const {return this->have_punch_name;} + + // state var setters + inline void Set_active(bool tf) {this->active = tf;} + inline void Set_new_def(bool tf) {this->new_def = tf;} + inline void Set_user_punch_new_def(bool tf) {this->user_punch_new_def = tf;} + inline void Set_have_punch_name(bool tf) {this->have_punch_name = tf;} + + // as_is getters + inline bool Get_user_punch(void)const {return this->user_punch;} + inline bool Get_high_precision(void)const {return this->high_precision;} + inline bool Get_inverse(void)const {return this->inverse;} + + inline bool Get_sim(void)const {return this->sim;} + inline bool Get_state(void)const {return this->state;} + inline bool Get_soln(void)const {return this->soln;} + inline bool Get_dist(void)const {return this->dist;} + inline bool Get_time(void)const {return this->time;} + + inline bool Get_step(void)const {return this->step;} + inline bool Get_ph(void)const {return this->ph;} + inline bool Get_pe(void)const {return this->pe;} + inline bool Get_rxn(void)const {return this->rxn;} + inline bool Get_temp(void)const {return this->temp;} + + inline bool Get_alk(void)const {return this->alk;} + inline bool Get_mu(void)const {return this->mu;} + inline bool Get_water(void)const {return this->water;} + inline bool Get_charge_balance(void)const {return this->charge_balance;} + inline bool Get_percent_error(void)const {return this->percent_error;} + inline bool Get_new_line(void)const {return this->new_line; } + + // as-is setters + inline void Set_user_punch(bool tf) {this->user_punch = tf; this->set_user_punch = true;} + inline void Set_high_precision(bool tf) {this->high_precision = tf; this->set_high_precision = true;} + inline void Set_inverse(bool tf) {this->inverse = tf; this->set_inverse = true;} + + inline void Set_sim(bool tf) {this->sim = tf; this->set_sim = true;} + inline void Set_state(bool tf) {this->state = tf; this->set_state = true;} + inline void Set_soln(bool tf) {this->soln = tf; this->set_soln = true;} + inline void Set_dist(bool tf) {this->dist = tf; this->set_dist = true;} + inline void Set_time(bool tf) {this->time = tf; this->set_time = true;} + + inline void Set_step(bool tf) {this->step = tf; this->set_step = true;} + inline void Set_ph(bool tf) {this->ph = tf; this->set_ph = true;} + inline void Set_pe(bool tf) {this->pe = tf; this->set_pe = true;} + inline void Set_rxn(bool tf) {this->rxn = tf; this->set_rxn = true;} + inline void Set_temp(bool tf) {this->temp = tf; this->set_temp = true;} + + inline void Set_alk(bool tf) {this->alk = tf; this->set_alk = true;} + inline void Set_mu(bool tf) {this->mu = tf; this->set_mu = true;} + inline void Set_water(bool tf) {this->water = tf; this->set_water = true;} + inline void Set_charge_balance(bool tf) {this->charge_balance = tf; this->set_charge_balance = true;} + inline void Set_percent_error(bool tf) {this->percent_error = tf; this->set_percent_error = true;} + inline void Set_new_line(bool tf) {this->new_line = tf; this->set_new_line = true;} + + // set flag getters + inline bool was_set_user_punch()const {return this->set_user_punch;} + inline bool was_set_high_precision()const {return this->set_high_precision;} + inline bool was_set_inverse()const {return this->set_inverse;} + + inline bool was_set_sim()const {return this->set_sim;} + inline bool was_set_state()const {return this->set_state;} + inline bool was_set_soln()const {return this->set_soln;} + inline bool was_set_dist()const {return this->set_dist;} + inline bool was_set_time()const {return this->set_time;} + + inline bool was_set_step()const {return this->set_step;} + inline bool was_set_ph()const {return this->set_ph;} + inline bool was_set_pe()const {return this->set_pe;} + inline bool was_set_rxn()const {return this->set_rxn;} + inline bool was_set_temp()const {return this->set_temp;} + + inline bool was_set_alk()const {return this->set_alk;} + inline bool was_set_mu()const {return this->set_mu;} + inline bool was_set_water()const {return this->set_water;} + inline bool was_set_charge_balance()const {return this->set_charge_balance;} + inline bool was_set_percent_error()const {return this->set_percent_error;} + inline bool was_set_new_line()const {return this->set_new_line;} + +protected: + + // vectors + std::vector< std::pair< std::string, void * > > totals; + std::vector< std::pair< std::string, void * > > molalities; + std::vector< std::pair< std::string, void * > > activities; + std::vector< std::pair< std::string, void * > > pure_phases; + std::vector< std::pair< std::string, void * > > si; + std::vector< std::pair< std::string, void * > > gases; + std::vector< std::pair< std::string, void * > > s_s; + std::vector< std::pair< std::string, void * > > kinetics; + std::vector< std::pair< std::string, void * > > isotopes; + std::vector< std::pair< std::string, void * > > calculate_values; + + // file_name + std::string file_name; + + // punch_ostream + std::ostream * punch_ostream; + + // state vars + bool active; + bool new_def; + bool user_punch_new_def; + bool have_punch_name; + + // as-is vars + bool user_punch; + bool high_precision; + bool inverse; + + bool sim; + bool state; + bool soln; + bool dist; + bool time; + + bool step; + bool ph; + bool pe; + bool rxn; + bool temp; + + bool alk; + bool mu; + bool water; + bool charge_balance; + bool percent_error; + bool new_line; + + // as-is set flags + bool set_user_punch; + bool set_high_precision; + bool set_inverse; + + bool set_sim; + bool set_state; + bool set_soln; + bool set_dist; + bool set_time; + + bool set_step; + bool set_ph; + bool set_pe; + bool set_rxn; + bool set_temp; + + bool set_alk; + bool set_mu; + bool set_water; + bool set_charge_balance; + bool set_percent_error; + bool set_new_line; +}; +#endif // !defined(SELECTEDOUTPUT_H_INCLUDED) diff --git a/phreeqcpp/Serializer.cxx b/phreeqcpp/Serializer.cxx new file mode 100644 index 00000000..b4ebab76 --- /dev/null +++ b/phreeqcpp/Serializer.cxx @@ -0,0 +1,209 @@ +#include "Serializer.h" +#include "Phreeqc.h" +#include "Utils.h" +#include "Solution.h" +#include "Exchange.h" +#include "Temperature.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "Surface.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +Serializer::Serializer(PHRQ_io *io) + : PHRQ_base(io) +{ +} +Serializer::~Serializer(void) +{ +} +bool Serializer::Serialize(Phreeqc &phreeqc_ref, int start, int end, bool include_t, bool include_p, PHRQ_io *io) +{ + for (int i = start; i <= end; i++) + { + cxxSolution *soln_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_solution_map(), i); + if (soln_ptr) + { + ints.push_back((int) PT_SOLUTION); + soln_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + // Exchangers + { + cxxExchange *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_exchange_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_EXCHANGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // GasPhases + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_gas_phase_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_GASPHASE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Kinetics + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_kinetics_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_KINETICS); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // PPassemblages + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_pp_assemblage_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_PPASSEMBLAGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // SSassemblages + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_ss_assemblage_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_SSASSEMBLAGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Surfaces + { + cxxSurface *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_surface_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_SURFACES); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Temperature + if (include_t) + { + cxxTemperature *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_temperature_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_TEMPERATURE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Pressure + if (include_p) + { + cxxPressure *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_pressure_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_PRESSURE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + } + return true; +} + +bool +Serializer::Deserialize(Phreeqc &phreeqc_ref, Dictionary &dictionary, std::vector &ints, std::vector &doubles) +{ + int ii = 0; + int dd = 0; + while (ii < (int) ints.size()) + { + PACK_TYPE type = (PACK_TYPE) ints[ii++]; + switch (type) + { + case PT_SOLUTION: + { + cxxSolution soln; + soln.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = soln.Get_n_user(); + //std::cerr << "unpacked solution " << n_user << std::endl; + phreeqc_ref.Get_Rxn_solution_map()[n_user] = soln; + } + break; + case PT_EXCHANGE: + { + cxxExchange entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_exchange_map()[n_user] = entity; + } + break; + case PT_GASPHASE: + { + cxxGasPhase entity(phreeqc_ref.Get_phrq_io()); + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_gas_phase_map()[n_user] = entity; + } + break; + case PT_KINETICS: + { + cxxKinetics entity(phreeqc_ref.Get_phrq_io()); + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_kinetics_map()[n_user] = entity; + } + break; + case PT_PPASSEMBLAGE: + { + cxxPPassemblage entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + //std::cerr << "unpacked pp assemblage " << n_user << std::endl; + phreeqc_ref.Get_Rxn_pp_assemblage_map()[n_user] = entity; + } + break; + case PT_SSASSEMBLAGE: + { + cxxSSassemblage entity(phreeqc_ref.Get_phrq_io()); + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_ss_assemblage_map()[n_user] = entity; + } + break; + case PT_SURFACES: + { + cxxSurface entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_surface_map()[n_user] = entity; + } + break; + case PT_TEMPERATURE: + { + cxxTemperature entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_temperature_map()[n_user] = entity; + } + break; + case PT_PRESSURE: + { + cxxPressure entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_pressure_map()[n_user] = entity; + } + break; + default: +#if !defined(R_SO) + std::cerr << "Unknown pack type in deserialize " << type << std::endl; + exit(4); +#endif + break; + } + } + return true; +} diff --git a/phreeqcpp/Serializer.h b/phreeqcpp/Serializer.h new file mode 100644 index 00000000..f468dd47 --- /dev/null +++ b/phreeqcpp/Serializer.h @@ -0,0 +1,42 @@ +#if !defined(SERIALIZER_H_INCLUDED) +#define SERIALIZER_H_INCLUDED +#include +#include +#include +#include +#include "PHRQ_base.h" +#include "Dictionary.h" +class Phreeqc; +class Serializer : public PHRQ_base +{ +public: + Serializer(PHRQ_io *io = NULL); + ~Serializer(void); + + enum PACK_TYPE + { + PT_SOLUTION = 0, + PT_EXCHANGE = 1, + PT_GASPHASE = 2, + PT_KINETICS = 3, + PT_PPASSEMBLAGE = 4, + PT_SSASSEMBLAGE = 5, + PT_SURFACES = 6, + PT_TEMPERATURE = 7, + PT_PRESSURE = 8 + }; + bool Serialize(Phreeqc &phreeqc_ptr, int start, int end, bool include_t, bool include_p, PHRQ_io *io = NULL); + bool Deserialize(Phreeqc &phreeqc_ptr, Dictionary &dictionary, std::vector &ints, std::vector &doubles); + Dictionary &GetDictionary(void) {return this->dictionary;} + std::vector &GetInts(void) {return this->ints;} + std::vector &GetDoubles(void) {return this->doubles;} + //std::string &GetWordsString(void) {return this->words_string;} + + +protected: + std::vector ints; + std::vector doubles; + //std::string words_string; + Dictionary dictionary; +}; +#endif // !defined(SERIALIZER_H_INCLUDED) diff --git a/phreeqcpp/Solution.cxx b/phreeqcpp/Solution.cxx new file mode 100644 index 00000000..cb2d2941 --- /dev/null +++ b/phreeqcpp/Solution.cxx @@ -0,0 +1,1757 @@ +// Solution.cxx: implementation of the cxxSolution class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif + +#include +#include // assert +#include // std::sort +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Solution.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +cxxSolution::cxxSolution(PHRQ_io * io) + // + // default constructor for cxxSolution + // +: cxxNumKeyword(io) +{ + this->io = io; + this->new_def = false; + this->patm = 1.0; + this->potV = 0.0; + this->tc = 25.0; + this->ph = 7.0; + this->pe = 4.0; + this->mu = 1e-7; + this->ah2o = 1.0; + this->total_h = 111.1; + this->total_o = 55.55; + this->cb = 0.0; + this->density = 1.0; + this->mass_water = 1.0; + this->soln_vol = 1.0; + this->total_alkalinity = 0.0; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + this->master_activity.type = cxxNameDouble::ND_SPECIES_LA; + this->species_gamma.type = cxxNameDouble::ND_SPECIES_GAMMA; + this->initial_data = NULL; +} +cxxSolution::cxxSolution(const cxxSolution &old_sol) +: initial_data(NULL) +{ + *this = old_sol; +} +const cxxSolution & +cxxSolution::operator =(const cxxSolution &rhs) +{ + if (this != &rhs) + { + this->io = rhs.io; + this->n_user = rhs.n_user; + this->n_user_end = rhs.n_user_end; + this->description = rhs.description; + this->new_def = rhs.new_def; + this->patm = rhs.patm; + this->potV = rhs.potV; + this->tc = rhs.tc; + this->ph = rhs.ph; + this->pe = rhs.pe; + this->mu = rhs.mu; + this->ah2o = rhs.ah2o; + this->total_h = rhs.total_h; + this->total_o = rhs.total_o; + this->density = rhs.density; + this->cb = rhs.cb; + this->mass_water = rhs.mass_water; + this->soln_vol = rhs.soln_vol; + this->total_alkalinity = rhs.total_alkalinity; + this->totals = rhs.totals; + this->master_activity = rhs.master_activity; + this->species_gamma = rhs.species_gamma; + this->isotopes = rhs.isotopes; + this->species_map = rhs.species_map; + this->log_gamma_map = rhs.log_gamma_map; + this->log_molalities_map = rhs.log_molalities_map; + if (this->initial_data) + delete initial_data; + if (rhs.initial_data != NULL) + this->initial_data = new cxxISolution(*rhs.initial_data); + else + this->initial_data = NULL; + } + return *this; +} +cxxSolution::cxxSolution(std::map < int, cxxSolution > &solutions, + cxxMix & mix, int l_n_user, PHRQ_io * io) +// +// constructor for cxxSolution from mixture of solutions +// +: cxxNumKeyword(io) +{ +// +// Zero out solution data +// + this->zero(); + this->n_user = this->n_user_end = l_n_user; + this->new_def = false; + this->ah2o = 0; + // potV is an external variable, imposed in a given solution, not mixed. + std::map < int, cxxSolution >::const_iterator sol = solutions.find(mix.Get_n_user()); + const cxxSolution *cxxsoln_ptr1; + if (sol != solutions.end()) + { + cxxsoln_ptr1 = &(sol->second); + if (cxxsoln_ptr1->new_def) + this->potV = 0.0; + else + this->potV = cxxsoln_ptr1->potV; + } +// +// Mix solutions +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + sol = solutions.find(it->first); + if (sol == solutions.end()) + { + std::ostringstream msg; + msg << "Solution " << it->first << " not found in mix_cxxSolutions."; + error_msg(msg.str(), CONTINUE); + } + else + { + cxxsoln_ptr1 = &(sol->second); + this->add(*cxxsoln_ptr1, it->second); + } + } + +} +cxxSolution::~cxxSolution() +{ + delete this->initial_data; +} + +void +cxxSolution::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + // Solution element and attributes + s_oss << indent0; + s_oss << "n_user << "\" " << "\n"; + + s_oss << indent1; + s_oss << "soln_description=\"" << this->description << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_tc=\"" << this->tc << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_ph=\"" << this->ph << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_solution_pe=\"" << this->pe << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_mu=\"" << this->mu << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_ah2o=\"" << this->ah2o << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_h=\"" << this->total_h << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_o=\"" << this->total_o << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_cb=\"" << this->cb << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_mass_water=\"" << this->mass_water << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_vol=\"" << this->soln_vol << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_alkalinity=\"" << this-> + total_alkalinity << "\"" << "\n"; + + s_oss << indent1; + s_oss << "\">" << "\n"; + + // soln_total conc structures + this->totals.dump_xml(s_oss, indent + 1); + + // master_activity map + this->master_activity.dump_xml(s_oss, indent + 1); + + // species_gamma map + this->species_gamma.dump_xml(s_oss, indent + 1); + + // End of solution + s_oss << indent0; + s_oss << "" << "\n"; + + return; +} + +void +cxxSolution::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Solution element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SOLUTION_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-temp " << this->tc << "\n"; + + s_oss << indent1; + s_oss << "-pressure " << this->patm << "\n"; + + s_oss << indent1; + s_oss << "-potential " << this->potV << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_h " << this->total_h << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_o " << this->total_o << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-cb " << this->cb << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-density " << this->density << "\n"; + + // soln_total conc structures + s_oss << indent1; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + + // Isotopes + { + for (std::map < std::string, cxxSolutionIsotope >::const_iterator it = + this->isotopes.begin(); it != isotopes.end(); ++it) + { + s_oss << indent1 << "-Isotope" << "\n"; + it->second.dump_raw(s_oss, indent + 2); + } + } + + s_oss << indent1; + s_oss << "-pH " << this->ph << "\n"; + + s_oss << indent1; + s_oss << "-pe " << this->pe << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-mu " << this->mu << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-ah2o " << this->ah2o << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-mass_water " << this->mass_water << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-soln_vol " << this->soln_vol << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_alkalinity " << this->total_alkalinity << "\n"; + + // master_activity map + s_oss << indent1; + s_oss << "-activities" << "\n"; + this->master_activity.dump_raw(s_oss, indent + 2); + + // species_gamma map + s_oss << indent1; + s_oss << "-gammas" << "\n"; + this->species_gamma.dump_raw(s_oss, indent + 2); + + // species_map + if (species_map.size() > 0) + { + s_oss << indent1; + s_oss << "-species_map" << "\n"; + std::map::const_iterator it = this->species_map.begin(); + for ( ; it != species_map.end(); it++) + { + s_oss << indent2; + s_oss << it->first << " " << it->second << "\n"; + } + } + + // log_gamma_map + if (log_gamma_map.size() > 0) + { + s_oss << indent1; + s_oss << "-log_gamma_map" << "\n"; + std::map::const_iterator it = this->log_gamma_map.begin(); + for ( ; it != log_gamma_map.end(); it++) + { + s_oss << indent2; + s_oss << it->first << " " << it->second << "\n"; + } + } + + // log_molalities_map + if (log_molalities_map.size() > 0) + { + s_oss << indent1; + s_oss << "-log_molalities_map" << "\n"; + std::map::const_iterator it = this->log_molalities_map.begin(); + for (; it != log_molalities_map.end(); it++) + { + s_oss << indent2; + s_oss << it->first << " " << it->second << "\n"; + } + } + return; +} + +#ifdef USE_REVISED_READ_RAW +void +cxxSolution::read_raw(CParser & parser, bool check) +{ + + // Used if it is modify + cxxNameDouble original_totals = this->totals; + cxxNameDouble original_activities(this->master_activity); + + this->master_activity.clear(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + // Read solution number and description + this->read_number_description(parser.line()); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + bool tc_defined(false); + bool ph_defined(false); + bool pe_defined(false); + bool mu_defined(false); + bool ah2o_defined(false); + bool total_h_defined(false); + bool total_o_defined(false); + bool cb_defined(false); + bool mass_water_defined(false); + bool total_alkalinity_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // totals + { + cxxNameDouble temp_totals; + if (temp_totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->totals.merge_redox(temp_totals); + } + } + opt_save = 0; + break; + + case 1: // activities + if (this->master_activity.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and log activity for activities.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + + case 2: // gammas + if (this->species_gamma.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and activity coefficient for gammas.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + break; + + case 3: // isotope + { + std::string name; + if (!(parser.get_iss() >> name)) + { + parser.incr_input_error(); + parser.error_msg("Expected character value for isotope name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSolutionIsotope iso(this->Get_io()); + iso.Set_isotope_name(name.c_str()); + iso.read_raw(parser, check); + this->isotopes[name] = iso; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // temp + case 5: // tc_avoid_conflict_with_technetium + case 6: // temperature + if (!(parser.get_iss() >> this->tc)) + { + this->tc = 25.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + tc_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // ph + if (!(parser.get_iss() >> this->ph)) + { + this->ph = 7.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pH.", + PHRQ_io::OT_CONTINUE); + } + ph_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // pe + if (!(parser.get_iss() >> this->pe)) + { + this->pe = 4.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pe.", + PHRQ_io::OT_CONTINUE); + } + pe_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // mu + case 10: // ionic_strength + if (!(parser.get_iss() >> this->mu)) + { + this->mu = 1e-7; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ionic strength.", + PHRQ_io::OT_CONTINUE); + } + mu_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ah2o + case 12: // activity_water + if (!(parser.get_iss() >> this->ah2o)) + { + this->ah2o = 1.0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for activity of water.", + PHRQ_io::OT_CONTINUE); + } + ah2o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 13: // total_h + if (!(parser.get_iss() >> this->total_h)) + { + this->total_h = 111.1; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total hydrogen.", + PHRQ_io::OT_CONTINUE); + } + total_h_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 14: // total_o + if (!(parser.get_iss() >> this->total_o)) + { + this->total_o = 55.55; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total oxygen.", + PHRQ_io::OT_CONTINUE); + } + total_o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 15: // mass_water + case 16: // mass_h2o + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass of water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 17: // total_alkalinity + case 18: // total_alk + if (!(parser.get_iss() >> this->total_alkalinity)) + { + this->total_alkalinity = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for total_alkalinity.", + PHRQ_io::OT_CONTINUE); + } + total_alkalinity_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 19: // cb + case 20: // charge_balance + if (!(parser.get_iss() >> this->cb)) + { + this->cb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge balance.", + PHRQ_io::OT_CONTINUE); + } + cb_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 21: // density + if (!(parser.get_iss() >> this->density)) + { + this->density = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for density.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (tc_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ph_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pH not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (pe_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pe not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mu_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ionic strength not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ah2o_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Activity of water not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_h_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total hydrogen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_o_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total oxygen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cb_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge balance not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_alkalinity_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Total alkalinity not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + + // Update activities + + if (original_activities.size() > 0) + { + cxxNameDouble new_activities = this->master_activity; + this->master_activity = original_activities; + this->Update_activities(original_totals); + cxxNameDouble::iterator it = new_activities.begin(); + for ( ; it != new_activities.end(); it++) + { + this->master_activity[it->first] = it->second; + } + } + return; +} +#endif +void +cxxSolution::read_raw(CParser & parser, bool check) +{ + + // Used if it is modify + cxxNameDouble simple_original_totals = this->totals.Simplify_redox(); + cxxNameDouble original_activities(this->master_activity); + + this->master_activity.clear(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + // Read solution number and description + this->read_number_description(parser.line()); + + opt_save = CParser::OPT_ERROR; + bool tc_defined(false); + bool ph_defined(false); + bool pe_defined(false); + bool mu_defined(false); + bool ah2o_defined(false); + bool total_h_defined(false); + bool total_o_defined(false); + bool cb_defined(false); + bool mass_water_defined(false); + bool total_alkalinity_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // totals + { + cxxNameDouble temp_totals; + if (temp_totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->totals.merge_redox(temp_totals); + } + } + opt_save = 0; + break; + + case 1: // activities + if (this->master_activity.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and log activity for activities.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + + case 2: // gammas + if (this->species_gamma.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and activity coefficient for gammas.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + break; + + case 3: // isotope + { + std::string name; + if (!(parser.get_iss() >> name)) + { + parser.incr_input_error(); + parser.error_msg("Expected character value for isotope name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSolutionIsotope iso(this->Get_io()); + iso.Set_isotope_name(name.c_str()); + iso.read_raw(parser, check); + this->isotopes[name] = iso; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // temp + case 5: // tc_avoid_conflict_with_technetium + case 6: // temperature + if (!(parser.get_iss() >> this->tc)) + { + this->tc = 25.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + tc_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // ph + if (!(parser.get_iss() >> this->ph)) + { + this->ph = 7.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pH.", + PHRQ_io::OT_CONTINUE); + } + ph_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // pe + if (!(parser.get_iss() >> this->pe)) + { + this->pe = 4.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pe.", + PHRQ_io::OT_CONTINUE); + } + pe_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // mu + case 10: // ionic_strength + if (!(parser.get_iss() >> this->mu)) + { + this->mu = 1e-7; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ionic strength.", + PHRQ_io::OT_CONTINUE); + } + mu_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ah2o + case 12: // activity_water + if (!(parser.get_iss() >> this->ah2o)) + { + this->ah2o = 1.0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for activity of water.", + PHRQ_io::OT_CONTINUE); + } + ah2o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 13: // total_h + if (!(parser.get_iss() >> this->total_h)) + { + this->total_h = 111.1; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total hydrogen.", + PHRQ_io::OT_CONTINUE); + } + total_h_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 14: // total_o + if (!(parser.get_iss() >> this->total_o)) + { + this->total_o = 55.55; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total oxygen.", + PHRQ_io::OT_CONTINUE); + } + total_o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 15: // mass_water + case 16: // mass_h2o + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass of water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 17: // total_alkalinity + case 18: // total_alk + if (!(parser.get_iss() >> this->total_alkalinity)) + { + this->total_alkalinity = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for total_alkalinity.", + PHRQ_io::OT_CONTINUE); + } + total_alkalinity_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 19: // cb + case 20: // charge_balance + if (!(parser.get_iss() >> this->cb)) + { + this->cb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge balance.", + PHRQ_io::OT_CONTINUE); + } + cb_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 21: // density + if (!(parser.get_iss() >> this->density)) + { + this->density = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for density.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + case 22: // pressure + if (!(parser.get_iss() >> this->patm)) + { + this->patm = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pressure.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 23: // soln_vol + if (!(parser.get_iss() >> this->soln_vol)) + { + this->soln_vol = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for solution volume.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 24: // species_map + { + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->species_map[s_num] = d; + } + } + opt_save = 24; + } + break; + case 25: // log_gamma_map + { + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->log_gamma_map[s_num] = d; + } + } + opt_save = 25; + } + break; + + case 26: // potential + if (!(parser.get_iss() >> this->potV)) + { + this->potV = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for potential (V).", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + case 27: // log_molalities_map + { + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species molality.", + PHRQ_io::OT_CONTINUE); + } + this->log_molalities_map[s_num] = d; + } + } + opt_save = 27; + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (tc_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ph_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pH not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (pe_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pe not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mu_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ionic strength not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ah2o_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Activity of water not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_h_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total hydrogen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_o_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total oxygen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cb_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge balance not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_alkalinity_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Total alkalinity not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + + // Update activities + if (original_activities.size() > 0) + { + + cxxNameDouble simple_this_totals = this->totals.Simplify_redox(); + cxxNameDouble::iterator it = simple_original_totals.begin(); + for ( ; it != simple_original_totals.end(); it++) + { + cxxNameDouble::iterator jit = simple_this_totals.find(it->first); + if (jit != simple_this_totals.end()) + { + if (it->second > 0 && jit->second > 0.0) + { + LDBLE f = jit->second / it->second; + if (f != 1) + { + original_activities.Multiply_activities_redox(it->first, f); + } + } + } + } + original_activities.merge_redox(this->master_activity); + this->master_activity = original_activities; + } + + return; +} + +void +cxxSolution::Update(LDBLE h_tot, LDBLE o_tot, LDBLE charge, const cxxNameDouble &const_nd) +{ + // H, O, charge, totals, and activities of solution are updated + this->total_h = h_tot; + this->total_o = o_tot; + this->cb = charge; + this->mass_water = o_tot / 55.5; + + // Don`t bother to update activities? + this->Update(const_nd); + //this->totals = const_nd; + cxxNameDouble::iterator it; + for (it = this->totals.begin(); it != this->totals.end(); it++) + { + if (it->second < 1e-25) + { + it->second = 0.0; + } + } +} +void +cxxSolution::Update_activities(const cxxNameDouble &original_tot) +{ + // Totals and activities of solution are updated + // nd does not have H, O, charge + cxxNameDouble simple_original = original_tot.Simplify_redox(); + // totals after read + cxxNameDouble simple_new = this->totals.Simplify_redox(); + + // make factors + cxxNameDouble factors; + { + cxxNameDouble::iterator it = simple_new.begin(); + cxxNameDouble::iterator jit = simple_original.begin(); + while (it != simple_new.end() && jit != simple_original.end()) + { + int j = strcmp(it->first.c_str(), jit->first.c_str()); + if (j < 0) + { + it++; + } + else if (j == 0) + { + if (jit->second != it->second) + { + if (it->second > 0 && jit->second > 0) + { + factors[it->first] = log10(jit->second / it->second); + } + } + it++; + jit++; + } + else + { + jit++; + } + } + + + // simple_new now has factors for master activities + // Now add factors to activities + { + cxxNameDouble::iterator activity_it = this->master_activity.begin(); + cxxNameDouble::iterator factors_it = factors.begin(); + std::string activity_ename; + std::basic_string < char >::size_type indexCh; + while (activity_it != master_activity.end() && factors_it != factors.end()) + { + activity_ename = activity_it->first; + if (activity_ename.size() > 3) + { + indexCh = activity_ename.find("("); + if (indexCh != std::string::npos) + { + activity_ename = activity_ename.substr(0, indexCh); + } + } + int j = strcmp(factors_it->first.c_str(), activity_ename.c_str()); + if (j < 0) + { + factors_it++; + } + else if (j == 0) + { + activity_it->second += factors_it->second; + activity_it++; + } + else + { + activity_it++; + } + } + } + } +} +void +cxxSolution::Update(const cxxNameDouble &const_nd) +{ + // const_nd is a list of new totals, assumed to be inclusive of all elements + // Totals and activities of solution are updated + // nd does not have H, O, charge + cxxNameDouble simple_original = this->totals.Simplify_redox(); + cxxNameDouble simple_new = const_nd.Simplify_redox(); + + cxxNameDouble factors; + { + // make factors + cxxNameDouble::iterator it = simple_new.begin(); + cxxNameDouble::iterator jit = simple_original.begin(); + while (it != simple_new.end() && jit != simple_original.end()) + { + int j = strcmp(it->first.c_str(), jit->first.c_str()); + if (j < 0) + { + it++; + } + else if (j == 0) + { + if (jit->second != it->second) + { + if (it->second > 0 && jit->second > 0) + { + factors[it->first] = log10(it->second / jit->second); + } + } + it++; + jit++; + } + else + { + jit++; + } + } + // simple_new now has factors for master activities + // Now add log factors to log activities + { + cxxNameDouble::iterator activity_it = this->master_activity.begin(); + cxxNameDouble::iterator factors_it = factors.begin(); + std::string activity_ename; + std::basic_string < char >::size_type indexCh; + while (activity_it != master_activity.end() && factors_it != factors.end()) + { + activity_ename = activity_it->first; + if (factors_it->first[0] < activity_ename[0]) + { + factors_it++; + continue; + } + else if (factors_it->first[0] > activity_ename[0]) + { + activity_it++; + continue; + } + if (activity_ename.size() > 3) + { + indexCh = activity_ename.find("("); + if (indexCh != std::string::npos) + { + activity_ename = activity_ename.substr(0, indexCh); + } + } + int j = strcmp(factors_it->first.c_str(), activity_ename.c_str()); + if (j < 0) + { + factors_it++; + } + else if (j == 0) + { + activity_it->second += factors_it->second; + activity_it++; + } + else + { + activity_it++; + } + } + } + } + + // update totals + this->totals = simple_new; +} +void +cxxSolution::zero() +{ + this->tc = 0.0; + this->ph = 0.0; + this->pe = 0.0; + this->mu = 0.0; + this->ah2o = 0.0; + this->total_h = 0.0; + this->total_o = 0.0; + this->cb = 0.0; + this->density = 1.0; + this->mass_water = 0.0; + this->soln_vol = 0.0; + this->total_alkalinity = 0.0; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + this->master_activity.type = cxxNameDouble::ND_SPECIES_LA; + this->species_gamma.type = cxxNameDouble::ND_SPECIES_GAMMA; + this->patm = 1.0; + this->potV = 0.0; + this->initial_data = NULL; +} + +void +cxxSolution::add(const cxxSolution & addee, LDBLE extensive) + // + // Add existing solution to "this" solution + // +{ + if (extensive == 0.0) + return; + LDBLE ext1 = this->mass_water; + LDBLE ext2 = addee.mass_water * extensive; + LDBLE f1 = ext1 / (ext1 + ext2); + LDBLE f2 = ext2 / (ext1 + ext2); + this->tc = f1 * this->tc + f2 * addee.tc; + this->ph = f1 * this->ph + f2 * addee.ph; + this->pe = f1 * this->pe + f2 * addee.pe; + this->mu = f1 * this->mu + f2 * addee.mu; + this->ah2o = f1 * this->mu + f2 * addee.ah2o; + this->total_h += addee.total_h * extensive; + this->total_o += addee.total_o * extensive; + this->cb += addee.cb * extensive; + this->density = f1 * this->density + f2 * addee.density; + this->patm = f1 * this->patm + f2 * addee.patm; + // this->potV = f1 * this->potV + f2 * addee.potV; // appt + this->mass_water += addee.mass_water * extensive; + this->soln_vol += addee.soln_vol * extensive; + this->total_alkalinity += addee.total_alkalinity * extensive; + this->totals.add_extensive(addee.totals, extensive); + this->master_activity.add_log_activities(addee.master_activity, f1, f2); + this->species_gamma.add_intensive(addee.species_gamma, f1, f2); + this->Add_isotopes(addee.isotopes, f2, extensive); + { + // Add species + std::map::const_iterator it = addee.species_map.begin(); + for ( ; it != addee.species_map.end(); it++) + { + if (this->species_map.find(it->first) != this->species_map.end()) + { + this->species_map[it->first] = this->species_map[it->first] * f1 + it->second * f2; + } + else + { + this->species_map[it->first] = it->second; + } + } + // Add gammas + std::map::const_iterator git = addee.log_gamma_map.begin(); + for ( ; git != addee.log_gamma_map.end(); git++) + { + if (this->log_gamma_map.find(git->first) != this->log_gamma_map.end()) + { + this->log_gamma_map[git->first] = this->log_gamma_map[git->first] * f1 + git->second * f2; + } + else + { + this->log_gamma_map[git->first] = git->second; + } + } + // Add molalities + std::map::const_iterator mit = addee.log_molalities_map.begin(); + for (; mit != addee.log_molalities_map.end(); mit++) + { + if (this->log_molalities_map.find(mit->first) != this->log_molalities_map.end()) + { + this->log_molalities_map[mit->first] = this->log_molalities_map[mit->first] * f1 + mit->second * f2; + } + else + { + this->log_molalities_map[mit->first] = mit->second; + } + } + } +} + +void +cxxSolution::multiply(LDBLE extensive) + // + // Multiply existing solution by extensive + // +{ + if (extensive == 0.0 || extensive == 1.0) + return; + this->total_h *= extensive; + this->total_o *= extensive; + this->cb *= extensive; + this->mass_water *= extensive; + this->soln_vol *= extensive; + this->total_alkalinity *= extensive; + this->totals.multiply(extensive); + this->Multiply_isotopes(extensive); +} + +LDBLE +cxxSolution::Get_total(const char *string) const +{ + cxxNameDouble::const_iterator it = this->totals.find(string); + if (it == this->totals.end()) + { + return (0.0); + } + else + { + return (it->second); + } +} + +void +cxxSolution::Set_total(char *string, LDBLE d) +{ + this->totals[string] = d; +} + +LDBLE +cxxSolution::Get_master_activity(char *string) const +{ + cxxNameDouble::const_iterator it = this->master_activity.find(string); + if (it == this->master_activity.end()) + { + return (0.0); + } + else + { + return (it->second); + } +} + +void +cxxSolution::Set_master_activity(char *string, LDBLE d) +{ + this->master_activity[string] = d; +} +void +cxxSolution::Add_isotopes(const std::map < std::string, cxxSolutionIsotope > & old, LDBLE intensive, LDBLE extensive) +{ + for (std::map < std::string, cxxSolutionIsotope >::const_iterator itold = old.begin(); itold != old.end(); ++itold) + { + std::map < std::string, cxxSolutionIsotope >::iterator it_this; + it_this = this->isotopes.find(itold->first); + if (it_this != this->isotopes.end()) + { + LDBLE t = it_this->second.Get_total(); + t += itold->second.Get_total() * extensive; + it_this->second.Set_total(t); + + t = it_this->second.Get_ratio(); + t += itold->second.Get_ratio() * intensive; + it_this->second.Set_ratio(t); + + t = it_this->second.Get_ratio_uncertainty(); + t += itold->second.Get_ratio_uncertainty() * intensive; + it_this->second.Set_ratio_uncertainty(t); + it_this->second.Set_ratio_uncertainty_defined(it_this->second.Get_ratio_uncertainty_defined() + || itold->second.Get_ratio_uncertainty_defined()); + } + else + { + cxxSolutionIsotope iso(itold->second); + iso.Set_total(itold->second.Get_total() * extensive); + this->Get_isotopes()[iso.Get_isotope_name()] = iso; + } + } +} +void +cxxSolution::Multiply_isotopes(LDBLE extensive) +{ + std::map < std::string, cxxSolutionIsotope>::iterator it; + for (it = this->isotopes.begin(); it != this->isotopes.end(); it++) + { + LDBLE total = it->second.Get_total(); + total *= extensive; + it->second.Set_total(total); + } +} + +/* ---------------------------------------------------------------------- */ +void +cxxSolution::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of list of ints and doubles from solution structure + * This list is not the complete structure, but only enough + * for batch-reaction, advection, and transport calculations + */ + ints.push_back(this->n_user); + ints.push_back(this->new_def ? 1 : 0); + doubles.push_back(this->patm); + doubles.push_back(this->potV); + doubles.push_back(this->tc); + doubles.push_back(this->ph); + doubles.push_back(this->pe); + doubles.push_back(this->mu); + doubles.push_back(this->ah2o); + doubles.push_back(this->total_h); + doubles.push_back(this->total_o); + doubles.push_back(this->cb); + doubles.push_back(this->mass_water); + doubles.push_back(this->density); + doubles.push_back(this->soln_vol); + doubles.push_back(this->total_alkalinity); +/* + * struct conc *totals; +*/ + this->totals.Serialize(dictionary, ints, doubles); +/* + * struct master_activity *master_activity; + */ + this->master_activity.Serialize(dictionary, ints, doubles); +/* + * struct master_activity *species_gamma + */ + this->species_gamma.Serialize(dictionary, ints, doubles); +/* + * isotopes + */ + ints.push_back((int) isotopes.size()); + { + std::map < std::string, cxxSolutionIsotope >::iterator it; + for (it = isotopes.begin(); it != isotopes.end(); it++) + { + ints.push_back(dictionary.Find(it->first)); + it->second.Serialize(dictionary, ints, doubles); + } + } +/* + * species_map + */ + ints.push_back((int) species_map.size()); + { + std::map < int, double >::iterator it; + for (it = species_map.begin(); it != species_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +/* + * log_gamma_map + */ + ints.push_back((int) log_gamma_map.size()); + { + std::map < int, double >::iterator it; + for (it = log_gamma_map.begin(); it != log_gamma_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } + /* + * log_molalities_map + */ + ints.push_back((int)log_molalities_map.size()); + { + std::map < int, double >::iterator it; + for (it = log_molalities_map.begin(); it != log_molalities_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +} + +/* ---------------------------------------------------------------------- */ +void +cxxSolution::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->new_def = (ints[ii++] != 0); + this->patm = doubles[dd++]; + this->potV = doubles[dd++]; + this->tc = doubles[dd++]; + this->ph = doubles[dd++]; + this->pe = doubles[dd++]; + this->mu = doubles[dd++]; + this->ah2o = doubles[dd++]; + this->total_h = doubles[dd++]; + this->total_o = doubles[dd++]; + this->cb = doubles[dd++]; + this->mass_water = doubles[dd++]; + this->density = doubles[dd++]; + this->soln_vol = doubles[dd++]; + this->total_alkalinity = doubles[dd++]; +/* + * struct conc *totals; +*/ + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * struct master_activity *master_activity; + */ + this->master_activity.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * struct master_activity *species_gamma; + */ + this->species_gamma.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * isotopes + */ + { + isotopes.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + std::string str = dictionary.GetWords()[ints[ii++]]; + cxxSolutionIsotope iso; + iso.Deserialize(dictionary, ints, doubles, ii, dd); + isotopes[str] = iso; + } + } +/* + * species_map + */ + { + species_map.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + species_map[ints[ii++]] = doubles[dd++]; + } + } +/* + * log_gamma_map + */ + { + log_gamma_map.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + log_gamma_map[ints[ii++]] = doubles[dd++]; + } + } + /* + * log_molalities_map + */ + { + log_molalities_map.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + log_molalities_map[ints[ii++]] = doubles[dd++]; + } + } +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("totals"), // 0 + std::vector< std::string >::value_type("activities"), // 1 + std::vector< std::string >::value_type("gammas"), // 2 + std::vector< std::string >::value_type("isotopes"), // 3 + std::vector< std::string >::value_type("temp"), // 4 + std::vector< std::string >::value_type("tc_avoid_conflict_with_technetium"), // 5 + std::vector< std::string >::value_type("temperature"), // 6 + std::vector< std::string >::value_type("ph"), // 7 + std::vector< std::string >::value_type("pe"), // 8 + std::vector< std::string >::value_type("mu"), // 9 + std::vector< std::string >::value_type("ionic_strength"), // 10 + std::vector< std::string >::value_type("ah2o"), // 11 + std::vector< std::string >::value_type("activity_water"), // 12 + std::vector< std::string >::value_type("total_h"), // 13 + std::vector< std::string >::value_type("total_o"), // 14 + std::vector< std::string >::value_type("mass_water"), // 15 + std::vector< std::string >::value_type("mass_h2o"), // 16 + std::vector< std::string >::value_type("total_alkalinity"), // 17 + std::vector< std::string >::value_type("total_alk"), // 18 + std::vector< std::string >::value_type("cb"), // 19 + std::vector< std::string >::value_type("charge_balance"), // 20 + std::vector< std::string >::value_type("density"), // 21 + std::vector< std::string >::value_type("pressure"), // 22 + std::vector< std::string >::value_type("soln_vol"), // 23 + std::vector< std::string >::value_type("species_map"), // 24 + std::vector< std::string >::value_type("log_gamma_map"), // 25 + std::vector< std::string >::value_type("potential"), // 26 + std::vector< std::string >::value_type("log_molalities_map") // 27 +}; +const std::vector< std::string > cxxSolution::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Solution.h b/phreeqcpp/Solution.h new file mode 100644 index 00000000..cecabe78 --- /dev/null +++ b/phreeqcpp/Solution.h @@ -0,0 +1,148 @@ +#if !defined(SOLUTION_H_INCLUDED) +#define SOLUTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::vector +#include +#include "NumKeyword.h" +#include "SolutionIsotope.h" +#include "NameDouble.h" +#include "PHRQ_base.h" +#include "PHRQ_io.h" +#include "ISolution.h" +class cxxMix; + +class cxxSolution:public cxxNumKeyword +{ + + public: + cxxSolution(PHRQ_io *io=NULL); + cxxSolution(const cxxSolution &old_sol); + const cxxSolution & operator = (const cxxSolution &rhs); + cxxSolution(std::map < int, cxxSolution > &solution_map, + cxxMix & mx, int n_user, PHRQ_io *io=NULL); + virtual ~cxxSolution(); + + bool Get_new_def() const {return this->new_def;} + void Set_new_def(bool p) {this->new_def = p;} + LDBLE Get_patm() const {return this->patm;} + void Set_patm(LDBLE p) {this->patm = p;} + LDBLE Get_potV() const {return this->potV;} + void Set_potV(LDBLE p) {this->potV = p;} + LDBLE Get_tc() const {return this->tc;} + void Set_tc(LDBLE l_tc) {this->tc = l_tc;} + LDBLE Get_ph() const {return this->ph;} + void Set_ph(LDBLE pH) {this->ph = pH;} + LDBLE Get_pe() const {return this->pe;} + void Set_pe(LDBLE l_pe) {this->pe = l_pe;} + LDBLE Get_mu() const {return this->mu;} + void Set_mu(LDBLE l_mu) {this->mu = l_mu;} + LDBLE Get_ah2o() const {return this->ah2o;} + void Set_ah2o(LDBLE l_ah2o) {this->ah2o = l_ah2o;} + LDBLE Get_total_h() const {return this->total_h;} + void Set_total_h(LDBLE l_total_h) {this->total_h = l_total_h;} + LDBLE Get_total_o() const {return this->total_o;} + void Set_total_o(LDBLE l_total_o) {this->total_o = l_total_o;} + LDBLE Get_cb() const {return this->cb;} + void Set_cb(LDBLE l_cb) {this->cb = l_cb;} + LDBLE Get_density() const {return this->density;} + void Set_density(LDBLE l_density) {this->density = l_density;} + LDBLE Get_mass_water() const {return this->mass_water;} + void Set_mass_water(LDBLE l_mass_water) {this->mass_water = l_mass_water;} + LDBLE Get_total_alkalinity() const {return this->total_alkalinity;} + void Set_total_alkalinity(LDBLE l_total_alkalinity) {this->total_alkalinity = l_total_alkalinity;} + LDBLE Get_soln_vol() const {return this->soln_vol;} + void Set_soln_vol(LDBLE t) {this->soln_vol = t;} + cxxNameDouble & Get_totals(void) {return this->totals;} + const cxxNameDouble & Get_totals(void)const {return this->totals;} + void Set_totals(cxxNameDouble & nd) + { + this->totals = nd; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + } + cxxNameDouble & Get_master_activity(void) {return this->master_activity;} + cxxNameDouble & Get_species_gamma(void) {return this->species_gamma;} + std::map & Get_species_map(void) {return this->species_map;} + std::map & Get_log_gamma_map(void) {return this->log_gamma_map;} + std::map& Get_log_molalities_map(void) { return this->log_molalities_map; } + std::map < std::string, cxxSolutionIsotope > & Get_isotopes(void) {return this->isotopes;} + const std::map < std::string, cxxSolutionIsotope > & Get_isotopes(void)const {return this->isotopes;} + void Set_isotopes(const std::map < std::string, cxxSolutionIsotope > &iso ) {this->isotopes = iso;} + cxxISolution *Get_initial_data() {return this->initial_data;} + const cxxISolution *Get_initial_data()const {return this->initial_data;} + void Set_initial_data(const cxxISolution * id) + { + if (this->initial_data != NULL) + delete this->initial_data; + this->initial_data = new cxxISolution(*id); + } + void Create_initial_data() + { + if (this->initial_data != NULL) + delete initial_data; + initial_data = new cxxISolution; + } + void Destroy_initial_data() + { + if (this->initial_data != NULL) + delete initial_data; + initial_data = NULL; + } + + void clear_totals() {this->totals.clear();} + void clear_master_activity() {this->master_activity.clear();} + + void zero(); + void add(const cxxSolution & addee, LDBLE extensive); + void Add_isotopes(const std::map < std::string, cxxSolutionIsotope > &old, LDBLE intensive, LDBLE extensive); + void multiply(LDBLE extensive); + void Multiply_isotopes(LDBLE extensive); + // not checked + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + LDBLE Get_master_activity(char *string) const; + void Set_master_activity(char *string, LDBLE value); + LDBLE Get_total(const char *string) const; + LDBLE Get_total_element(const char *string) const; + void Set_total(char *string, LDBLE value); + void read_raw(CParser & parser, bool check = true); + //void modify_activities(const cxxSolution & original); + //void Simplify_totals(); + void Update(const cxxNameDouble &nd); + void Update(LDBLE h_tot, LDBLE o_tot, LDBLE charge, const cxxNameDouble &nd); + void Update_activities(const cxxNameDouble &original_tot); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + bool new_def; + LDBLE patm; + LDBLE potV; + LDBLE tc; + LDBLE ph; + LDBLE pe; + LDBLE mu; + LDBLE ah2o; + LDBLE total_h; + LDBLE total_o; + LDBLE cb; + LDBLE mass_water; + LDBLE density; + LDBLE soln_vol; + LDBLE total_alkalinity; + cxxNameDouble totals; + cxxNameDouble master_activity; + cxxNameDouble species_gamma; + //cxxSolutionIsotopeList isotopes; + std::map < std::string, cxxSolutionIsotope > isotopes; + cxxISolution *initial_data; + const static std::vector < std::string > vopts; + std::map species_map; + std::map log_gamma_map; + std::map log_molalities_map; +}; + +#endif // !defined(SOLUTION_H_INCLUDED) diff --git a/phreeqcpp/SolutionIsotope.cxx b/phreeqcpp/SolutionIsotope.cxx new file mode 100644 index 00000000..78d832a5 --- /dev/null +++ b/phreeqcpp/SolutionIsotope.cxx @@ -0,0 +1,337 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // std::ostrstream + +#include "Utils.h" +#include "Phreeqc.h" +#include "SolutionIsotope.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +cxxSolutionIsotope::cxxSolutionIsotope(PHRQ_io *io) +: +PHRQ_base(io), +isotope_number(0.0) +{ + isotope_number = 0; + elt_name.clear(); + isotope_name.clear(); + total = 0; + ratio = -9999.9; + ratio_uncertainty = 1; + ratio_uncertainty_defined = false; + x_ratio_uncertainty = 0; + coef = 0; +} +cxxSolutionIsotope::~cxxSolutionIsotope(void) +{ +} + +void +cxxSolutionIsotope::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + s_oss << indent0; + s_oss << " + isotope_number << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_elt_name=\"" << this->elt_name << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_isotope_name=\"" << this->isotope_name << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_total=\"" << this->total << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_ratio=\"" << this->ratio << "\"" << "\n"; + +#ifdef NPP + if (!isnan(this->ratio_uncertainty)) +#else + if (this->ratio_uncertainty != NAN) +#endif + { + s_oss << indent1; + s_oss << "iso_ratio_uncertainty=\"" << this-> + ratio_uncertainty << "\"" << "\n"; + } + s_oss << indent0; + s_oss << "\">" << "\n"; +} + +void +cxxSolutionIsotope::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + + std::string indent0(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + std::string indent1 = indent0; + indent1.append(Utilities::INDENT); + + s_oss << indent0; + s_oss << indent0 << "-isotope_number " << this->isotope_number << "\n"; + s_oss << indent0 << "-elt_name " << this->elt_name << "\n"; + s_oss << indent0 << "-total " << this->total << "\n"; + s_oss << indent0 << "-ratio " << this->ratio << "\n"; + s_oss << indent0 << "-ratio_uncertainty_defined " << this->ratio_uncertainty_defined << "\n"; + if (this->ratio_uncertainty_defined) + { + s_oss << indent0 << "-ratio_uncertainty " << this->ratio_uncertainty << "\n"; + } + s_oss << indent0 << "-x_ratio_uncertainty " << this->x_ratio_uncertainty << "\n"; + s_oss << indent0 << "-coef " << this->coef << "\n"; +} +void cxxSolutionIsotope::read_raw(CParser & parser, bool check ) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool isotope_number_defined(false); + bool elt_name_defined(false); + bool total_defined(false); + bool ratio_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + opt_save = CParser::OPT_DEFAULT; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in isotopes of SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // isotope_number + if (!(parser.get_iss() >> this->isotope_number)) + { + this->isotope_number = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for isotope_number.", + PHRQ_io::OT_CONTINUE); + } + isotope_number_defined = true; + break; + + case 1: // elt_name + if (!(parser.get_iss() >> this->elt_name)) + { + this->elt_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected character value for elt_name.", + PHRQ_io::OT_CONTINUE); + } + elt_name_defined = true; + break; + + case 2: // total + if (!(parser.get_iss() >> this->total)) + { + this->total = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total.", + PHRQ_io::OT_CONTINUE); + } + total_defined = true; + break; + + case 3: // ratio + if (!(parser.get_iss() >> this->ratio)) + { + this->ratio = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ratio.", + PHRQ_io::OT_CONTINUE); + } + total_defined = true; + break; + + case 4: // ratio_uncertainty_defined + if (!(parser.get_iss() >> this->ratio_uncertainty_defined)) + { + this->ratio_uncertainty_defined = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for ratio_uncertainty_defined.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // ratio_uncertainty + if (!(parser.get_iss() >> this->ratio_uncertainty)) + { + this->ratio_uncertainty = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ratio_uncertainty.", + PHRQ_io::OT_CONTINUE); + } + ratio_uncertainty_defined = true; + break; + + case 6: // x_ratio_uncertainty + if (!(parser.get_iss() >> this->x_ratio_uncertainty)) + { + this->x_ratio_uncertainty = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for x_ratio_uncertainty.", + PHRQ_io::OT_CONTINUE); + } + break; + case 7: // coef + if (!(parser.get_iss() >> this->coef)) + { + this->coef = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for coef.", + PHRQ_io::OT_CONTINUE); + } + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (isotope_number_defined == false) + { + parser.incr_input_error(); + parser.error_msg("isotope_number not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (elt_name_defined == false) + { + parser.incr_input_error(); + parser.error_msg("elt_name not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_defined == false) + { + parser.incr_input_error(); + parser.error_msg("total not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ratio_defined == false) + { + parser.incr_input_error(); + parser.error_msg("ratio not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ratio_uncertainty_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("ratio_uncertainty not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + return; +} +bool +cxxSolutionIsotope::operator<(const cxxSolutionIsotope & isotope) const +{ + int i = Utilities::strcmp_nocase(this->elt_name.c_str(), isotope.elt_name.c_str()); + if (i != 0) + return (i < 0); + return (this->isotope_number < isotope.isotope_number); +} + +void +cxxSolutionIsotope::add(const cxxSolutionIsotope & isotope_ptr, + LDBLE intensive, LDBLE extensive) +{ + if ((this->isotope_number == isotope_ptr.isotope_number) && + (this->elt_name == isotope_ptr.elt_name) && + (this->isotope_name == isotope_ptr.isotope_name)) + { + this->total += isotope_ptr.total * extensive; + this->ratio += isotope_ptr.ratio * intensive; + this->ratio_uncertainty += isotope_ptr.ratio_uncertainty * intensive; + this->ratio_uncertainty_defined = (this->ratio_uncertainty_defined + || isotope_ptr. + ratio_uncertainty_defined); + } +} +void +cxxSolutionIsotope::multiply(LDBLE extensive) +{ + this->total *= extensive; +} +void +cxxSolutionIsotope::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + doubles.push_back(this->isotope_number); + ints.push_back(dictionary.Find(this->elt_name)); + ints.push_back(dictionary.Find(this->isotope_name)); + doubles.push_back(this->total); + doubles.push_back(this->ratio); + doubles.push_back(this->ratio_uncertainty); + ints.push_back(this->ratio_uncertainty_defined ? 1 : 0); + doubles.push_back(this->x_ratio_uncertainty); + doubles.push_back(this->coef); +} +void +cxxSolutionIsotope::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->isotope_number = doubles[dd++]; + this->elt_name = dictionary.GetWords()[ints[ii++]]; + this->isotope_name = dictionary.GetWords()[ints[ii++]]; + this->total = doubles[dd++]; + this->ratio = doubles[dd++]; + this->ratio_uncertainty = doubles[dd++]; + this->ratio_uncertainty_defined = (ints[ii++] != 0); + this->x_ratio_uncertainty = doubles[dd++]; + this->coef = doubles[dd++]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("isotope_number"), // 0 + std::vector< std::string >::value_type("elt_name"), // 1 + std::vector< std::string >::value_type("total"), // 2 + std::vector< std::string >::value_type("ratio"), // 3 + std::vector< std::string >::value_type("ratio_uncertainty_defined"), // 4 + std::vector< std::string >::value_type("ratio_uncertainty"), // 5 + std::vector< std::string >::value_type("x_ratio_uncertainty"), // 6 + std::vector< std::string >::value_type("coef") // 7 +}; +const std::vector< std::string > cxxSolutionIsotope::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SolutionIsotope.h b/phreeqcpp/SolutionIsotope.h new file mode 100644 index 00000000..13fdd041 --- /dev/null +++ b/phreeqcpp/SolutionIsotope.h @@ -0,0 +1,85 @@ +#if !defined(SOLUTIONISOTOPE_H_INCLUDED) +#define SOLUTIONISOTOPE_H_INCLUDED + +#include // std::ostream +#include // std::string +#include // std::list +#include "Parser.h" +class Dictionary; + +class cxxSolutionIsotope: public PHRQ_base +{ + public: + cxxSolutionIsotope(PHRQ_io *io=NULL); + cxxSolutionIsotope(class isotope *isotope_ptr, PHRQ_io *io=NULL); + virtual ~cxxSolutionIsotope(void); + + void dump_xml(std::ostream & os, unsigned int indent) const; + void dump_raw(std::ostream & os, unsigned int indent) const; + + //CParser::STATUS_TYPE read_raw(CParser & parser, std::istream::pos_type next_char); + void read_raw(CParser & parser, bool check); + + LDBLE Get_isotope_number() const + { + return this->isotope_number; + } + void Set_isotope_number(LDBLE d) + { + this->isotope_number = d; + } + const std::string &Get_elt_name() const {return this->elt_name;} + void Set_elt_name(const char *cstring) + { + if (cstring != NULL) + this->elt_name = std::string(cstring); + else + this->elt_name.clear(); + } + + const std::string &Get_isotope_name() const {return this->isotope_name;} + void Set_isotope_name(const char *cstring) + { + if (cstring != NULL) + this->isotope_name = std::string(cstring); + else + this->isotope_name.clear(); + } + + LDBLE Get_total() const {return this->total;} + void Set_total(LDBLE d) {this->total = d;} + + LDBLE Get_ratio() const {return this->ratio;} + void Set_ratio(LDBLE t) {this->ratio = t;} + + LDBLE Get_ratio_uncertainty() const {return this->ratio_uncertainty;} + void Set_ratio_uncertainty(LDBLE t) {this->ratio_uncertainty = t;} + + LDBLE Get_x_ratio_uncertainty() const {return this->x_ratio_uncertainty;} + void Set_x_ratio_uncertainty(LDBLE t) {this->x_ratio_uncertainty = t;} + + bool Get_ratio_uncertainty_defined() const {return this->ratio_uncertainty_defined;} + void Set_ratio_uncertainty_defined(bool tf) {this->ratio_uncertainty_defined = tf;} + LDBLE Get_coef() const {return this->coef;} + void Set_coef(LDBLE t) {this->coef = t;} + + bool operator<(const cxxSolutionIsotope & conc) const; + void add(const cxxSolutionIsotope & isotope_ptr, LDBLE intensive, + LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + LDBLE isotope_number; + std::string elt_name; + std::string isotope_name; + LDBLE total; + LDBLE ratio; + LDBLE ratio_uncertainty; + bool ratio_uncertainty_defined; + LDBLE x_ratio_uncertainty; + LDBLE coef; /* coefficient of element in phase */ + const static std::vector < std::string > vopts; +}; +#endif // SOLUTIONISOTOPE_H_INCLUDED diff --git a/phreeqcpp/StorageBin.cxx b/phreeqcpp/StorageBin.cxx new file mode 100644 index 00000000..25665a03 --- /dev/null +++ b/phreeqcpp/StorageBin.cxx @@ -0,0 +1,1507 @@ +// StorageBin.cxx: implementation of the cxxStorageBin class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // std::cout std::cerr +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "NameDouble.h" +#include "StorageBin.h" +#include "SSassemblage.h" +#include "Solution.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SS.h" +#include "SSassemblage.h" +#include "Surface.h" +#include "cxxMix.h" +#include "Reaction.h" +#include "Temperature.h" +#include "phqalloc.h" +#include "Use.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +cxxStorageBin::cxxStorageBin(PHRQ_io *io) +: +PHRQ_base(io) +{ + // default constructor for cxxStorageBin + this->system.Set_io(io); + this->system.Initialize(); +} + +cxxStorageBin::cxxStorageBin(cxxUse &use_ref, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->system.Set_io(io); + this->system.Initialize(); + // Solution + if (use_ref.Get_solution_ptr() != NULL) + { + this->Set_Solution(use_ref.Get_solution_ptr()->Get_n_user(), use_ref.Get_solution_ptr()); + } + + // Exchange + if (use_ref.Get_exchange_ptr() != NULL) + { + this->Set_Exchange(use_ref.Get_exchange_ptr()->Get_n_user(), use_ref.Get_exchange_ptr()); + } + + // gas_phase + if (use_ref.Get_gas_phase_ptr() != NULL) + { + this->Set_GasPhase(use_ref.Get_gas_phase_ptr()->Get_n_user(), use_ref.Get_gas_phase_ptr()); + } + + // kinetics + if (use_ref.Get_kinetics_ptr() != NULL) + { + this->Set_Kinetics(use_ref.Get_kinetics_ptr()->Get_n_user(), use_ref.Get_kinetics_ptr()); + } + + // pp_assemblage + if (use_ref.Get_pp_assemblage_ptr() != NULL) + { + this->Set_PPassemblage(use_ref.Get_pp_assemblage_ptr()->Get_n_user(), use_ref.Get_pp_assemblage_ptr()); + } + + // ss_assemblage + if (use_ref.Get_ss_assemblage_ptr() != NULL) + { + this->Set_SSassemblage(use_ref.Get_ss_assemblage_ptr()->Get_n_user(), use_ref.Get_ss_assemblage_ptr()); + } + + // surface + if (use_ref.Get_surface_ptr() != NULL) + { + this->Set_Surface(use_ref.Get_surface_ptr()->Get_n_user(), use_ref.Get_surface_ptr()); + } + + // mix + if (use_ref.Get_mix_ptr() != NULL) + { + this->Set_Mix(use_ref.Get_mix_ptr()->Get_n_user(), use_ref.Get_mix_ptr()); + } + + // reaction + if (use_ref.Get_reaction_ptr() != NULL) + { + this->Set_Reaction(use_ref.Get_reaction_ptr()->Get_n_user(), use_ref.Get_reaction_ptr()); + } + + // reaction temperature + if (use_ref.Get_temperature_ptr() != NULL) + { + this->Set_Temperature(use_ref.Get_temperature_ptr()->Get_n_user(), use_ref.Get_temperature_ptr()); + } + + // reaction pressure + if (use_ref.Get_pressure_ptr() != NULL) + { + this->Set_Pressure(use_ref.Get_pressure_ptr()->Get_n_user(), use_ref.Get_pressure_ptr()); + } +} +cxxStorageBin::~cxxStorageBin() +{ +} +void +cxxStorageBin::Add(cxxStorageBin &src, int n) +{ + // Solution + if (src.Get_Solution(n) != NULL) + { + this->Set_Solution(n, src.Get_Solution(n)); + } + + // Exchange + if (src.Get_Exchange(n) != NULL) + { + this->Set_Exchange(n, src.Get_Exchange(n)); + } + + // gas_phase + if (src.Get_GasPhase(n) != NULL) + { + this->Set_GasPhase(n, src.Get_GasPhase(n)); + } + + // kinetics + if (src.Get_Kinetics(n) != NULL) + { + this->Set_Kinetics(n, src.Get_Kinetics(n)); + } + + // pp_assemblage + if (src.Get_PPassemblage(n) != NULL) + { + this->Set_PPassemblage(n, src.Get_PPassemblage(n)); + } + + // ss_assemblage + if (src.Get_SSassemblage(n) != NULL) + { + this->Set_SSassemblage(n, src.Get_SSassemblage(n)); + } + + // surface + if (src.Get_Surface(n) != NULL) + { + this->Set_Surface(n, src.Get_Surface(n)); + } + + // mix + if (src.Get_Mix(n) != NULL) + { + this->Set_Mix(n, src.Get_Mix(n)); + } + + // reaction + if (src.Get_Reaction(n) != NULL) + { + this->Set_Reaction(n, src.Get_Reaction(n)); + } + + // reaction temperature + if (src.Get_Temperature(n) != NULL) + { + this->Set_Temperature(n, src.Get_Temperature(n)); + } + + // reaction pressure + if (src.Get_Pressure(n) != NULL) + { + this->Set_Pressure(n, src.Get_Pressure(n)); + } +} +void +cxxStorageBin::Add_uz(cxxStorageBin &uzbin) +{ + cxxMix mx; + mx.Add(0, 1.0); + mx.Add(1, 1.0); + + // Solution + + // Exchange + { + std::map::iterator it_uz = uzbin.Get_Exchangers().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_Exchangers().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxExchange >::iterator it_sz = this->Exchangers.find(n_user); + if (it_sz == this->Exchangers.end()) + { + this->Exchangers[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxExchange temp_entity(temp_map, mx, n_user); + this->Exchangers[n_user] = temp_entity; + } + } + } + + // gas_phase + { + std::map::iterator it_uz = uzbin.Get_GasPhases().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_GasPhases().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxGasPhase >::iterator it_sz = this->GasPhases.find(n_user); + if (it_sz == this->GasPhases.end()) + { + this->GasPhases[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxGasPhase temp_entity(temp_map, mx, n_user); + this->GasPhases[n_user] = temp_entity; + } + } + } + + // kinetics + { + std::map::iterator it_uz = uzbin.Get_Kinetics().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_Kinetics().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxKinetics >::iterator it_sz = this->Kinetics.find(n_user); + if (it_sz == this->Kinetics.end()) + { + this->Kinetics[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxKinetics temp_entity(temp_map, mx, n_user); + this->Kinetics[n_user] = temp_entity; + } + } + } + + // pp_assemblage + { + std::map::iterator it_uz = uzbin.Get_PPassemblages().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_PPassemblages().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxPPassemblage >::iterator it_sz = this->PPassemblages.find(n_user); + if (it_sz == this->PPassemblages.end()) + { + this->PPassemblages[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxPPassemblage temp_entity(temp_map, mx, n_user); + this->PPassemblages[n_user] = temp_entity; + } + } + } + + // ss_assemblage + { + std::map::iterator it_uz = uzbin.Get_SSassemblages().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_SSassemblages().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxSSassemblage >::iterator it_sz = this->SSassemblages.find(n_user); + if (it_sz == this->SSassemblages.end()) + { + this->SSassemblages[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxSSassemblage temp_entity(temp_map, mx, n_user); + this->SSassemblages[n_user] = temp_entity; + } + } + } + + // surface + { + std::map::iterator it_uz = uzbin.Get_Surfaces().begin(); + std::map temp_map; + for (; it_uz != uzbin.Get_Surfaces().end(); it_uz++) + { + int n_user = it_uz->second.Get_n_user(); + std::map < int, cxxSurface >::iterator it_sz = this->Surfaces.find(n_user); + if (it_sz == this->Surfaces.end()) + { + this->Surfaces[n_user] = it_uz->second; + } + else + { + temp_map[0] = it_uz->second; + temp_map[1] = it_sz->second; + cxxSurface temp_entity(temp_map, mx, n_user); + this->Surfaces[n_user] = temp_entity; + } + } + } + + // mix + + // reaction + + // reaction temperature + + // reaction pressure +} +void +cxxStorageBin::Copy(int destination, int source) +{ + if (destination == source) + return; + this->Remove(destination); + // Solution + { + std::map < int, cxxSolution >::iterator it = this->Solutions.find(source); + if (it != this->Solutions.end()) + { + this->Set_Solution(destination, &(it->second)); + } + } + + // Exchange + { + std::map < int, cxxExchange >::iterator it = this->Exchangers.find(source); + if (it != this->Exchangers.end()) + { + this->Set_Exchange(destination, &(it->second)); + } + } + + // gas_phase + { + std::map < int, cxxGasPhase >::iterator it = this->GasPhases.find(source); + if (it != this->GasPhases.end()) + { + this->Set_GasPhase(destination, &(it->second)); + } + } + // kinetics + { + std::map < int, cxxKinetics >::iterator it = this->Kinetics.find(source); + if (it != this->Kinetics.end()) + { + this->Set_Kinetics(destination, &(it->second)); + } + } + // pp_assemblage + { + std::map < int, cxxPPassemblage >::iterator it = this->PPassemblages.find(source); + if (it != this->PPassemblages.end()) + { + this->Set_PPassemblage(destination, &(it->second)); + } + } + // ss_assemblage + { + std::map < int, cxxSSassemblage >::iterator it = this->SSassemblages.find(source); + if (it != this->SSassemblages.end()) + { + this->Set_SSassemblage(destination, &(it->second)); + } + } + // surface + { + std::map < int, cxxSurface >::iterator it = this->Surfaces.find(source); + if (it != this->Surfaces.end()) + { + this->Set_Surface(destination, &(it->second)); + } + } + // mix + { + std::map < int, cxxMix >::iterator it = this->Mixes.find(source); + if (it != this->Mixes.end()) + { + this->Set_Mix(destination, &(it->second)); + } + } + // reaction + { + std::map < int, cxxReaction >::iterator it = this->Reactions.find(source); + if (it != this->Reactions.end()) + { + this->Set_Reaction(destination, &(it->second)); + } + } + // reaction temperature + { + std::map < int, cxxTemperature >::iterator it = this->Temperatures.find(source); + if (it != this->Temperatures.end()) + { + this->Set_Temperature(destination, &(it->second)); + } + } + + // reaction pressure + { + this->Set_Pressure(destination, Utilities::Rxn_find(this->Pressures, source)); + } +} + +cxxSolution * +cxxStorageBin::Get_Solution(int n_user) +{ + if (this->Solutions.find(n_user) != this->Solutions.end()) + { + return (&(this->Solutions.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Solution(int n_user, cxxSolution * entity) +{ + if (entity == NULL) + return; + Solutions[n_user] = *entity; + Solutions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Solution(int n_user, cxxSolution & entity) +{ + Solutions[n_user] = entity; + Solutions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Solution(int n_user) +{ + Solutions.erase(n_user); +} + +cxxExchange * +cxxStorageBin::Get_Exchange(int n_user) +{ + if (this->Exchangers.find(n_user) != this->Exchangers.end()) + { + return (&(this->Exchangers.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Exchange(int n_user, cxxExchange * entity) +{ + if (entity == NULL) + return; + Exchangers[n_user] = *entity; + Exchangers.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Exchange(int n_user, cxxExchange & entity) +{ + Exchangers[n_user] = entity; + Exchangers.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Exchange(int n_user) +{ + Exchangers.erase(n_user); +} + +cxxPPassemblage * +cxxStorageBin::Get_PPassemblage(int n_user) +{ + if (this->PPassemblages.find(n_user) != this->PPassemblages.end()) + { + return (&(this->PPassemblages.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_PPassemblage(int n_user, cxxPPassemblage * entity) +{ + if (entity == NULL) + return; + PPassemblages[n_user] = *entity; + PPassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_PPassemblage(int n_user, cxxPPassemblage & entity) +{ + PPassemblages[n_user] = entity; + PPassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_PPassemblage(int n_user) +{ + PPassemblages.erase(n_user); +} + +cxxGasPhase * +cxxStorageBin::Get_GasPhase(int n_user) +{ + if (this->GasPhases.find(n_user) != this->GasPhases.end()) + { + return (&(this->GasPhases.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_GasPhase(int n_user, cxxGasPhase * entity) +{ + if (entity == NULL) + return; + GasPhases[n_user] = *entity; + GasPhases.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_GasPhase(int n_user, cxxGasPhase & entity) +{ + GasPhases[n_user] = entity; + GasPhases.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_GasPhase(int n_user) +{ + GasPhases.erase(n_user); +} + +cxxSSassemblage * +cxxStorageBin::Get_SSassemblage(int n_user) +{ + if (this->SSassemblages.find(n_user) != this->SSassemblages.end()) + { + return (&(this->SSassemblages.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_SSassemblage(int n_user, cxxSSassemblage * entity) +{ + if (entity == NULL) + return; + SSassemblages[n_user] = *entity; + SSassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_SSassemblage(int n_user, cxxSSassemblage & entity) +{ + SSassemblages[n_user] = entity; + SSassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_SSassemblage(int n_user) +{ + SSassemblages.erase(n_user); +} + +cxxKinetics * +cxxStorageBin::Get_Kinetics(int n_user) +{ + if (this->Kinetics.find(n_user) != this->Kinetics.end()) + { + return (&(this->Kinetics.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Kinetics(int n_user, cxxKinetics * entity) +{ + if (entity == NULL) + return; + Kinetics[n_user] = *entity; + Kinetics.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Kinetics(int n_user, cxxKinetics & entity) +{ + Kinetics[n_user] = entity; + Kinetics.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Kinetics(int n_user) +{ + Kinetics.erase(n_user); +} + +cxxSurface * +cxxStorageBin::Get_Surface(int n_user) +{ + if (this->Surfaces.find(n_user) != this->Surfaces.end()) + { + return (&(this->Surfaces.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Surface(int n_user, cxxSurface * entity) +{ + if (entity == NULL) + return; + Surfaces[n_user] = *entity; + Surfaces.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Surface(int n_user, cxxSurface & entity) +{ + Surfaces[n_user] = entity; + Surfaces.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Surface(int n_user) +{ + Surfaces.erase(n_user); +} + +cxxMix * +cxxStorageBin::Get_Mix(int n_user) +{ + if (this->Mixes.find(n_user) != this->Mixes.end()) + { + return (&(this->Mixes.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Mix(int n_user, cxxMix * entity) +{ + if (entity == NULL) + return; + Mixes[n_user] = *entity; + Mixes.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Mix(int n_user, cxxMix & entity) +{ + Mixes[n_user] = entity; + Mixes.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Mix(int n_user) +{ + Mixes.erase(n_user); +} + +cxxReaction * +cxxStorageBin::Get_Reaction(int n_user) +{ + if (this->Reactions.find(n_user) != this->Reactions.end()) + { + return (&(this->Reactions.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Reaction(int n_user, cxxReaction * entity) +{ + if (entity == NULL) + return; + Reactions[n_user] = *entity; + Reactions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Reaction(int n_user, cxxReaction & entity) +{ + Reactions[n_user] = entity; + Reactions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Reaction(int n_user) +{ + Reactions.erase(n_user); +} + +cxxTemperature * +cxxStorageBin::Get_Temperature(int n_user) +{ + if (this->Temperatures.find(n_user) != this->Temperatures.end()) + { + return (&(this->Temperatures.find(n_user)->second)); + } + return (NULL); +} + +void +cxxStorageBin::Set_Temperature(int n_user, cxxTemperature * entity) +{ + if (entity == NULL) + return; + Temperatures[n_user] = *entity; + Temperatures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Temperature(int n_user, cxxTemperature & entity) +{ + Temperatures[n_user] = entity; + Temperatures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Temperature(int n_user) +{ + Temperatures.erase(n_user); +} + +cxxPressure * +cxxStorageBin::Get_Pressure(int n_user) +{ + return Utilities::Rxn_find(this->Pressures, n_user); +} + +void +cxxStorageBin::Set_Pressure(int n_user, cxxPressure * entity) +{ + if (entity == NULL) + return; + Pressures[n_user] = *entity; + Pressures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Pressure(int n_user, cxxPressure & entity) +{ + Pressures[n_user] = entity; + Pressures.find(n_user)->second.Set_n_user_both(n_user); +} +void + +cxxStorageBin::Remove_Pressure(int n_user) +{ + Pressures.erase(n_user); +} + +std::map < int, cxxSolution > & +cxxStorageBin::Get_Solutions() +{ + return this->Solutions; +} +std::map < int, cxxExchange > & +cxxStorageBin::Get_Exchangers() +{ + return this->Exchangers; +} +std::map < int, cxxGasPhase > & +cxxStorageBin::Get_GasPhases() +{ + return this->GasPhases; +} +std::map < int, cxxKinetics > & +cxxStorageBin::Get_Kinetics() +{ + return this->Kinetics; +} +std::map < int, cxxPPassemblage > & +cxxStorageBin::Get_PPassemblages() +{ + return this->PPassemblages; +} +std::map < int, cxxSSassemblage > & +cxxStorageBin::Get_SSassemblages() +{ + return this->SSassemblages; +} +std::map < int, cxxSurface > & +cxxStorageBin::Get_Surfaces() +{ + return this->Surfaces; +} +std::map < int, cxxMix > & +cxxStorageBin::Get_Mixes() +{ + return this->Mixes; +} +std::map < int, cxxReaction > & +cxxStorageBin::Get_Reactions() +{ + return this->Reactions; +} +std::map < int, cxxTemperature > & +cxxStorageBin::Get_Temperatures() +{ + return this->Temperatures; +} +std::map < int, cxxPressure > & +cxxStorageBin::Get_Pressures() +{ + return this->Pressures; +} +void +cxxStorageBin::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + // Dump all data + s_oss.precision(DBL_DIG - 1); + + // Solutions + Utilities::Rxn_dump_raw(Solutions, s_oss, indent); + + // Exchange + Utilities::Rxn_dump_raw(Exchangers, s_oss, indent); + + // Gas Phases + Utilities::Rxn_dump_raw(GasPhases, s_oss, indent); + + // Kinetics + Utilities::Rxn_dump_raw(Kinetics, s_oss, indent); + + // PPassemblage + Utilities::Rxn_dump_raw(PPassemblages, s_oss, indent); + + // SSassemblage + Utilities::Rxn_dump_raw(SSassemblages, s_oss, indent); + + // Surface + Utilities::Rxn_dump_raw(Surfaces, s_oss, indent); + + // Mix + Utilities::Rxn_dump_raw(Mixes, s_oss, indent); + + // Reactions + Utilities::Rxn_dump_raw(Reactions, s_oss, indent); + + // Temperature + Utilities::Rxn_dump_raw(Temperatures, s_oss, indent); +} + +void +cxxStorageBin::dump_raw(std::ostream & s_oss, int n, unsigned int indent, int *n_out) +{ + // Dump one user number, optionally change number from n to n_out + int n_user_local = (n_out != NULL) ? *n_out : n; + s_oss.precision(DBL_DIG - 1); + + // Solutions + if (this->Get_Solution(n) != NULL) + { + this->Get_Solution(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Exchange + if (this->Get_Exchange(n) != NULL) + { + this->Get_Exchange(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Gas Phases + if (this->Get_GasPhase(n) != NULL) + { + this->Get_GasPhase(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Kinetics + if (this->Get_Kinetics(n) != NULL) + { + this->Get_Kinetics(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // PPassemblage + if (this->Get_PPassemblage(n) != NULL) + { + this->Get_PPassemblage(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // SSassemblage + if (this->Get_SSassemblage(n) != NULL) + { + this->Get_SSassemblage(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Surface + if (this->Get_Surface(n) != NULL) + { + this->Get_Surface(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Mix + if (this->Get_Mix(n) != NULL) + { + this->Get_Mix(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Reaction + if (this->Get_Reaction(n) != NULL) + { + this->Get_Reaction(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Temperature + if (this->Get_Temperature(n) != NULL) + { + this->Get_Temperature(n)->dump_raw(s_oss, indent, &n_user_local); + } +} + +void +cxxStorageBin::dump_raw_range(std::ostream & s_oss, int start, int end, unsigned int indent) const +{ + // Dump all data + s_oss.precision(DBL_DIG - 1); + + // Solutions + Utilities::Rxn_dump_raw_range(Solutions, s_oss, start, end, indent); + + // Exchange + Utilities::Rxn_dump_raw_range(Exchangers, s_oss, start, end, indent); + + // Gas Phases + Utilities::Rxn_dump_raw_range(GasPhases, s_oss, start, end, indent); + + // Kinetics + Utilities::Rxn_dump_raw_range(Kinetics, s_oss, start, end, indent); + + // PPassemblage + Utilities::Rxn_dump_raw_range(PPassemblages, s_oss, start, end, indent); + + // SSassemblage + Utilities::Rxn_dump_raw_range(SSassemblages, s_oss, start, end, indent); + + // Surface + Utilities::Rxn_dump_raw_range(Surfaces, s_oss, start, end, indent); + + // Mix + Utilities::Rxn_dump_raw_range(Mixes, s_oss, start, end, indent); + + // Reactions + Utilities::Rxn_dump_raw_range(Reactions, s_oss, start, end, indent); + + // Temperature + Utilities::Rxn_dump_raw_range(Temperatures, s_oss, start, end, indent); +} + +void +cxxStorageBin::read_raw(CParser & parser) +{ + PHRQ_io::LINE_TYPE i; + while ((i = + parser.check_line("StorageBin read_raw", false, true, true, + true)) != PHRQ_io::LT_KEYWORD) + { + if (i == PHRQ_io::LT_EOF) + return; // PHRQ_io::LT_EOF; + } + + for (;;) + { + switch (parser.next_keyword()) + { + case Keywords::KEY_END: + case Keywords::KEY_NONE: + goto END_OF_SIMULATION_INPUT; + break; + case Keywords::KEY_SOLUTION_RAW: + { + cxxSolution entity(this->Get_io()); + entity.read_raw(parser); + Solutions[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_SOLUTION_MODIFY: + { + Utilities::SB_read_modify(this->Solutions, parser); + } + break; + case Keywords::KEY_EXCHANGE_RAW: + { + cxxExchange entity(this->Get_io()); + entity.read_raw(parser); + Exchangers[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_EXCHANGE_MODIFY: + { + Utilities::SB_read_modify(this->Exchangers, parser); + } + break; + case Keywords::KEY_GAS_PHASE_RAW: + { + cxxGasPhase entity(this->Get_io()); + entity.read_raw(parser); + GasPhases[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_GAS_PHASE_MODIFY: + { + Utilities::SB_read_modify(this->GasPhases, parser); + } + break; + case Keywords::KEY_KINETICS_RAW: + { + cxxKinetics entity(this->Get_io()); + entity.read_raw(parser); + Kinetics[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_KINETICS_MODIFY: + { + Utilities::SB_read_modify(this->Kinetics, parser); + } + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + { + cxxPPassemblage entity(this->Get_io()); + entity.read_raw(parser); + PPassemblages[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY: + { + Utilities::SB_read_modify(this->PPassemblages, parser); + } + break; + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + { + cxxSSassemblage entity(this->Get_io()); + entity.read_raw(parser); + SSassemblages[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_SOLID_SOLUTIONS_MODIFY: + { + Utilities::SB_read_modify(this->SSassemblages, parser); + } + break; + case Keywords::KEY_SURFACE_RAW: + { + cxxSurface entity(this->Get_io()); + entity.read_raw(parser); + Surfaces[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_SURFACE_MODIFY: + { + Utilities::SB_read_modify(this->Surfaces, parser); + } + break; + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + { + cxxTemperature entity(this->Get_io()); + entity.read_raw(parser); + Temperatures[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_REACTION_RAW: + { + cxxReaction entity; + entity.read_raw(parser, true); + Reactions[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_REACTION_MODIFY: + { + Utilities::SB_read_modify(this->Reactions, parser); + } + break; + case Keywords::KEY_MIX_RAW: + { + cxxMix entity; + entity.read_raw(parser); + Mixes[entity.Get_n_user()] = entity; + } + break; + default: + { + for (;;) + { + PHRQ_io::LINE_TYPE lt; + lt = parser.check_line("read_raw", false, true, true, false); + if (lt == PHRQ_io::LT_KEYWORD) + break; + if (lt == PHRQ_io::LT_EOF) + goto END_OF_SIMULATION_INPUT; + } + } + break; + } + } + + END_OF_SIMULATION_INPUT: + return; //PHRQ_io::LT_OK; +} + +int +cxxStorageBin::read_raw_keyword(CParser & parser) +{ + PHRQ_io::LINE_TYPE i; + int entity_number = -999; + + switch (parser.next_keyword()) + { + case Keywords::KEY_NONE: + case Keywords::KEY_END: + while ((i = + parser.check_line("StorageBin read_raw_keyword", false, true, + true, true)) != PHRQ_io::LT_KEYWORD) + { + if (i == PHRQ_io::LT_EOF) + break; // PHRQ_io::LT_EOF; + } + break; + + case Keywords::KEY_SOLUTION_RAW: + { + cxxSolution entity(this->Get_io()); + entity.read_raw(parser); + Solutions[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_EXCHANGE_RAW: + { + cxxExchange entity(this->Get_io()); + entity.read_raw(parser); + Exchangers[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_GAS_PHASE_RAW: + { + cxxGasPhase entity(this->Get_io()); + entity.read_raw(parser); + GasPhases[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_KINETICS_RAW: + { + cxxKinetics entity(this->Get_io()); + entity.read_raw(parser); + Kinetics[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + { + cxxPPassemblage entity(this->Get_io()); + entity.read_raw(parser); + PPassemblages[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + { + cxxSSassemblage entity(this->Get_io()); + entity.read_raw(parser); + SSassemblages[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_SURFACE_RAW: + { + cxxSurface entity(this->Get_io()); + entity.read_raw(parser); + Surfaces[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + { + cxxTemperature entity(this->Get_io()); + entity.read_raw(parser); + Temperatures[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_REACTION_RAW: + { + cxxReaction entity; + entity.read_raw(parser, true); + Reactions[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_MIX_RAW: + { + cxxMix entity; + entity.read_raw(parser); + Mixes[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + default: + break; + } + return (entity_number); //PHRQ_io::LT_OK; +} + +void +cxxStorageBin::Remove(int n) +{ + // Solution + this->Solutions.erase(n); + + // Exchanger + this->Exchangers.erase(n); + + // GasPhase + this->GasPhases.erase(n); + + // Kinetics + this->Kinetics.erase(n); + + // PPassemblage + this->PPassemblages.erase(n); + + // SSassemblage + this->SSassemblages.erase(n); + + // Surface + this->Surfaces.erase(n); + + // Mixes + this->Mixes.erase(n); + + // Reactions + this->Reactions.erase(n); + + // Temperature + this->Temperatures.erase(n); + + // Pressure + this->Pressures.erase(n); +} +void +cxxStorageBin::Clear(void) +{ + // Delete all data + + // Solutions + this->Solutions.clear(); + + // Exchange + this->Exchangers.clear(); + + // Gas Phases + this->GasPhases.clear(); + + // Kinetics + this->Kinetics.clear(); + + // PPassemblage + this->PPassemblages.clear(); + + // SSassemblage + this->SSassemblages.clear(); + + // Surface + this->Surfaces.clear(); + + // Mix + this->Mixes.clear(); + + // Reactions + this->Reactions.clear(); + + // Temperature + this->Temperatures.clear(); + + // Pressure + this->Pressures.clear(); +} + +cxxSystem & +cxxStorageBin::Get_System(void) +{ + return this->system; +} + +void +cxxStorageBin::Set_System(cxxUse *use_ptr) +{ + // Initialize + this->system.Initialize(); + // Solution + if (use_ptr->Get_solution_ptr() != NULL) + { + std::map < int, cxxSolution >::iterator it = + this->Solutions.find(use_ptr->Get_n_solution_user()); + if (it != this->Solutions.end()) + { + this->system.Set_Solution(&(it->second)); + } + } + // Exchange + if (use_ptr->Get_exchange_ptr() != NULL) + { + std::map < int, cxxExchange >::iterator it = + this->Exchangers.find(use_ptr->Get_n_exchange_user()); + if (it != this->Exchangers.end()) + { + this->system.Set_Exchange(&(it->second)); + } + } + // gas_phase + if (use_ptr->Get_gas_phase_ptr() != NULL) + { + std::map < int, cxxGasPhase >::iterator it = + this->GasPhases.find(use_ptr->Get_n_gas_phase_user()); + if (it != this->GasPhases.end()) + { + this->system.Set_GasPhase(&(it->second)); + } + } + // kinetics + if (use_ptr->Get_kinetics_ptr() != NULL) + { + std::map < int, cxxKinetics >::iterator it = + this->Kinetics.find(use_ptr->Get_n_kinetics_user()); + if (it != this->Kinetics.end()) + { + this->system.Set_Kinetics(&(it->second)); + } + } + // pp_assemblage + if (use_ptr->Get_pp_assemblage_ptr() != NULL) + { + std::map < int, cxxPPassemblage >::iterator it = + this->PPassemblages.find(use_ptr->Get_n_pp_assemblage_user()); + if (it != this->PPassemblages.end()) + { + this->system.Set_PPassemblage(&(it->second)); + } + } + // ss_assemblage + if (use_ptr->Get_ss_assemblage_ptr() != NULL) + { + std::map < int, cxxSSassemblage >::iterator it = + this->SSassemblages.find(use_ptr->Get_n_ss_assemblage_user()); + if (it != this->SSassemblages.end()) + { + this->system.Set_SSassemblage(&(it->second)); + } + } + // surface + if (use_ptr->Get_surface_ptr() != NULL) + { + std::map < int, cxxSurface >::iterator it = + this->Surfaces.find(use_ptr->Get_n_surface_user()); + if (it != this->Surfaces.end()) + { + this->system.Set_Surface(&(it->second)); + } + } + // mix + if (use_ptr->Get_mix_ptr() != NULL) + { + std::map < int, cxxMix >::iterator it = + this->Mixes.find(use_ptr->Get_n_mix_user()); + if (it != this->Mixes.end()) + { + this->system.Set_Mix(&(it->second)); + } + } + // reaction + if (use_ptr->Get_reaction_ptr() != NULL) + { + std::map < int, cxxReaction >::iterator it = + this->Reactions.find(use_ptr->Get_n_reaction_user()); + if (it != this->Reactions.end()) + { + this->system.Set_Reaction(&(it->second)); + } + } + // reaction temperature + if (use_ptr->Get_temperature_ptr() != NULL) + { + std::map < int, cxxTemperature >::iterator it = + this->Temperatures.find(use_ptr->Get_n_temperature_user()); + if (it != this->Temperatures.end()) + { + this->system.Set_Temperature(&(it->second)); + } + } + // reaction pressure + if (use_ptr->Get_pressure_ptr() != NULL) + { + cxxPressure * p = Utilities::Rxn_find(this->Pressures, use_ptr->Get_n_pressure_user()); + if (p != NULL) + { + this->system.Set_Pressure(p); + } + } +} +void +cxxStorageBin::Set_System(int i) +{ + // Initialize + this->system.Initialize(); + // Solution + { + std::map < int, cxxSolution >::iterator it = this->Solutions.find(i); + if (it != this->Solutions.end()) + { + this->system.Set_Solution(&(it->second)); + } + } + + // Exchange + { + std::map < int, cxxExchange >::iterator it = this->Exchangers.find(i); + if (it != this->Exchangers.end()) + { + this->system.Set_Exchange(&(it->second)); + } + } + + // gas_phase + { + std::map < int, cxxGasPhase >::iterator it = this->GasPhases.find(i); + if (it != this->GasPhases.end()) + { + this->system.Set_GasPhase(&(it->second)); + } + } + // kinetics + { + std::map < int, cxxKinetics >::iterator it = this->Kinetics.find(i); + if (it != this->Kinetics.end()) + { + this->system.Set_Kinetics(&(it->second)); + } + } + // pp_assemblage + { + std::map < int, cxxPPassemblage >::iterator it = this->PPassemblages.find(i); + if (it != this->PPassemblages.end()) + { + this->system.Set_PPassemblage(&(it->second)); + } + } + // ss_assemblage + { + std::map < int, cxxSSassemblage >::iterator it = this->SSassemblages.find(i); + if (it != this->SSassemblages.end()) + { + this->system.Set_SSassemblage(&(it->second)); + } + } + // surface + { + std::map < int, cxxSurface >::iterator it = this->Surfaces.find(i); + if (it != this->Surfaces.end()) + { + this->system.Set_Surface(&(it->second)); + } + } + // mix + { + std::map < int, cxxMix >::iterator it = this->Mixes.find(i); + if (it != this->Mixes.end()) + { + this->system.Set_Mix(&(it->second)); + } + } + // reaction + { + std::map < int, cxxReaction >::iterator it = this->Reactions.find(i); + if (it != this->Reactions.end()) + { + this->system.Set_Reaction(&(it->second)); + } + } + // reaction temperature + { + std::map < int, cxxTemperature >::iterator it = this->Temperatures.find(i); + if (it != this->Temperatures.end()) + { + this->system.Set_Temperature(&(it->second)); + } + } + + // reaction pressure + { + this->system.Set_Pressure(Utilities::Rxn_find(this->Pressures, i)); + } +} diff --git a/phreeqcpp/StorageBin.h b/phreeqcpp/StorageBin.h new file mode 100644 index 00000000..1cf5c9c7 --- /dev/null +++ b/phreeqcpp/StorageBin.h @@ -0,0 +1,141 @@ +#if !defined(STORAGEBIN_H_INCLUDED) +#define STORAGEBIN_H_INCLUDED +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "System.h" +#include "PHRQ_io.h" +#include "PHRQ_base.h" +class cxxSolution; +class cxxExchange; +class cxxGasPhase; +class cxxKinetics; +class cxxPPassemblage; +class cxxSSassemblage; +class cxxSurface; +class cxxReaction; +class cxxTemperature; +class cxxUse; + +class cxxStorageBin: public PHRQ_base +{ + + public: + cxxStorageBin(PHRQ_io *io=NULL); + cxxStorageBin(cxxUse &use_ref, PHRQ_io *io=NULL); + virtual ~cxxStorageBin(); + + void Copy(int destination, int source); + void Remove(int n); + void Clear(void); + + cxxSolution *Get_Solution(int n_user); + void Set_Solution(int n_user, cxxSolution * entity); + void Set_Solution(int n_user, cxxSolution & entity); + void Remove_Solution(int n_user); + + cxxExchange *Get_Exchange(int n_user); + void Set_Exchange(int n_user, cxxExchange * entity); + void Set_Exchange(int n_user, cxxExchange & entity); + void Remove_Exchange(int n_user); + + cxxPPassemblage *Get_PPassemblage(int n_user); + void Set_PPassemblage(int n_user, cxxPPassemblage * entity); + void Set_PPassemblage(int n_user, cxxPPassemblage & entity); + void Remove_PPassemblage(int n_user); + + cxxGasPhase *Get_GasPhase(int n_user); + void Set_GasPhase(int n_user, cxxGasPhase * entity); + void Set_GasPhase(int n_user, cxxGasPhase & entity); + void Remove_GasPhase(int n_user); + + cxxSSassemblage *Get_SSassemblage(int n_user); + void Set_SSassemblage(int n_user, cxxSSassemblage * entity); + void Set_SSassemblage(int n_user, cxxSSassemblage & entity); + void Remove_SSassemblage(int n_user); + + cxxKinetics *Get_Kinetics(int n_user); + void Set_Kinetics(int n_user, cxxKinetics * entity); + void Set_Kinetics(int n_user, cxxKinetics & entity); + void Remove_Kinetics(int n_user); + + cxxSurface *Get_Surface(int n_user); + void Set_Surface(int n_user, cxxSurface * entity); + void Set_Surface(int n_user, cxxSurface & entity); + void Remove_Surface(int n_user); + + cxxMix *Get_Mix(int n_user); + void Set_Mix(int n_user, cxxMix * entity); + void Set_Mix(int n_user, cxxMix & entity); + void Remove_Mix(int n_user); + + cxxReaction *Get_Reaction(int n_user); + void Set_Reaction(int n_user, cxxReaction * entity); + void Set_Reaction(int n_user, cxxReaction & entity); + void Remove_Reaction(int n_user); + + cxxTemperature *Get_Temperature(int n_user); + void Set_Temperature(int n_user, cxxTemperature * entity); + void Set_Temperature(int n_user, cxxTemperature & entity); + void Remove_Temperature(int n_user); + + cxxPressure *Get_Pressure(int n_user); + void Set_Pressure(int n_user, cxxPressure * entity); + void Set_Pressure(int n_user, cxxPressure & entity); + void Remove_Pressure(int n_user); + + cxxSystem &Get_System(void); + void Set_System(cxxUse *use_ptr); + void Set_System(int i); + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void dump_raw(std::ostream & s_oss, int i, unsigned int indent, int *n_out=NULL); + void dump_raw_range(std::ostream & s_oss, int start, int end, unsigned int indent) const; + + void read_raw(CParser & parser); + int read_raw_keyword(CParser & parser); + + void Add(cxxStorageBin &src, int n); + void Add_uz(cxxStorageBin &uzbin); + + //cxxSolution *mix_cxxSolutions(cxxMix &mixmap); + cxxExchange *mix_cxxExchange(cxxMix & mixmap); + + std::map < int, cxxSolution > &Get_Solutions(); + std::map < int, cxxExchange > &Get_Exchangers(); + std::map < int, cxxGasPhase > &Get_GasPhases(); + std::map < int, cxxKinetics > &Get_Kinetics(); + std::map < int, cxxPPassemblage > &Get_PPassemblages(); + std::map < int, cxxSSassemblage > &Get_SSassemblages(); + std::map < int, cxxSurface > &Get_Surfaces(); + std::map < int, cxxMix > &Get_Mixes(); + std::map < int, cxxReaction > &Get_Reactions(); + std::map < int, cxxTemperature > &Get_Temperatures(); + std::map < int, cxxPressure > &Get_Pressures(); + + cxxSystem & Get_system(void) {return system;}; + + protected: + // Tidied classes + std::map < int, cxxSolution > Solutions; + std::map < int, cxxExchange > Exchangers; + std::map < int, cxxGasPhase > GasPhases; + std::map < int, cxxKinetics > Kinetics; + std::map < int, cxxPPassemblage > PPassemblages; + std::map < int, cxxSSassemblage > SSassemblages; + std::map < int, cxxSurface > Surfaces; + + // Reaction classes + std::map < int, cxxMix > Mixes; + std::map < int, cxxReaction > Reactions; + std::map < int, cxxTemperature > Temperatures; + std::map < int, cxxPressure > Pressures; + cxxSystem system; + +}; + +#endif // !defined(STORAGEBIN_H_INCLUDED) diff --git a/phreeqcpp/StorageBinList.cpp b/phreeqcpp/StorageBinList.cpp new file mode 100644 index 00000000..f968a66d --- /dev/null +++ b/phreeqcpp/StorageBinList.cpp @@ -0,0 +1,358 @@ +#include // std::replace +#include "StorageBinList.h" +#include "Parser.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +StorageBinListItem::StorageBinListItem(void) +{ + this->defined = false; +} + +StorageBinListItem::~StorageBinListItem(void) +{ +} +StorageBinListItem::StorageBinListItem(CParser & parser) +{ + this->Clear(); + // Read list of numbers or number ranges + for (;;) + { + //read lines + PHRQ_io::LINE_TYPE l = parser.check_line("read StorageBinListLtem", false, true, true, true); + std::istream::pos_type next_char = 0; + if (l == PHRQ_io::LT_EOF) break; + for (;;) + { + std::string token; + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (j == CParser::TT_DIGIT) + { + this->Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + break; + } + } + } +} +void StorageBinListItem::Augment(std::string token) +{ + this->defined = true; + if (token.size() == 0) return; + + // split string accounting for possible negative numbers + size_t pos; + if ((pos = token.find("--")) != std::string::npos) + { + token.replace(pos,2," &"); + } + std::replace(token.begin() + 1, token.end(), '-', ' '); + std::replace(token.begin() + 1, token.end(), '&', '-'); + + // parse string into 1 or 2 numbers + std::istringstream iss(token); + std::set < int > temp_set; + int i; + if (iss >> i) + { + // add first + temp_set.insert(i); + if (iss >> i) + { + // add second if defined + temp_set.insert(i); + } + } + + // add single number or range to StorageBinListItem + if (temp_set.size() == 1) + { + this->numbers.insert(*(temp_set.begin())); + } + else if (temp_set.size() == 2) + { + int i1, i2; + std::set ::iterator it; + it = temp_set.begin(); + i1 = *it; + it++; + i2 = *it; + for (i = i1; i <= i2; i++) + { + this->numbers.insert(i); + } + } +} +void StorageBinListItem::Augment(int i) +{ + // Skip if all are defined + if (this->defined == true && this->numbers.size() == 0) return; + this->defined = true; + this->numbers.insert(i); +} +// +//Class definitions for StorageBinList +// +StorageBinList::StorageBinList(PHRQ_io *io) +: +PHRQ_base(io) +{ +} +StorageBinList::StorageBinList(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->Read(parser); +} +StorageBinList::~StorageBinList(void) +{ +} + +std::set StorageBinList::GetAllItems(void) +{ + // don't include this->cell + std::set items; + items.insert(&this->solution); + items.insert(&this->pp_assemblage); + items.insert(&this->exchange); + items.insert(&this->surface); + items.insert(&this->ss_assemblage); + items.insert(&this->gas_phase); + items.insert(&this->kinetics); + items.insert(&this->mix); + items.insert(&this->reaction); + items.insert(&this->temperature); + items.insert(&this->pressure); + return items; +} + +void StorageBinList::SetAll(bool tf) +{ + std::set all = this->GetAllItems(); + std::set::iterator it = all.begin(); + for (; it != all.end(); ++it) + { + (*it)->Clear(); + (*it)->Set_defined(tf); + } +} + +bool StorageBinList::Read(CParser & parser) +{ + bool return_value(true); + + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_DEFAULT; + // reset cells + this->cell.Clear(); + this->cell.Set_defined(false); + for (;;) + { + int opt; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + + // Select StorageBinListItem + StorageBinListItem *item = NULL; + switch (opt) + { + case 0: + item = &(this->Get_solution()); + break; + case 1: + case 2: + item = &(this->Get_pp_assemblage()); + break; + case 3: + item = &(this->Get_exchange()); + break; + case 4: + item = &(this->Get_surface()); + break; + case 5: + case 6: + case 7: + item = &(this->Get_ss_assemblage()); + break; + case 8: + item = &(this->Get_gas_phase()); + break; + case 9: + item = &(this->Get_kinetics()); + break; + case 10: + item = &(this->Get_mix()); + break; + case 11: + item = &(this->Get_reaction()); + break; + case 12: + case 16: + item = &(this->Get_temperature()); + break; + case 14: + case 15: + item = &(this->Get_cell()); + break; + case 17: + case 18: + item = &(this->Get_pressure()); + break; + default: + break; + } + + // Read dump entity list of numbers or number ranges for line, store in item + if ((opt >= 0 && opt <= 12) || (opt >= 14)) + { + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (item) + { + if (j == CParser::TT_DIGIT) + { + item->Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + item->Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + break; + } + } + else + { + parser.error_msg("Dump entity type not defined.", + PHRQ_io::OT_CONTINUE); + break; + } + } + } + + // Process other identifiers + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 16: + case 17: + case 18: + break; + case 13: //all + this->SetAll(true); + break; + case 14: + case 15: + //if (cell_list.Get_numbers().empty()) + //{ + // this->SetAll(true); + //} + //else + //{ + //this->TransferAll(cell_list); + //} + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading DELETE definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + + // Now check to see if cell_list defined + if (this->Get_cell().Get_defined()) + { + if (this->Get_cell().Get_numbers().empty()) + { + this->SetAll(true); + } + else + { + this->TransferAll(this->Get_cell()); + } + } + return(return_value); +} + +void StorageBinList::TransferAll(StorageBinListItem &source) +{ + std::set items = this->GetAllItems(); + std::set < int >::iterator it; + for (it = source.Get_numbers().begin(); it != source.Get_numbers().end(); it++) + { + std::set::iterator item = items.begin(); + for (; item != items.end(); ++item) + { + (*item)->Augment(*it); + } + } +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("solution"), // 0 + std::vector< std::string >::value_type("pp_assemblage"), // 1 + std::vector< std::string >::value_type("equilibrium_phases"), // 2 + std::vector< std::string >::value_type("exchange"), // 3 + std::vector< std::string >::value_type("surface"), // 4 + std::vector< std::string >::value_type("ss_assemblage"), // 5 + std::vector< std::string >::value_type("solid_solution"), // 6 + std::vector< std::string >::value_type("solid_solutions"), // 7 + std::vector< std::string >::value_type("gas_phase"), // 8 + std::vector< std::string >::value_type("kinetics"), // 9 + std::vector< std::string >::value_type("mix"), // 10 + std::vector< std::string >::value_type("reaction"), // 11 + std::vector< std::string >::value_type("temperature"), // 12 + std::vector< std::string >::value_type("all"), // 13 + std::vector< std::string >::value_type("cell"), // 14 + std::vector< std::string >::value_type("cells"), // 15 + std::vector< std::string >::value_type("reaction_temperature"), // 16 + std::vector< std::string >::value_type("pressure"), // 17 + std::vector< std::string >::value_type("reaction_pressure") // 18 +}; +const std::vector< std::string > StorageBinList::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/StorageBinList.h b/phreeqcpp/StorageBinList.h new file mode 100644 index 00000000..f55a6735 --- /dev/null +++ b/phreeqcpp/StorageBinList.h @@ -0,0 +1,81 @@ +#if !defined(STORAGEBINLIST_H_INCLUDED) +#define STORAGEBINLIST_H_INCLUDED +#include // std::set +#include // std::string +#include // std::list +#include // std::vector +#include "PHRQ_base.h" +class CParser; + +class StorageBinListItem +{ +public: + StorageBinListItem(void); + StorageBinListItem(CParser & parser); + ~StorageBinListItem(void); + void Set_defined(bool tf) { this->defined = tf; }; + bool Get_defined(void)const { return(this->defined); }; + void Augment(std::string token); + void Augment(int i); + std::set < int > &Get_numbers(void) { return(this->numbers); }; + const std::set < int > &Get_numbers(void)const { return(this->numbers); }; + void Clear(void) { this->numbers.clear(); }; +protected: + std::set < int > numbers; + bool defined; +}; +class StorageBinList: public PHRQ_base +{ +public: + StorageBinList(PHRQ_io *io=NULL); + StorageBinList(CParser & parser, PHRQ_io *io=NULL); + virtual ~StorageBinList(void); + bool Read(CParser & parser); + void SetAll(bool tf); + void TransferAll(StorageBinListItem &source); + std::set GetAllItems(void); + + StorageBinListItem & Get_solution(void) { return(this->solution); }; + StorageBinListItem & Get_pp_assemblage(void) { return(this->pp_assemblage); }; + StorageBinListItem & Get_exchange(void) { return(this->exchange); }; + StorageBinListItem & Get_surface(void) { return(this->surface); }; + StorageBinListItem & Get_ss_assemblage(void) { return(this->ss_assemblage); }; + StorageBinListItem & Get_gas_phase(void) { return(this->gas_phase); }; + StorageBinListItem & Get_kinetics(void) { return(this->kinetics); }; + StorageBinListItem & Get_mix(void) { return(this->mix); }; + StorageBinListItem & Get_reaction(void) { return(this->reaction); }; + StorageBinListItem & Get_temperature(void) { return(this->temperature); }; + StorageBinListItem & Get_pressure(void) { return(this->pressure); }; + StorageBinListItem & Get_cell(void) { return(this->cell); }; + + const StorageBinListItem & Get_solution(void)const { return(this->solution); }; + const StorageBinListItem & Get_pp_assemblage(void)const { return(this->pp_assemblage); }; + const StorageBinListItem & Get_exchange(void)const { return(this->exchange); }; + const StorageBinListItem & Get_surface(void)const { return(this->surface); }; + const StorageBinListItem & Get_ss_assemblage(void)const { return(this->ss_assemblage); }; + const StorageBinListItem & Get_gas_phase(void)const { return(this->gas_phase); }; + const StorageBinListItem & Get_kinetics(void)const { return(this->kinetics); }; + const StorageBinListItem & Get_mix(void)const { return(this->mix); }; + const StorageBinListItem & Get_reaction(void)const { return(this->reaction); }; + const StorageBinListItem & Get_temperature(void)const { return(this->temperature); }; + const StorageBinListItem & Get_pressure(void)const { return(this->pressure); }; + const StorageBinListItem & Get_cell(void)const { return(this->cell); }; +protected: + // update GetAllItems() if StorageBinListItem is added/removed + StorageBinListItem solution; + StorageBinListItem pp_assemblage; + StorageBinListItem exchange; + StorageBinListItem surface; + StorageBinListItem ss_assemblage; + StorageBinListItem gas_phase; + StorageBinListItem kinetics; + StorageBinListItem mix; + StorageBinListItem reaction; + StorageBinListItem temperature; + StorageBinListItem pressure; + const static std::vector < std::string > vopts; + StorageBinListItem cell; // not included in GetAllItems +}; + + +#endif // !defined(STORAGEBINLIST_H_INCLUDED) diff --git a/phreeqcpp/Surface.cxx b/phreeqcpp/Surface.cxx new file mode 100644 index 00000000..40c8cc0f --- /dev/null +++ b/phreeqcpp/Surface.cxx @@ -0,0 +1,808 @@ +// Surface.cxx: implementation of the cxxSurface class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Surface.h" +#include "cxxMix.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurface::cxxSurface(PHRQ_io *io) + // + // default constructor for cxxSurface + // +: cxxNumKeyword(io) +{ + new_def = false; + tidied = false; + type = DDL; + dl_type = NO_DL; + sites_units = SITES_ABSOLUTE; + only_counter_ions = false; + thickness = 1e-8; + debye_lengths = 0.0; + DDL_viscosity = 1.0; + DDL_limit = 0.8; + transport = false; + solution_equilibria = false; + n_solution = -999; +} +cxxSurface::cxxSurface(std::map < int, cxxSurface > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + this->new_def = false; + this->tidied = true; + type = DDL; + dl_type = NO_DL; + sites_units = SITES_ABSOLUTE; + only_counter_ions = false; + thickness = 1e-8; + debye_lengths = 0.0; + DDL_viscosity = 1.0; + DDL_limit = 0.8; + transport = false; + solution_equilibria = false; + n_solution = -999; +// +// Mix surfaces +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + this->add(entities.find(it->first)->second, it->second); + } + } +} + +cxxSurface::~cxxSurface() +{ +} + +bool +cxxSurface::Get_related_phases() const +{ + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + if (this->surface_comps[i].Get_phase_name().size() == 0) + continue; + return (true); + } + return (false); +} + +bool +cxxSurface::Get_related_rate() const +{ + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + if (this->surface_comps[i].Get_rate_name().size() == 0) + continue; + return (true); + } + return (false); +} +void +cxxSurface::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surface element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SURFACE_RAW " << n_user_local << " " << this->description << "\n"; + s_oss << indent1 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-type " << this->type << "\n"; + s_oss << indent1; + s_oss << "-dl_type " << this->dl_type << "\n"; + s_oss << indent1; + s_oss << "-only_counter_ions " << this->only_counter_ions << "\n"; + s_oss << indent1; + s_oss << "-thickness " << this->thickness << "\n"; + s_oss << indent1; + s_oss << "-debye_lengths " << this->debye_lengths << "\n"; + s_oss << indent1; + s_oss << "-DDL_viscosity " << this->DDL_viscosity << "\n"; + s_oss << indent1; + s_oss << "-DDL_limit " << this->DDL_limit << "\n"; + // surfaceComps + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + const cxxSurfaceComp * comp_ptr = &(this->surface_comps[i]); + s_oss << indent1; + s_oss << "-component " << comp_ptr->Get_formula() << "\n"; + comp_ptr->dump_raw(s_oss, indent + 2); + } + // surface charge + for (size_t i = 0; i != this->surface_charges.size(); i++) + { + const cxxSurfaceCharge * charge_ptr = &(this->surface_charges[i]); + s_oss << indent1; + s_oss << "-charge_component " << charge_ptr->Get_name() << "\n"; + charge_ptr->dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# SURFACE_MODIFY candidates with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << this->new_def << "\n"; + s_oss << indent1; + s_oss << "-tidied " << this->tidied << "\n"; + s_oss << indent1; + s_oss << "-sites_units " << this->sites_units << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << this->solution_equilibria << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + + s_oss << indent1 << "# Surface workspace variables #\n"; + s_oss << indent1; + s_oss << "-transport " << this->transport << "\n"; + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + + return; +} + +void +cxxSurface::read_raw(CParser & parser, bool check) +{ + int i = 0; + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + bool useLastLine(false); + + // Read surface number and description + this->read_number_description(parser); + this->Set_new_def(false); + this->Set_tidied(true); + + bool only_counter_ions_defined(false); + bool thickness_defined(false); + bool type_defined(false); + bool dl_type_defined(false); + bool sites_units_defined(false); + bool debye_lengths_defined(false); + bool DDL_viscosity_defined(false); + bool DDL_limit_defined(false); + bool transport_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SURFACE keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // diffuse_layer + parser.incr_input_error(); + parser.error_msg("Diffuse layer is obsolete, use -type.", + PHRQ_io::OT_CONTINUE); + break; + + case 1: // edl + parser.incr_input_error(); + parser.error_msg("-edl is obsolete, use -type.", + PHRQ_io::OT_CONTINUE); + break; + + case 2: // only_counter_ions + if (!(parser.get_iss() >> this->only_counter_ions)) + { + this->only_counter_ions = false; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for only_counter_ions.", + PHRQ_io::OT_CONTINUE); + } + only_counter_ions_defined = true; + break; + + case 3: // donnan + parser.incr_input_error(); + parser.error_msg("-Donnan is obsolete, use -dl_type.", + PHRQ_io::OT_CONTINUE); + break; + + case 4: // thickness + if (!(parser.get_iss() >> this->thickness)) + { + this->thickness = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for thickness.", + PHRQ_io::OT_CONTINUE); + } + thickness_defined = true; + break; + case 5: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSurfaceComp temp_comp(this->io); + temp_comp.Set_formula(str.c_str()); + cxxSurfaceComp *comp_ptr = this->Find_comp(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + if (comp_ptr) + { + for (size_t j = 0; j < this->surface_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->surface_comps[j].Get_formula().c_str(), str.c_str()) == 0) + { + this->surface_comps[j] = temp_comp; + } + } + } + else + { + this->surface_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + case 6: // charge_component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for charge name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSurfaceCharge temp_charge(this->io); + temp_charge.Set_name(str.c_str()); + cxxSurfaceCharge *charge_ptr = this->Find_charge(str); + if (charge_ptr) + { + temp_charge = *charge_ptr; + } + temp_charge.read_raw(parser, check); + if (charge_ptr) + { + for (size_t j = 0; j < this->surface_charges.size(); j++) + { + if (Utilities::strcmp_nocase(this->surface_charges[j].Get_name().c_str(), str.c_str()) == 0) + { + this->surface_charges[j] = temp_charge; + } + } + } + else + { + this->surface_charges.push_back(temp_charge); + } + useLastLine = true; + } + } + useLastLine = true; + break; + case 7: // type + i = 0; + if (!(parser.get_iss() >> i)) + { + this->type = NO_EDL; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for type.", + PHRQ_io::OT_CONTINUE); + } + this->type = (SURFACE_TYPE) i; + type_defined = true; + break; + case 8: // dl_type + i = 0; + if (!(parser.get_iss() >> i)) + { + this->dl_type = NO_DL; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dl_type.", + PHRQ_io::OT_CONTINUE); + } + this->dl_type = (DIFFUSE_LAYER_TYPE) i; + dl_type_defined = true; + break; + case 9: // sites_units + i = 0; + if (!(parser.get_iss() >> i)) + { + this->sites_units = SITES_ABSOLUTE; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sites_units.", + PHRQ_io::OT_CONTINUE); + } + this->sites_units = (SITES_UNITS) i; + sites_units_defined = true; + break; + + case 10: // debye_lengths + if (!(parser.get_iss() >> this->debye_lengths)) + { + this->debye_lengths = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for debye_lengths.", + PHRQ_io::OT_CONTINUE); + } + debye_lengths_defined = true; + break; + + case 11: // DDL_viscosity + if (!(parser.get_iss() >> this->DDL_viscosity)) + { + this->DDL_viscosity = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for DDL_viscosity.", + PHRQ_io::OT_CONTINUE); + } + DDL_viscosity_defined = true; + break; + + case 12: // DDL_limit + if (!(parser.get_iss() >> this->DDL_limit)) + { + this->DDL_limit = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for DDL_limit.", + PHRQ_io::OT_CONTINUE); + } + DDL_limit_defined = true; + break; + + case 13: // transport + if (!(parser.get_iss() >> this->transport)) + { + this->transport = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for transport.", + PHRQ_io::OT_CONTINUE); + } + transport_defined = true; + break; + + case 14: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for new_def.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 15: // new_def + if (!(parser.get_iss() >> this->solution_equilibria)) + { + this->solution_equilibria = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for solution_equilibria.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 16: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + this->n_solution = -999; + parser.incr_input_error(); + parser.error_msg("Expected integer value for n_solution.", + PHRQ_io::OT_CONTINUE); + } + break; + case 17: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Surface totals.", + PHRQ_io::OT_CONTINUE); + } + break; + case 18: // tidied + if (!(parser.get_iss() >> this->tidied)) + { + this->tidied = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for tidied.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (only_counter_ions_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Only_counter_ions not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (thickness_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Thickness not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Surface type not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (dl_type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dl_type not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (sites_units_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Sites_units not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (debye_lengths_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Debye_lengths not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (DDL_viscosity_defined == false) + { + parser.incr_input_error(); + parser.error_msg("DDL_viscosity not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (DDL_limit_defined == false) + { + parser.incr_input_error(); + parser.error_msg("DDL_limit not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (transport_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Transport not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + this->Sort_comps(); +} + +void +cxxSurface::totalize() +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + cxxSurfaceComp * comp_ptr = &(this->surface_comps[i]); + this->totals.add_extensive(comp_ptr->Get_totals(), 1.0); + this->totals.add("Charge", comp_ptr->Get_charge_balance()); + } + return; +} +void +cxxSurface::add(const cxxSurface & addee_in, LDBLE extensive) + // + // Add surface to "this" surface + // +{ + cxxSurface addee = addee_in; + if (extensive == 0.0) + return; + if (this->surface_comps.size() == 0) + { + this->only_counter_ions = addee.only_counter_ions; + this->dl_type = addee.dl_type; + this->type = addee.type; + this->sites_units = addee.sites_units; + this->thickness = addee.thickness; + this->debye_lengths = addee.debye_lengths; + this->DDL_viscosity = addee.DDL_viscosity; + this->DDL_limit = addee.DDL_limit; + this->solution_equilibria = addee.solution_equilibria; + this->n_solution = addee.n_solution; + this->transport = addee.transport; + } + + for (size_t i_add = 0; i_add < addee.Get_surface_comps().size(); i_add++) + { + const cxxSurfaceComp & comp_add_ptr = addee.Get_surface_comps()[i_add]; + size_t i_this; + for (i_this = 0; i_this < this->surface_comps.size(); i_this++) + { + cxxSurfaceComp & comp_this_ptr = this->surface_comps[i_this]; + if(comp_add_ptr.Get_formula() == comp_this_ptr.Get_formula()) + { + comp_this_ptr.add(addee.surface_comps[i_add], extensive); + break; + } + } + if (i_this == this->surface_comps.size()) + { + + cxxSurfaceComp entity = comp_add_ptr; + entity.multiply(extensive); + this->surface_comps.push_back(entity); + } + } + for (size_t i_add = 0; i_add < addee.Get_surface_charges().size(); i_add++) + { + const cxxSurfaceCharge & charge_add_ptr = addee.Get_surface_charges()[i_add]; + + size_t i_this; + for (i_this = 0; i_this < this->surface_charges.size(); i_this++) + { + cxxSurfaceCharge & charge_this_ptr = this->Get_surface_charges()[i_this]; + if(charge_add_ptr.Get_name() == charge_this_ptr.Get_name()) + { + charge_this_ptr.add(addee.surface_charges[i_add], extensive); + break; + } + } + if (i_this == this->surface_charges.size()) + { + + cxxSurfaceCharge entity = charge_add_ptr; + entity.multiply(extensive); + this->surface_charges.push_back(entity); + } + } +} + +void +cxxSurface::multiply(LDBLE extensive) + // + // Add surface to "this" surface + // +{ + + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + cxxSurfaceComp *comp_ptr = &(this->surface_comps[i]); + comp_ptr->multiply(extensive); + } + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(this->surface_charges[i]); + charge_ptr->multiply(extensive); + } +} +cxxSurfaceComp * cxxSurface:: +Find_comp(std::string str) +{ + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_comps[i].Get_formula().c_str()) == 0) + return &(this->surface_comps[i]); + } + return NULL; +} + +cxxSurfaceCharge * cxxSurface:: +Find_charge(std::string str) +{ + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_charges[i].Get_name().c_str()) == 0) + return &(this->surface_charges[i]); + } + return NULL; +} +const cxxSurfaceCharge * cxxSurface:: +Find_charge(std::string str)const +{ + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_charges[i].Get_name().c_str()) == 0) + return &(this->surface_charges[i]); + } + return NULL; +} +void cxxSurface:: +Sort_comps(void) +{ + // sort comps + { + std::map comp_map; + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + comp_map[this->surface_comps[i].Get_formula()] = this->surface_comps[i]; + } + this->surface_comps.clear(); + std::map::iterator it; + for (it = comp_map.begin(); it != comp_map.end(); it++) + { + this->surface_comps.push_back(it->second); + } + } + + // sort charge too + { + std::map charge_map; + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + charge_map[this->surface_charges[i].Get_name()] = this->surface_charges[i]; + } + this->surface_charges.clear(); + std::map::iterator it; + for (it = charge_map.begin(); it != charge_map.end(); it++) + { + this->surface_charges.push_back(it->second); + } + } +} +/* ---------------------------------------------------------------------- */ +void +cxxSurface::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + + ints.push_back(this->n_user); + ints.push_back((int) this->surface_comps.size()); + { + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + surface_comps[i].Serialize(dictionary, ints, doubles); + } + } + ints.push_back((int) this->surface_charges.size()); + { + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + surface_charges[i].Serialize(dictionary, ints, doubles); + } + } + ints.push_back(this->new_def ? 1 : 0); + ints.push_back(this->tidied ? 1 : 0); + ints.push_back((int) this->type); + ints.push_back((int) this->dl_type); + ints.push_back((int) this->sites_units); + ints.push_back(this->only_counter_ions ? 1 : 0); + doubles.push_back(this->thickness); + doubles.push_back(this->debye_lengths); + doubles.push_back(this->DDL_viscosity); + doubles.push_back(this->DDL_limit); + ints.push_back(this->transport ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back((int) this->n_solution); + +} + +/* ---------------------------------------------------------------------- */ +void +cxxSurface::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + { + int count = ints[ii++]; + this->surface_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxSurfaceComp sc(this->io); + sc.Deserialize(dictionary, ints, doubles, ii, dd); + this->surface_comps.push_back(sc); + } + } + { + int count = ints[ii++]; + this->surface_charges.clear(); + for (int n = 0; n < count; n++) + { + cxxSurfaceCharge sc(this->io); + sc.Deserialize(dictionary, ints, doubles, ii, dd); + this->surface_charges.push_back(sc); + } + } + this->new_def = (ints[ii++] != 0); + this->tidied = (ints[ii++] != 0); + this->type = (SURFACE_TYPE) ints[ii++]; + this->dl_type = (DIFFUSE_LAYER_TYPE) ints[ii++]; + this->sites_units = (SITES_UNITS) ints[ii++]; + this->only_counter_ions = (ints[ii++] != 0); + this->thickness = doubles[dd++]; + this->debye_lengths = doubles[dd++]; + this->DDL_viscosity = doubles[dd++]; + this->DDL_limit = doubles[dd++]; + this->transport = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->solution_equilibria = (ints[ii++] != 0); + this->n_solution = ints[ii++]; + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("diffuse_layer"), // 0 + std::vector< std::string >::value_type("edl"), // 1 + std::vector< std::string >::value_type("only_counter_ions"), // 2 + std::vector< std::string >::value_type("donnan"), // 3 + std::vector< std::string >::value_type("thickness"), // 4 + std::vector< std::string >::value_type("component"), // 5 + std::vector< std::string >::value_type("charge_component"), // 6 + std::vector< std::string >::value_type("type "), // 7 + std::vector< std::string >::value_type("dl_type"), // 8 + std::vector< std::string >::value_type("sites_units"), // 9 + std::vector< std::string >::value_type("debye_lengths"), // 10 + std::vector< std::string >::value_type("ddl_viscosity"), // 11 + std::vector< std::string >::value_type("ddl_limit"), // 12 + std::vector< std::string >::value_type("transport"), // 13 + std::vector< std::string >::value_type("new_def"), // 14 + std::vector< std::string >::value_type("solution_equilibria"), // 15 + std::vector< std::string >::value_type("n_solution"), // 16 + std::vector< std::string >::value_type("totals"), // 17 + std::vector< std::string >::value_type("tidied") // 18 +}; +const std::vector< std::string > cxxSurface::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Surface.h b/phreeqcpp/Surface.h new file mode 100644 index 00000000..7f97bce3 --- /dev/null +++ b/phreeqcpp/Surface.h @@ -0,0 +1,101 @@ +#if !defined(SURFACE_H_INCLUDED) +#define SURFACE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "SurfaceComp.h" +#include "SurfaceCharge.h" +class cxxMix; + +class cxxSurface:public cxxNumKeyword +{ + +public: + enum SURFACE_TYPE + { UNKNOWN_DL, NO_EDL, DDL, CD_MUSIC, CCM }; + enum DIFFUSE_LAYER_TYPE + { NO_DL, BORKOVEK_DL, DONNAN_DL }; + enum SITES_UNITS + { SITES_ABSOLUTE, SITES_DENSITY }; + + cxxSurface(PHRQ_io *io=NULL); + cxxSurface(std::map < int, cxxSurface > &entity_map, cxxMix & mx, + int n_user, PHRQ_io *io=NULL); + ~cxxSurface(); + + //void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + void read_raw(CParser & parser, bool check = true); + bool Get_related_phases(void) const; + bool Get_related_rate(void) const; + void totalize(); + cxxSurfaceComp *Find_comp(std::string str); + cxxSurfaceCharge *Find_charge(const std::string str); + const cxxSurfaceCharge *Find_charge(const std::string str)const; + void Sort_comps(); + + void add(const cxxSurface & addee, LDBLE extensive); + void multiply(LDBLE extensive); + + std::vector < cxxSurfaceComp > & Get_surface_comps() {return this->surface_comps;} + const std::vector < cxxSurfaceComp > & Get_surface_comps()const {return this->surface_comps;} + void Set_surface_comps(std::vector < cxxSurfaceComp > &sc) {this->surface_comps = sc;} + std::vector < cxxSurfaceCharge > & Get_surface_charges() {return this->surface_charges;} + void Set_surface_charges(std::vector < cxxSurfaceCharge > &sc) {this->surface_charges = sc;} + bool Get_new_def(void) {return new_def;} + void Set_new_def(bool tf) {new_def = tf;} + bool Get_tidied(void) { return tidied; } + void Set_tidied(bool tf) { tidied = tf; } + SURFACE_TYPE Get_type(void) const {return this->type;} + void Set_type(SURFACE_TYPE t) {this->type = t;} + DIFFUSE_LAYER_TYPE Get_dl_type(void) const {return dl_type;} + void Set_dl_type(DIFFUSE_LAYER_TYPE t) {dl_type = t;} + SITES_UNITS Get_sites_units(void) const {return sites_units;} + void Set_sites_units(SITES_UNITS u) {sites_units = u;} + bool Get_only_counter_ions(void) const {return only_counter_ions;} + void Set_only_counter_ions(bool tf) {only_counter_ions = tf;} + LDBLE Get_thickness(void) const {return thickness;} + void Set_thickness(LDBLE t) {thickness = t;} + LDBLE Get_debye_lengths(void) const {return debye_lengths;} + void Set_debye_lengths(LDBLE t) {debye_lengths = t;} + LDBLE Get_DDL_viscosity(void) const {return DDL_viscosity;} + void Set_DDL_viscosity(LDBLE t) {DDL_viscosity = t;} + LDBLE Get_DDL_limit(void) const {return DDL_limit;} + void Set_DDL_limit(LDBLE t) {DDL_limit = t;} + bool Get_transport(void) const {return transport;} + void Set_transport(bool tf) {transport = tf;} + cxxNameDouble & Get_totals() {return this->totals;} + void Get_totals(cxxNameDouble &nd) {this->totals = nd;} + bool Get_solution_equilibria(void)const {return solution_equilibria;} + void Set_solution_equilibria(bool tf) {solution_equilibria = tf;} + int Get_n_solution(void)const {return n_solution;} + void Set_n_solution(int i) {n_solution = i;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < cxxSurfaceComp > surface_comps; + std::vector < cxxSurfaceCharge > surface_charges; + bool new_def; + bool tidied; + SURFACE_TYPE type; + DIFFUSE_LAYER_TYPE dl_type; + SITES_UNITS sites_units; + bool only_counter_ions; + LDBLE thickness; + LDBLE debye_lengths; + LDBLE DDL_viscosity; + LDBLE DDL_limit; + bool transport; + cxxNameDouble totals; + bool solution_equilibria; + int n_solution; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SURFACE_H_INCLUDED) diff --git a/phreeqcpp/SurfaceCharge.cxx b/phreeqcpp/SurfaceCharge.cxx new file mode 100644 index 00000000..5d2857d8 --- /dev/null +++ b/phreeqcpp/SurfaceCharge.cxx @@ -0,0 +1,586 @@ +// SurfaceCharge.cxx: implementation of the cxxSurfaceCharge class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SurfaceCharge.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurfaceCharge::cxxSurfaceCharge(PHRQ_io *io) +: +PHRQ_base(io) +// +// default constructor for cxxSurfaceCharge +// +{ + specific_area = 0.0; + grams = 0.0; + charge_balance = 0.0; + mass_water = 0.0; + la_psi = 0.0; + capacitance[0] = 1.0; + capacitance[1] = 5.0; + sigma0 = sigma1 = sigma2 = sigmaddl = 0; + diffuse_layer_totals.type = cxxNameDouble::ND_ELT_MOLES; +} +cxxSurfaceCharge::~cxxSurfaceCharge() +{ +} + +void +cxxSurfaceCharge::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Charge element and attributes + s_oss << indent0 << "name=\"" << this->name << "\"" << "\n"; + s_oss << indent0 << "specific_area=\"" << this-> + specific_area << "\"" << "\n"; + s_oss << indent0 << "grams=\"" << this->grams << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this-> + charge_balance << "\"" << "\n"; + s_oss << indent0 << "mass_water=\"" << this-> + mass_water << "\"" << "\n"; + s_oss << indent0 << "la_psi=\"" << this->la_psi << "\"" << "\n"; + s_oss << indent0 << "capacitance=\"" << this-> + capacitance[0] << " " << this->capacitance[0] << "\"" << "\n"; + + // totals + s_oss << indent0; + s_oss << "diffuse_layer_totals.dump_xml(s_oss, indent + 1); + +} + +void +cxxSurfaceCharge::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Charge element and attributes + s_oss << indent0 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-specific_area " << this->specific_area << "\n"; + s_oss << indent0 << "-grams " << this->grams << "\n"; + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + s_oss << indent0 << "-mass_water " << this->mass_water << "\n"; + s_oss << indent0 << "-la_psi " << this->la_psi << "\n"; + s_oss << indent0 << "-capacitance0 " << this->capacitance[0] << "\n"; + s_oss << indent0 << "-capacitance1 " << this->capacitance[1] << "\n"; + // totals + s_oss << indent0; + s_oss << "-diffuse_layer_totals" << "\n"; + this->diffuse_layer_totals.dump_raw(s_oss, indent + 1); + + // DL species + //s_oss << indent0; + //s_oss << "-diffuse_layer_species" << "\n"; + if (dl_species_map.size() > 0) + { + s_oss << indent0; + s_oss << "-diffuse_layer_species" << "\n"; + std::map::const_iterator it = this->dl_species_map.begin(); + for ( ; it != dl_species_map.end(); it++) + { + s_oss << indent1; + s_oss << it->first << " " << it->second << "\n"; + } + } + s_oss << indent0 << "# Surface workspace variables #\n"; + s_oss << indent0 << "-sigma0 " << this->sigma0 << "\n"; + s_oss << indent0 << "-sigma1 " << this->sigma1 << "\n"; + s_oss << indent0 << "-sigma2 " << this->sigma2 << "\n"; + s_oss << indent0 << "-sigmaddl " << this->sigmaddl << "\n"; + std::map::const_iterator git; + for (git = this->g_map.begin(); git != g_map.end(); git++) + { + s_oss << indent0 << "-g_map " << git->first << "\t"; + s_oss << git->second.Get_g() << "\t"; + s_oss << git->second.Get_dg() << "\t"; + s_oss << git->second.Get_psi_to_z() << "\n"; + } +} + +void +cxxSurfaceCharge::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool specific_area_defined(false); + bool grams_defined(false); + bool charge_balance_defined(false); + bool mass_water_defined(false); + bool la_psi_defined(false); + bool capacitance0_defined(false); + bool capacitance1_defined(false); + bool g_map_first(true); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Surface for more processing + break; + + case 0: // name + warning_msg("-name ignored. Defined with -charge_component."); + break; + + case 1: // specific_area + if (!(parser.get_iss() >> this->specific_area)) + { + this->specific_area = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for specific_area.", + PHRQ_io::OT_CONTINUE); + } + specific_area_defined = true; + break; + + case 2: // grams + if (!(parser.get_iss() >> this->grams)) + { + this->grams = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for grams.", + PHRQ_io::OT_CONTINUE); + } + grams_defined = true; + break; + + + case 3: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 4: // mass_water + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass_water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + break; + + + case 5: // la_psi + if (!(parser.get_iss() >> this->la_psi)) + { + this->la_psi = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la_psi.", + PHRQ_io::OT_CONTINUE); + } + la_psi_defined = true; + break; + + + case 6: // diffuse_layer_totals + if (this->diffuse_layer_totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SurfaceCharge diffuse_layer_totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 6; + break; + + case 7: // la_psi1 + parser.warning_msg("-la_psi1 identifier not used"); + break; + + case 8: // la_psi2 + parser.warning_msg("-la_psi2 identifier not used"); + break; + + case 9: // capacitance0 + if (!(parser.get_iss() >> this->capacitance[0])) + { + this->capacitance[0] = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for capacitance0.", + PHRQ_io::OT_CONTINUE); + } + capacitance0_defined = true; + break; + + case 10: // capacitance1 + if (!(parser.get_iss() >> this->capacitance[1])) + { + this->capacitance[1] = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for capacitance1.", + PHRQ_io::OT_CONTINUE); + } + capacitance1_defined = true; + break; + case 11: // sigma0 + if (!(parser.get_iss() >> this->sigma0)) + { + this->sigma0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma0.", + PHRQ_io::OT_CONTINUE); + } + break; + case 12: // sigma1 + if (!(parser.get_iss() >> this->sigma1)) + { + this->sigma1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma1.", + PHRQ_io::OT_CONTINUE); + } + break; + case 13: // sigma2 + if (!(parser.get_iss() >> this->sigma2)) + { + this->sigma2 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma2.", + PHRQ_io::OT_CONTINUE); + } + break; + case 14: // sigmaddl + if (!(parser.get_iss() >> this->sigmaddl)) + { + this->sigmaddl = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigmaddl.", + PHRQ_io::OT_CONTINUE); + } + break; + case 15: // g_map + { + if (g_map_first) + { + this->g_map.clear(); + g_map_first = false; + } + LDBLE z, dummy; + if (!(parser.get_iss() >> z)) + break; + cxxSurfDL temp_surf_dl; + this->g_map[z] = temp_surf_dl; + std::map::iterator git = g_map.find(z); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_g(dummy); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_dg(dummy); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_psi_to_z(dummy); + } + break; + case 16: // dl_species_map + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->dl_species_map[s_num] = d; + } + } + opt_save = 16; + + + + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (specific_area_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Specific_area not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (grams_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Grams not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Charge_balance not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Mass_water not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (la_psi_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La_psi not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (capacitance0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Capacitance0 not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (capacitance1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Capacitance1 not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSurfaceCharge::add(const cxxSurfaceCharge & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + + if (this->name.size() == 0 && addee.name.size() == 0) + { + return; + } + assert(this->name == addee.name); + + LDBLE ext1, ext2, f1, f2; + ext1 = this->specific_area * this->grams; + ext2 = addee.specific_area * addee.grams * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + this->specific_area = f1 * this->specific_area + f2 * addee.specific_area; + this->grams += addee.grams * extensive; + this->charge_balance += addee.charge_balance * extensive; + this->mass_water += addee.mass_water * extensive; + this->la_psi = this->la_psi * f1 + addee.la_psi * f2; + this->capacitance[0] = + this->capacitance[0] * f1 + this->capacitance[0] * f2; + this->capacitance[1] = + this->capacitance[1] * f1 + this->capacitance[1] * f2; + this->diffuse_layer_totals.add_extensive(addee.diffuse_layer_totals, extensive); +} + +void +cxxSurfaceCharge::multiply(LDBLE extensive) +{ + this->grams *= extensive; + this->charge_balance *= extensive; + this->mass_water *= extensive; + this->diffuse_layer_totals.multiply(extensive); +} +void +cxxSurfaceCharge::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->specific_area); + doubles.push_back(this->grams); + doubles.push_back(this->charge_balance); + doubles.push_back(this->mass_water); + doubles.push_back(this->la_psi); + doubles.push_back(this->capacitance[0]); + doubles.push_back(this->capacitance[1]); + this->diffuse_layer_totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->sigma0); + doubles.push_back(this->sigma1); + doubles.push_back(this->sigma2); + doubles.push_back(this->sigmaddl); + ints.push_back((int) this->g_map.size()); + { + std::map::iterator it; + for (it = this->g_map.begin(); it != this->g_map.end(); it++) + { + doubles.push_back(it->first); + it->second.Serialize(dictionary, ints, doubles); + } + } + ints.push_back((int) this->dl_species_map.size()); + { + std::map::iterator it; + for (it = this->dl_species_map.begin(); it != this->dl_species_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +} + +void +cxxSurfaceCharge::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->specific_area = doubles[dd++]; + this->grams = doubles[dd++]; + this->charge_balance = doubles[dd++]; + this->mass_water = doubles[dd++]; + this->la_psi = doubles[dd++]; + this->capacitance[0] = doubles[dd++]; + this->capacitance[1] = doubles[dd++]; + this->diffuse_layer_totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->sigma0 = doubles[dd++]; + this->sigma1 = doubles[dd++]; + this->sigma2 = doubles[dd++]; + this->sigmaddl = doubles[dd++]; + { + this->g_map.clear(); + int count = ints[ii++]; + for (int i = 0; i < count; i++) + { + double d = doubles[dd++]; + cxxSurfDL sdl; + sdl.Deserialize(dictionary, ints, doubles, ii, dd); + this->g_map[d] = sdl; + } + } + { + this->dl_species_map.clear(); + int count = ints[ii++]; + for (int i = 0; i < count; i++) + { + int j = ints[ii++]; + double d = doubles[dd++]; + dl_species_map[j] = d; + } + } + + +} +void +cxxSurfDL::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + doubles.push_back(this->g); + doubles.push_back(this->dg); + doubles.push_back(this->psi_to_z); +} + +void +cxxSurfDL::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->g = doubles[dd++]; + this->dg = doubles[dd++]; + this->psi_to_z = doubles[dd++]; +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("specific_area"), // 1 + std::vector< std::string >::value_type("grams"), // 2 + std::vector< std::string >::value_type("charge_balance"), // 3 + std::vector< std::string >::value_type("mass_water"), // 4 + std::vector< std::string >::value_type("la_psi"), // 5 + std::vector< std::string >::value_type("diffuse_layer_totals"), // 6 + std::vector< std::string >::value_type("la_psi1"), // 7 + std::vector< std::string >::value_type("la_psi2"), // 8 + std::vector< std::string >::value_type("capacitance0"), // 9 + std::vector< std::string >::value_type("capacitance1"), // 10 + std::vector< std::string >::value_type("sigma0"), // 11 + std::vector< std::string >::value_type("sigma1"), // 12 + std::vector< std::string >::value_type("sigma2"), // 13 + std::vector< std::string >::value_type("sigmaddl"), // 14 + std::vector< std::string >::value_type("g_map"), // 15 + std::vector< std::string >::value_type("diffuse_layer_species") // 16 +}; +const std::vector< std::string > cxxSurfaceCharge::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SurfaceCharge.h b/phreeqcpp/SurfaceCharge.h new file mode 100644 index 00000000..091b26b7 --- /dev/null +++ b/phreeqcpp/SurfaceCharge.h @@ -0,0 +1,131 @@ +#if !defined(SURFACECHARGE_H_INCLUDED) +#define SURFACECHARGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxSpeciesDL +{ +public: + cxxSpeciesDL() + { + g_moles = dg_g_moles = dx_moles = dh2o_moles = drelated_moles = 0; + } + LDBLE Get_g_moles(void) const {return g_moles;} + void Set_g_moles(LDBLE t) {g_moles = t;} + LDBLE Get_dg_g_moles(void) const {return dg_g_moles;} + void Set_dg_g_moles(LDBLE t) {dg_g_moles = t;} + LDBLE Get_dx_moles(void) const {return dx_moles;} + void Set_dx_moles(LDBLE t) {dx_moles = t;} + LDBLE Get_dh2o_moles(void) const {return dh2o_moles;} + void Set_dh2o_moles(LDBLE t) {dh2o_moles = t;} + LDBLE Get_drelated_moles(void) const {return drelated_moles;} + void Set_drelated_moles(LDBLE t) {drelated_moles = t;} + + LDBLE *Get_g_moles_address(void) {return &g_moles;} + LDBLE *Get_dg_g_moles_address(void) {return &dg_g_moles;} + LDBLE *Get_dx_moles_address(void) {return &dx_moles;} + LDBLE *Get_dh2o_moles_address(void) {return &dh2o_moles;} + LDBLE *Get_drelated_moles_address(void) {return &drelated_moles;} + +protected: + LDBLE g_moles; + LDBLE dg_g_moles; /* g_moles*dgterm */ + LDBLE dx_moles; + LDBLE dh2o_moles; /* moles*g*Ws/Waq */ + LDBLE drelated_moles; /* for related phase */ +}; +class cxxSurfDL +{ +public: + cxxSurfDL() + { + g = dg = psi_to_z = 0; + } + LDBLE Get_g(void) const {return g;} + void Set_g(LDBLE t) {g = t;} + LDBLE Get_dg(void) const {return dg;} + void Set_dg(LDBLE t) {dg = t;} + LDBLE Get_psi_to_z(void) const {return psi_to_z;} + void Set_psi_to_z(LDBLE t) {psi_to_z = t;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + LDBLE g; + LDBLE dg; + LDBLE psi_to_z; +}; +class cxxSurfaceCharge: public PHRQ_base +{ + +public: + + cxxSurfaceCharge(PHRQ_io *io=NULL); + cxxSurfaceCharge(struct surface_charge *, PHRQ_io *io=NULL); + virtual ~cxxSurfaceCharge(); + + class master *Get_psi_master(); + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + void add(const cxxSurfaceCharge & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + const std::string &Get_name() const {return this->name;} + void Set_name(const char * f) {this->name = f ? f : "";} + LDBLE Get_specific_area() const {return this->specific_area;} + void Set_specific_area(LDBLE d) {this->specific_area = d;} + LDBLE Get_grams() const {return this->grams;} + void Set_grams(LDBLE d) {this->grams = d;} + LDBLE Get_charge_balance() const {return this->charge_balance;} + void Set_charge_balance(LDBLE d) {this->charge_balance = d;} + LDBLE Get_mass_water() const {return this->mass_water;} + void Set_mass_water(LDBLE d) {this->mass_water = d;} + LDBLE Get_la_psi() const {return this->la_psi;} + void Set_la_psi(LDBLE d) {this->la_psi = d;} + LDBLE Get_capacitance0() const {return this->capacitance[0];} + void Set_capacitance0(LDBLE d) {this->capacitance[0] = d;} + LDBLE Get_capacitance1() const {return this->capacitance[1];} + void Set_capacitance1(LDBLE d) {this->capacitance[1] = d;} + LDBLE Get_sigma0() const {return this->sigma0;} + void Set_sigma0(LDBLE d) {this->sigma0 = d;} + LDBLE Get_sigma1() const {return this->sigma1;} + void Set_sigma1(LDBLE d) {this->sigma1 = d;} + LDBLE Get_sigma2() const {return this->sigma2;} + void Set_sigma2(LDBLE d) {this->sigma2 = d;} + LDBLE Get_sigmaddl() const {return this->sigmaddl;} + void Set_sigmaddl(LDBLE d) {this->sigmaddl = d;} + cxxNameDouble & Get_diffuse_layer_totals(void) { return this->diffuse_layer_totals; } + const cxxNameDouble & Get_diffuse_layer_totals(void)const { return this->diffuse_layer_totals; } + void Set_diffuse_layer_totals(cxxNameDouble & nd) {this->diffuse_layer_totals = nd;} + std::map &Get_g_map(void) {return g_map;} + void Set_g_map(std::map & t) {g_map = t;} + std::map &Get_z_gMCD_map(void) { return z_gMCD_map; } // z, exp(-zF Psi / RT) * fraction of dl water + std::map & Get_dl_species_map(void) {return this->dl_species_map;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + LDBLE specific_area; + LDBLE grams; + LDBLE charge_balance; + LDBLE mass_water; + LDBLE la_psi; + LDBLE capacitance[2]; + cxxNameDouble diffuse_layer_totals; + // workspace variables + LDBLE sigma0, sigma1, sigma2, sigmaddl; + std::map g_map; + std::map z_gMCD_map; + const static std::vector < std::string > vopts; + std::map dl_species_map; +}; + +#endif // !defined(SURFACECHARGE_H_INCLUDED) diff --git a/phreeqcpp/SurfaceComp.cxx b/phreeqcpp/SurfaceComp.cxx new file mode 100644 index 00000000..d0435db5 --- /dev/null +++ b/phreeqcpp/SurfaceComp.cxx @@ -0,0 +1,509 @@ +// SurfaceComp.cxx: implementation of the cxxSurfaceComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SurfaceComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurfaceComp::cxxSurfaceComp(PHRQ_io *io) +: +PHRQ_base(io) +// +// default constructor for cxxSurfaceComp +// +{ + formula_z = 0.0; + moles = 0.0; + totals.type = cxxNameDouble::ND_ELT_MOLES; + la = 0.0; + charge_balance = 0; + phase_proportion = 0.0; + Dw = 0.0; +} +cxxSurfaceComp::~cxxSurfaceComp() +{ +} + +void +cxxSurfaceComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Comp element and attributes + + //s_oss << indent0 << "formula=\"" << this->formula << "\"" << "\n"; + s_oss << indent0 << "formula_z=\"" << this->formula_z << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "la=\"" << this->la << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this->charge_balance << "\"" << "\n"; + s_oss << indent0 << "phase_proportion=\"" << this->phase_proportion << "\"" << "\n"; + s_oss << indent0 << "Dw=\"" << this->Dw << "\"" << "\n"; + s_oss << indent0 << "charge_name=\"" << this->charge_name << "\"" << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "phase_name=\"" << this->phase_name << "\"" << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "rate_name=\"" << this->rate_name << "\"" << "\n"; + } + + // totals + s_oss << indent0; + s_oss << "totals.dump_xml(s_oss, indent + 1); +} + +void +cxxSurfaceComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-formula_z " << this->formula_z << "\n"; + s_oss << indent0 << "-moles " << this->moles << "\n"; + s_oss << indent0 << "-la " << this->la << "\n"; + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "-phase_name " << this->phase_name << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "-rate_name " << this->rate_name << "\n"; + } + s_oss << indent0 << "-phase_proportion " << this->phase_proportion << "\n"; + s_oss << indent0 << "-Dw " << this->Dw << "\n"; + s_oss << indent0 << "-charge_name " << this->charge_name << "\n"; + s_oss << indent0 << "-master_element " << this->master_element << "\n"; + // totals + s_oss << indent0; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); +} + +void +cxxSurfaceComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + + bool master_element_defined(false); + bool charge_name_defined(false); + bool moles_defined(false); + bool la_defined(false); + bool charge_balance_defined(false); + bool formula_z_defined(false); + bool Dw_defined(false); + bool totals_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Surface for more processing + break; + + case 0: // formula + output_msg("-formula is obsolete in surface comp raw"); + break; + + case 1: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 2: // la + if (!(parser.get_iss() >> this->la)) + { + this->la = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la.", + PHRQ_io::OT_CONTINUE); + } + la_defined = true; + break; + case 3: // charge_number not used + parser.warning_msg("-charge_number identifier is obsolete."); + break; + + case 4: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 5: // phase_name + if (!(parser.get_iss() >> str)) + { + this->phase_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for phase_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->phase_name = str; + } + break; + + case 6: // rate_name + if (!(parser.get_iss() >> str)) + { + this->rate_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for rate_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->rate_name = str; + } + break; + + case 7: // phase_proportion + if (!(parser.get_iss() >> this->phase_proportion)) + { + this->phase_proportion = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for phase_proportion.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SurfaceComp totals.", + PHRQ_io::OT_CONTINUE); + } + totals_defined = true; + opt_save = 8; + break; + + case 9: // formula_z + if (!(parser.get_iss() >> this->formula_z)) + { + this->formula_z = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for formula_z.", + PHRQ_io::OT_CONTINUE); + } + formula_z_defined = true; + break; + + case 10: // formula_totals + parser.warning_msg("-formula_totals is an obsolete identifier."); + break; + + case 11: // Dw + if (!(parser.get_iss() >> this->Dw)) + { + this->Dw = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for Dw.", + PHRQ_io::OT_CONTINUE); + } + Dw_defined = true; + break; + case 12: // charge_name + if (!(parser.get_iss() >> str)) + { + this->charge_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for charge_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->charge_name = str; + } + charge_name_defined = true; + break; + case 13: // master_element + if (!(parser.get_iss() >> str)) + { + this->master_element.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for master_element.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->master_element = str; + } + master_element_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (charge_name_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_name not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (formula_z_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Formula_z not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (la_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_balance not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (Dw_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dw not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (master_element_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Master_element name not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (totals_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Totals not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSurfaceComp::add(const cxxSurfaceComp & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee.formula.size() == 0) + return; + + if (this->formula.size() == 0 && addee.formula.size() == 0) + { + return; + } + assert(this->formula == addee.formula); + assert(this->formula_z == addee.formula_z); + if (this->formula.size() == 0 && addee.formula.size() != 0) + { + this->formula = addee.formula; + } + + // this and addee must have same formula + // otherwise generate a new exchcomp with multiply + LDBLE ext1, ext2, f1, f2; + ext1 = this->moles; + ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + this->moles += addee.moles * extensive; + this->totals.add_extensive(addee.totals, extensive); + this->la = f1 * this->la + f2 * addee.la; + this->charge_balance += addee.charge_balance * extensive; + //if (this->phase_name != addee.phase_name) + if (Phreeqc::strcmp_nocase(this->phase_name.c_str(),addee.phase_name.c_str()) != 0) + { + std::ostringstream oss; + oss << + "Cannot mix two Surface components with same formula and different related phases, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->phase_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + + //if (this->rate_name != addee.rate_name) + if (Phreeqc::strcmp_nocase(this->rate_name.c_str(), addee.rate_name.c_str()) != 0) + { + std::ostringstream oss; + oss << + "Cannot mix two surface components with same formula and different related kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->rate_name.size() != 0) + { + //LDBLE phase_proportion; + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + if ((this->rate_name.size() != 0 && addee.phase_name.size() != 0) || + (this->phase_name.size() != 0 && addee.rate_name.size() != 0)) + { + std::ostringstream oss; + oss << + "Cannot mix surface components related to phase with surface components related to kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + +} +void +cxxSurfaceComp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + + this->totals.multiply(extensive); + + this->charge_balance *= extensive; +} +void +cxxSurfaceComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->formula)); + doubles.push_back(this->formula_z); + doubles.push_back(this->moles); + this->totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->la); + ints.push_back(dictionary.Find(this->charge_name)); + doubles.push_back(this->charge_balance); + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->phase_proportion); + ints.push_back(dictionary.Find(this->rate_name)); + doubles.push_back(this->Dw); + ints.push_back(dictionary.Find(this->master_element)); +} + +void +cxxSurfaceComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->formula = dictionary.GetWords()[ints[ii++]]; + this->formula_z = doubles[dd++]; + this->moles = doubles[dd++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->la = doubles[dd++]; + this->charge_name = dictionary.GetWords()[ints[ii++]]; + this->charge_balance = doubles[dd++]; + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->phase_proportion = doubles[dd++]; + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->Dw = doubles[dd++]; + this->master_element = dictionary.GetWords()[ints[ii++]]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("formula"), // 0 + std::vector< std::string >::value_type("moles"), // 1 + std::vector< std::string >::value_type("la"), // 2 + std::vector< std::string >::value_type("charge_number"), // 3 + std::vector< std::string >::value_type("charge_balance"), // 4 + std::vector< std::string >::value_type("phase_name"), // 5 + std::vector< std::string >::value_type("rate_name"), // 6 + std::vector< std::string >::value_type("phase_proportion"), // 7 + std::vector< std::string >::value_type("totals"), // 8 + std::vector< std::string >::value_type("formula_z"), // 9 + std::vector< std::string >::value_type("formula_totals"), // 10 + std::vector< std::string >::value_type("dw"), // 11 + std::vector< std::string >::value_type("charge_name"), // 12 + std::vector< std::string >::value_type("master_element") // 13 +}; +const std::vector< std::string > cxxSurfaceComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SurfaceComp.h b/phreeqcpp/SurfaceComp.h new file mode 100644 index 00000000..d3708acf --- /dev/null +++ b/phreeqcpp/SurfaceComp.h @@ -0,0 +1,70 @@ +#if !defined(SURFACECOMP_H_INCLUDED) +#define SURFACECOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "NameDouble.h" + +class cxxSurfaceComp: public PHRQ_base +{ + +public: + + cxxSurfaceComp(PHRQ_io *io=NULL); + virtual ~cxxSurfaceComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + void add(const cxxSurfaceComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + const std::string &Get_formula() const {return this->formula;} + void Set_formula(const char * f) {this->formula = f ? f : "";} + LDBLE Get_formula_z(void) const {return formula_z;}; + void Set_formula_z(LDBLE t) {this->formula_z = t;} + LDBLE Get_moles(void) const {return moles;} + void Set_moles(LDBLE t) {this->moles = t;} + cxxNameDouble & Get_totals() {return (this->totals);} + void Set_totals(cxxNameDouble & nd) {this->totals = nd;} + LDBLE Get_la(void) const {return la;}; + void Set_la(LDBLE t) {this->la = t;} + LDBLE Get_charge_balance() const {return this->charge_balance;} + void Set_charge_balance(LDBLE d) {this->charge_balance = d;} + const std::string &Get_charge_name() const {return this->charge_name;} + void Set_charge_name(const char * f) {this->charge_name = f ? f : "";} + const std::string &Get_phase_name() const {return this->phase_name;} + void Set_phase_name(const char * f) {this->phase_name = f ? f : "";} + LDBLE Get_phase_proportion(void) const {return phase_proportion;} + void Set_phase_proportion(LDBLE d) {this->phase_proportion = d;} + const std::string &Get_rate_name() const {return this->rate_name;} + void Set_rate_name(const char * f) {this->rate_name = f ? f : "";} + LDBLE Get_Dw(void) const {return Dw;} + void Set_Dw(LDBLE d) {this->Dw = d;} + const std::string &Get_master_element() const {return this->master_element;} + void Set_master_element(const char * f) {this->master_element = f ? f : "";} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string formula; + LDBLE formula_z; + LDBLE moles; + cxxNameDouble totals; + LDBLE la; + std::string charge_name; + LDBLE charge_balance; + std::string phase_name; + LDBLE phase_proportion; + std::string rate_name; + LDBLE Dw; + std::string master_element; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(SURFACECOMP_H_INCLUDED) diff --git a/phreeqcpp/System.cxx b/phreeqcpp/System.cxx new file mode 100644 index 00000000..ebae0af7 --- /dev/null +++ b/phreeqcpp/System.cxx @@ -0,0 +1,103 @@ +#include // std::replace + +#include "Phreeqc.h" +#include "System.h" +#include "SSassemblage.h" +#include "Solution.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SS.h" +#include "SSassemblage.h" +#include "Surface.h" +#include "cxxMix.h" +#include "Reaction.h" +#include "Temperature.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +cxxSystem::cxxSystem(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->solution = NULL; + this->exchange = NULL; + this->ppassemblage = NULL; + this->gasphase = NULL; + this->ssassemblage = NULL; + this->kinetics = NULL; + this->surface = NULL; + this->mix = NULL; + this->reaction = NULL; + this->temperature = NULL; + this->pressure = NULL; +} +cxxSystem::~cxxSystem(void) +{ +} +void +cxxSystem::Initialize(void) +{ + this->solution = NULL; + this->exchange = NULL; + this->ppassemblage = NULL; + this->gasphase = NULL; + this->ssassemblage = NULL; + this->kinetics = NULL; + this->surface = NULL; + this->mix = NULL; + this->reaction = NULL; + this->temperature = NULL; + this->pressure = NULL; +} +void +cxxSystem::totalize(Phreeqc * phreeqc_ptr) +{ + //initialize + this->totals.clear(); + //add solution + if (this->solution != NULL) + { + char token[MAX_LENGTH]; + strcpy(token, "O"); + this->totals[token] = this->solution->Get_total_o(); + strcpy(token, "H"); + this->totals[token] = this->solution->Get_total_h(); + strcpy(token, "Charge"); + this->totals[token] = this->solution->Get_cb(); + this->totals.add_extensive(this->solution->Get_totals(), 1.0); + } + if (this->exchange != NULL) + { + this->exchange->totalize(); + this->totals.add_extensive(this->exchange->Get_totals(), 1.0); + } + if (this->ppassemblage != NULL) + { + this->ppassemblage->totalize(phreeqc_ptr); + this->totals.add_extensive(this->ppassemblage->Get_assemblage_totals(), 1.0); + } + if (this->gasphase != NULL) + { + this->gasphase->totalize(phreeqc_ptr); + this->totals.add_extensive(this->gasphase->Get_totals(), 1.0); + } + if (this->ssassemblage != NULL) + { + this->ssassemblage->totalize(phreeqc_ptr); + this->totals.add_extensive(this->ssassemblage->Get_totals(), 1.0); + } + if (this->surface != NULL) + { + this->surface->totalize(); + this->totals.add_extensive(this->surface->Get_totals(), 1.0); + } + return; +} diff --git a/phreeqcpp/System.h b/phreeqcpp/System.h new file mode 100644 index 00000000..328ed235 --- /dev/null +++ b/phreeqcpp/System.h @@ -0,0 +1,89 @@ +#if !defined(SYSTEM_H_INCLUDED) +#define SYSTEM_H_INCLUDED +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxSolution; +class cxxExchange; +class cxxGasPhase; +class cxxKinetics; +class cxxPPassemblage; +class cxxSSassemblage; +class cxxSurface; +class cxxReaction; +class cxxTemperature; +class cxxPressure; +class cxxMix; + +class cxxSystem: public PHRQ_base +{ +public: + cxxSystem(PHRQ_io *io=NULL); + virtual ~cxxSystem(void); + void Initialize(void); + void Set_Solution(cxxSolution * entity) + { + this->solution = entity; + } + void Set_Exchange(cxxExchange * entity) + { + this->exchange = entity; + } + void Set_PPassemblage(cxxPPassemblage * entity) + { + this->ppassemblage = entity; + } + void Set_GasPhase(cxxGasPhase * entity) + { + this->gasphase = entity; + } + void Set_SSassemblage(cxxSSassemblage * entity) + { + this->ssassemblage = entity; + } + void Set_Kinetics(cxxKinetics * entity) + { + this->kinetics = entity; + } + void Set_Surface(cxxSurface * entity) + { + this->surface = entity; + } + void Set_Mix(cxxMix * entity) + { + this->mix = entity; + } + void Set_Reaction(cxxReaction * entity) + { + this->reaction = entity; + } + void Set_Temperature(cxxTemperature * entity) + { + this->temperature = entity; + } + void Set_Pressure(cxxPressure * entity) + { + this->pressure = entity; + } + void totalize(Phreeqc * phreeqc_ptr); + cxxNameDouble &Get_Totals(void) + { + return this->totals; + } + +protected: + cxxSolution * solution; + cxxExchange * exchange; + cxxPPassemblage * ppassemblage; + cxxGasPhase * gasphase; + cxxSSassemblage * ssassemblage; + cxxKinetics * kinetics; + cxxSurface * surface; + cxxMix * mix; + cxxReaction * reaction; + cxxTemperature * temperature; + cxxPressure * pressure; + cxxNameDouble totals; +}; + + +#endif // !defined(SYSTEM_H_INCLUDED) diff --git a/phreeqcpp/Temperature.cxx b/phreeqcpp/Temperature.cxx new file mode 100644 index 00000000..d67afe82 --- /dev/null +++ b/phreeqcpp/Temperature.cxx @@ -0,0 +1,423 @@ +// Temperature.cxx: implementation of the cxxTemperature class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Parser.h" +#include "Temperature.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxTemperature::cxxTemperature(PHRQ_io *io) + // + // default constructor for cxxTemperature + // +: cxxNumKeyword(io) +{ + countTemps = 0; + equalIncrements = false; +} +cxxTemperature::~cxxTemperature() +{ +} + +int +cxxTemperature::read(CParser & parser) +{ +/* + * Reads temperature data for reaction steps + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + // Number and description set in read_reaction_temperature + + PHRQ_io::LINE_TYPE lt; + bool done = false; + for (;;) + { + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token, str; + lt = parser.check_line(str, false, true, true, true); + + if (lt == PHRQ_io::LT_EMPTY || + lt == PHRQ_io::LT_KEYWORD || + lt == PHRQ_io::LT_EOF) + { + break; + } + if (lt == PHRQ_io::LT_OPTION) + { + this->error_msg("Expected numeric value for temperatures.", PHRQ_io::OT_CONTINUE); + break; + } + + if (done) + { + this->error_msg("Unknown input following equal increment definition.", PHRQ_io::OT_CONTINUE); + continue; + } + + // LT_OK + + for (;;) + { + if (done) break; + // new token + std::string token; + CParser::TOKEN_TYPE k = parser.copy_token(token, next_char); + + // need new line + if (k == CParser::TT_EMPTY) + { + break; + } + + // read a pressure + if (k == CParser::TT_DIGIT) + { + std::istringstream iss(token); + LDBLE d; + if (!(iss >> d)) + { + this->error_msg("Expected numeric value for temperatures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->temps.push_back(d); + } + continue; + } + + // non digit, must be "in" + if (k == CParser::TT_UPPER || k == CParser::TT_LOWER) + { + if (this->temps.size() != 2) + { + this->error_msg("To define equal increments, exactly two temperatures should be defined.", CONTINUE); + } + else + { + int i = parser.copy_token(token, next_char); + if (i == EMPTY) + { + error_msg("To define equal increments, define 'in n steps'.", CONTINUE); + } + else + { + std::istringstream iss(token); + if ((iss >> i) && i > 0) + { + this->equalIncrements = true; + this->countTemps = i; + } + else + { + error_msg("Unknown input for temperature steps.", CONTINUE); + } + } + done = true; + } + if (k == CParser::TT_UNKNOWN) + { + error_msg("Unknown input for temperature steps.", CONTINUE); + } + } + } // tokens + } // lines + return lt; +} + +void +cxxTemperature::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_TEMPERATURE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-count_temps " << this->Get_countTemps() << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // Temperature element and attributes + + s_oss << indent1; + s_oss << "-temps " << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->temps.begin(); + it != this->temps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } +} + +void +cxxTemperature::read_raw(CParser & parser, bool check) +{ + LDBLE d; + CParser::TOKEN_TYPE k; + // clear steps for modify operation, if pressures are read + bool cleared_once = false; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read temperature number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool equalIncrements_defined(false); + bool countTemps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in TEMPERATURE_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // temps + if (!cleared_once) + { + this->temps.clear(); + cleared_once = true; + } + while ((k = + parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temps.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->temps.push_back(d); + } + } + opt_save = 0; + useLastLine = false; + break; + + case 1: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 2: // countTemps + if (!(parser.get_iss() >> this->countTemps)) + { + this->countTemps = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for countTemps.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + countTemps_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Equal_increments not defined for REACTION_TEMPERATURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (countTemps_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Count_temps not defined for REACTION_TEMPERATURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +/* ---------------------------------------------------------------------- */ +LDBLE cxxTemperature:: +Temperature_for_step(int step_number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determine pressure of reaction step + */ + LDBLE t_temp; + if (this->temps.size() == 0) + { + t_temp = 1; + } + else if (this->equalIncrements) + { + if (this->temps.size() != 2) + { + error_msg("Number of temperatures not equal to 2 for equal increments.", 0); + } + if (step_number > this->countTemps) + { + t_temp = this->temps[1]; + } + else + { + LDBLE denom; + denom = (this->countTemps <= 1) ? 1 : (LDBLE)this->countTemps - 1; + t_temp = this->temps[0] + ( this->temps[1] - this->temps[0]) * + ((LDBLE)step_number - 1) / (denom); + } + } + else + { + if (step_number > (int) this->temps.size()) + { + t_temp = this->temps[this->temps.size() - 1]; + } + else + { + t_temp = this->temps[(size_t)step_number - 1]; + } + + } + + return (t_temp); +} +int cxxTemperature:: +Get_countTemps(void) const +{ + if (equalIncrements) + { + return this->countTemps; + } + return (int) this->temps.size(); +} +void +cxxTemperature::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + { + ints.push_back((int) this->temps.size()); + for (size_t i = 0; i < this->temps.size(); i++) + { + doubles.push_back(temps[i]); + } + } + ints.push_back(this->countTemps); + ints.push_back(this->equalIncrements ? 1 : 0); +} + +void +cxxTemperature::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + { + int count = ints[ii++]; + this->temps.clear(); + for (int i = 0; i < count; i++) + { + this->temps.push_back(doubles[dd++]); + } + } + this->countTemps = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("temps"), //0 + std::vector< std::string >::value_type("equal_increments"), //1 + std::vector< std::string >::value_type("count_temps") //2 +}; +const std::vector< std::string > cxxTemperature::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Temperature.h b/phreeqcpp/Temperature.h new file mode 100644 index 00000000..6af70b38 --- /dev/null +++ b/phreeqcpp/Temperature.h @@ -0,0 +1,42 @@ +#if !defined(TEMPERATURE_H_INCLUDED) +#define TEMPERATURE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" + +class cxxTemperature:public cxxNumKeyword +{ + + public: + cxxTemperature(PHRQ_io *io=NULL); + ~cxxTemperature(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = false); + int read(CParser & parser); + LDBLE Temperature_for_step(int step_number); + std::vector & Get_temps(void) {return temps;} + const std::vector & Get_temps(void)const {return temps;} + int Get_countTemps(void) const; + void Set_countTemps(int i) {countTemps = i;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < LDBLE >temps; + int countTemps; + bool equalIncrements; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(TEMPERATURE_H_INCLUDED) diff --git a/phreeqcpp/Use.cpp b/phreeqcpp/Use.cpp new file mode 100644 index 00000000..c8b87979 --- /dev/null +++ b/phreeqcpp/Use.cpp @@ -0,0 +1,78 @@ +#include +#include "Use.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +cxxUse::cxxUse() +{ + this->init(); + +} + + +cxxUse::~cxxUse(void) +{ +} +void cxxUse:: +init(void) +{ + solution_in = false; + n_solution_user = -999; + solution_ptr = NULL; + + pp_assemblage_in = false; + n_pp_assemblage_user = -999; + pp_assemblage_ptr = NULL; + + mix_in = false; + n_mix_user = -999; + mix_ptr = NULL; + n_mix_user_orig = -999; + + reaction_in = false; + n_reaction_user = -999; + reaction_ptr = NULL; + + exchange_in = false; + n_exchange_user = -999; + exchange_ptr = NULL; + + kinetics_in = false; + n_kinetics_user = -999; + kinetics_ptr = NULL; + + surface_in = false; + n_surface_user = -999; + surface_ptr = NULL; + + pressure_in = false; + n_pressure_user = -999; + pressure_ptr = NULL; + + temperature_in = false; + n_temperature_user = -999; + temperature_ptr = NULL; + + inverse_in = false; + n_inverse_user = -999; + inverse_ptr = NULL; + + gas_phase_in = false; + n_gas_phase_user = -999; + gas_phase_ptr = NULL; + + ss_assemblage_in = false; + n_ss_assemblage_user = -999; + ss_assemblage_ptr = NULL; + + trans_in = false; + advect_in = false; +} + + diff --git a/phreeqcpp/Use.h b/phreeqcpp/Use.h new file mode 100644 index 00000000..a1ff1094 --- /dev/null +++ b/phreeqcpp/Use.h @@ -0,0 +1,159 @@ +#if !defined(USE_H_INCLUDED) +#define USE_H_INCLUDED +class cxxPPassemblage; +class cxxMix; +class cxxReaction; +class cxxExchange; +class cxxGasPhase; +class cxxPressure; +class cxxTemperature; +class cxxSSassemblage; +class cxxKinetics; +class cxxSurface; +class cxxSolution; + +class cxxUse +{ +public: + cxxUse(); + virtual ~cxxUse(void); + void init(void); + + bool Get_solution_in(void) const {return this->solution_in;} + bool Get_pp_assemblage_in(void) const {return this->pp_assemblage_in;} + bool Get_mix_in(void) const {return this->mix_in;} + bool Get_reaction_in(void) const {return this->reaction_in;} + bool Get_exchange_in(void) const {return this->exchange_in;} + bool Get_kinetics_in(void) const {return this->kinetics_in;} + bool Get_surface_in(void) const {return this->surface_in;} + bool Get_pressure_in(void) const {return this->pressure_in;} + bool Get_temperature_in(void) const {return this->temperature_in;} + bool Get_gas_phase_in(void) const {return this->gas_phase_in;} + bool Get_inverse_in(void) const {return this->inverse_in;} + bool Get_ss_assemblage_in(void) const {return this->ss_assemblage_in;} + bool Get_advect_in(void) const {return this->advect_in;} + bool Get_trans_in(void) const {return this->trans_in;} + + void Set_solution_in(bool tf) {solution_in = tf;} + void Set_pp_assemblage_in(bool tf) {pp_assemblage_in = tf;} + void Set_mix_in(bool tf) {mix_in = tf;} + void Set_reaction_in(bool tf) {reaction_in = tf;} + void Set_exchange_in(bool tf) {exchange_in = tf;} + void Set_kinetics_in(bool tf) {kinetics_in = tf;} + void Set_surface_in(bool tf) {surface_in = tf;} + void Set_pressure_in(bool tf) {pressure_in = tf;} + void Set_temperature_in(bool tf) {temperature_in = tf;} + void Set_gas_phase_in(bool tf) {gas_phase_in = tf;} + void Set_inverse_in(bool tf) {inverse_in = tf;} + void Set_ss_assemblage_in(bool tf) {ss_assemblage_in = tf;} + void Set_advect_in(bool tf) {advect_in = tf;} + void Set_trans_in(bool tf) {trans_in = tf;} + + int Get_n_solution_user(void) const {return n_solution_user;} + int Get_n_pp_assemblage_user(void) const {return n_pp_assemblage_user;} + int Get_n_mix_user(void) const {return n_mix_user;} + int Get_n_mix_user_orig(void) const {return n_mix_user_orig;} + int Get_n_reaction_user(void) const {return n_reaction_user;} + int Get_n_exchange_user(void) const {return n_exchange_user;} + int Get_n_kinetics_user(void) const {return n_kinetics_user;} + int Get_n_surface_user(void) const {return n_surface_user;} + int Get_n_pressure_user(void) const {return n_pressure_user;} + int Get_n_temperature_user(void) const {return n_temperature_user;} + int Get_n_gas_phase_user(void) const {return n_gas_phase_user;} + int Get_n_inverse_user(void) const {return n_inverse_user;} + int Get_n_ss_assemblage_user(void) const {return n_ss_assemblage_user;} + + void Set_n_solution_user(int i) {n_solution_user = i;} + void Set_n_pp_assemblage_user(int i) {n_pp_assemblage_user = i;} + void Set_n_mix_user(int i) {n_mix_user = i;} + void Set_n_mix_user_orig(int i) {n_mix_user_orig = i;} + void Set_n_reaction_user(int i) {n_reaction_user = i;} + void Set_n_exchange_user(int i) {n_exchange_user = i;} + void Set_n_kinetics_user(int i) {n_kinetics_user = i;} + void Set_n_surface_user(int i) {n_surface_user = i;} + void Set_n_pressure_user(int i) {n_pressure_user = i;} + void Set_n_temperature_user(int i) {n_temperature_user = i;} + void Set_n_gas_phase_user(int i) {n_gas_phase_user = i;} + void Set_n_inverse_user(int i) {n_inverse_user = i;} + void Set_n_ss_assemblage_user(int i) {n_ss_assemblage_user = i;} + + cxxSolution * Get_solution_ptr(void) const {return this->solution_ptr;} + cxxPPassemblage * Get_pp_assemblage_ptr(void) const {return this->pp_assemblage_ptr;} + cxxMix * Get_mix_ptr(void) const {return this->mix_ptr;} + cxxReaction * Get_reaction_ptr(void) const {return this->reaction_ptr;} + cxxExchange * Get_exchange_ptr(void) const {return this->exchange_ptr;} + cxxKinetics * Get_kinetics_ptr(void) const {return this->kinetics_ptr;} + cxxSurface * Get_surface_ptr(void) const {return this->surface_ptr;} + cxxPressure * Get_pressure_ptr(void) const {return this->pressure_ptr;} + cxxTemperature * Get_temperature_ptr(void) const {return this->temperature_ptr;} + cxxGasPhase * Get_gas_phase_ptr(void) const {return this->gas_phase_ptr;} + class inverse * Get_inverse_ptr(void) const {return this->inverse_ptr;} + cxxSSassemblage * Get_ss_assemblage_ptr(void) {return this->ss_assemblage_ptr;} + + void Set_solution_ptr(cxxSolution * p) {this->solution_ptr = p;} + void Set_pp_assemblage_ptr(cxxPPassemblage * p) {this->pp_assemblage_ptr = p;} + void Set_mix_ptr(cxxMix * p) {this->mix_ptr = p;} + void Set_reaction_ptr(cxxReaction * p) {this->reaction_ptr = p;} + void Set_exchange_ptr(cxxExchange * p) {this->exchange_ptr = p;} + void Set_kinetics_ptr(cxxKinetics * p) {this->kinetics_ptr = p;} + void Set_surface_ptr(cxxSurface * p) {this->surface_ptr = p;} + void Set_pressure_ptr(cxxPressure * p) {this->pressure_ptr = p;} + void Set_temperature_ptr(cxxTemperature * p) {this->temperature_ptr = p;} + void Set_gas_phase_ptr(cxxGasPhase * p) {this->gas_phase_ptr = p;} + void Set_inverse_ptr(class inverse * p) {this->inverse_ptr = p;} + void Set_ss_assemblage_ptr(cxxSSassemblage * p) {this->ss_assemblage_ptr = p;} + +protected: + bool solution_in; + int n_solution_user; + cxxSolution *solution_ptr; + + bool pp_assemblage_in; + int n_pp_assemblage_user; + cxxPPassemblage *pp_assemblage_ptr; + + bool mix_in; + int n_mix_user; + cxxMix * mix_ptr; + int n_mix_user_orig; + + bool reaction_in; + int n_reaction_user; + cxxReaction * reaction_ptr; + + bool exchange_in; + int n_exchange_user; + cxxExchange * exchange_ptr; + + bool kinetics_in; + int n_kinetics_user; + cxxKinetics *kinetics_ptr; + + bool surface_in; + int n_surface_user; + cxxSurface *surface_ptr; + + bool pressure_in; + int n_pressure_user; + cxxPressure *pressure_ptr; + + bool temperature_in; + int n_temperature_user; + cxxTemperature *temperature_ptr; + + bool inverse_in; + int n_inverse_user; + class inverse *inverse_ptr; + + bool gas_phase_in; + int n_gas_phase_user; + cxxGasPhase * gas_phase_ptr; + + bool ss_assemblage_in; + int n_ss_assemblage_user; + cxxSSassemblage *ss_assemblage_ptr; + + bool trans_in; + bool advect_in; +}; +#endif // !defined(USE_H_INCLUDED) diff --git a/phreeqcpp/UserPunch.cpp b/phreeqcpp/UserPunch.cpp new file mode 100644 index 00000000..39ea8955 --- /dev/null +++ b/phreeqcpp/UserPunch.cpp @@ -0,0 +1,32 @@ +#include "UserPunch.h" +#include "Phreeqc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +UserPunch::UserPunch(int n, PHRQ_io *io) +: cxxNumKeyword(io) +{ + this->PhreeqcPtr = NULL; + this->rate = NULL; +} + + +UserPunch::~UserPunch(void) +{ + if (this->rate != NULL) + { + if (this->PhreeqcPtr != NULL) + { + this->PhreeqcPtr->rate_free(this->rate); + delete this->rate; + } + } + this->PhreeqcPtr = NULL; + this->rate = NULL; +} diff --git a/phreeqcpp/UserPunch.h b/phreeqcpp/UserPunch.h new file mode 100644 index 00000000..81d87168 --- /dev/null +++ b/phreeqcpp/UserPunch.h @@ -0,0 +1,39 @@ +#if !defined(USERPUNCH_H_INCLUDED) +#define USERPUNCH_H_INCLUDED +#include +#include // std::string +#include "NumKeyword.h" +class Phreeqc; +class UserPunch:public cxxNumKeyword +{ +public: + UserPunch(int n=1, PHRQ_io *io=NULL); + ~UserPunch(void); + + // headings + // + std::vector &Get_headings() {return this->headings;} + const std::vector &Get_headings()const {return this->headings;} + + void Set_headings(std::vector & h) {this->headings = h;} + + // phreeqc pointer + // + Phreeqc * Get_PhreeqcPtr() {return this->PhreeqcPtr;} + const Phreeqc * Get_PhreeqcPtr()const {return this->PhreeqcPtr;} + + void Set_PhreeqcPtr(Phreeqc * p) {this->PhreeqcPtr = p;} + + // rate + // + class rate * Get_rate() {return this->rate;} + const class rate * Get_rate()const {return this->rate;} + + void Set_rate(class rate * r) {this->rate = r;} + +protected: + std::vector headings; + class rate * rate; + Phreeqc * PhreeqcPtr; +}; +#endif // !defined(USERPUNCH_H_INCLUDED) diff --git a/phreeqcpp/ZedGraph.dll b/phreeqcpp/ZedGraph.dll new file mode 100644 index 0000000000000000000000000000000000000000..8b150d8249231da8eaaa67bc920c1f18b25ce9c2 GIT binary patch literal 307200 zcmeFad3;<~bqD_R-kUd@G}6<^Gx8E!@s=0ONVX-%v7E$`vnOOD39%$+-$-I7&mGw* zWIT%4mnI=lC?o_LLIVW~DO(GrKuV!dAZdV>9TQRtlqH0vC8fpreZS}2w~Q=1q5XY6 zzdwGN&u8Ad_uS>2d+xdCp1Z!Yr|+~J%d%YjpL)u&K7u!Y6Y@O#V-CTk6@OE*K9qmx z$d7cK_0W+QUU&V>!0tx){KgeG4qSD`O*e&m2Clqjpt1L+f$MJ?IQiTQ25tICcsh_`Hh*x~#GmW2|;za0Fh z$3Dv%-gC_h_P~GPK2xqar8^acMZbKwG1IsTK%osZvTW9!c&C07V90Renq46xl2!{e zOFYI;{U$8y#FY10=d&DmmIKdn;8_kl%YkP(@GJ+O<-oHXc$Ndta^P7G{Qrmp>p8B* zk7HBY%2=;_xothsXIpuJVyJnmZ9VB()_DV#HD_Rld#&ErbK36TRUh8j_0!AVfA;pD zz3>YU{OU9Pd!PTi6OQ?df4b@Ri{BF5a_`vrpZ(zP-m&@KiMKuQqVIkEBNx5*)Nj4z zoX@Sl{`ofyeg8Xe|Nh@yef%juzWd1=N7vqe%+GH=E?@inMc?ec>ftm0XV3A!{M}!@ z>-j&sdGJkZ&f9YOlKhje`puhnyfNH({)2D&)_Ldd>R&u^({j6cJZ1w1SK3ZX8~|*M z0OA1Pga{xG0LCMLH~^T40O9}u182=t8~|*K0OA1PqzE7m0FI3S;s9WC1P})RV-Y|c z0Bnf>;sD?{0`NX23J?bXn<9WXENkg{+ggH(>98!{UQ-Nw%PQE-KEx{Q&t`*Oc)#rp zA{_GvHfARBzLjM?GTmalZAn+qbWV#2_h&s#hhz(;1G6}HG!I{WrQ!|Gax5<_194?T zw}L9+Wq=j-7yH_Xif9_%l+c7?wbLYJsV__C4I)h$o$yLzoF+KRbM3I^IbsrChKDqYvC%6me4=08}D?H~?4`0mK2o$_OBitq`{XK=4|@07Rfy#84anNC4$Big9EV zLmY^)Is%9TfHe_7902r30C50-x>GFT002Uu0C50-St|vI1ArqUfH(kH8Ue%sz)%Db z2LQtnKpX(n2sj^Qp~JRkz_&v17nX9f9tF}dKX4rbFSVO|JHwtGd;qDCk-(AjdK1of zI$YqERffERDhpc6x0{emx4uf>A*u?Zwn%-I@Kz6^)@ubX#e1+q87{APEVQ(A+VyQ| zA-T*kPU{3OLt+E;!a3#y5|2h4z8QCL#NHR+9lV@@o7Ce3ub?jqdSzSCtLT$*pe-9( zm?O)sNRpE|fak_XC@$^+$T%R}ihKvN#%MxOqDv`C5mue6X6PeThi z{B-pzj6V!5lE>54GwZ9po)6;7Goj__`0|WsQNA#0#njPX*-gw*3n$L!d#U=vDkOfW z({2(dp1~?49g>LvZS&#)V0i=(2LMM!0C51YAp(d)`UTnqm_SeTdvHD4>QF_sXT79v zRq8{<2D+ZzGjDV)?-rwLw~4Y(dY?!qA3L7tdFUVu=0$=-^nBlej3=%=G%YCJ zNQ&`Z8&NKfD{Z?8<={F2v{_+~gKnfn|6mdTML0hI#f(xQvfZLzQfPprXb%9!#qLU;o-NHIJ1VG!+xFtmUe9hE!rX8;7y`S8O~bSGX9 zza6Xwpq$Bu<49HZV7)RAKjVA7cCXXxhKr!&>zCS1@}&-v__njTyzhqpLvl0U8G#v` z+q)K-m2-Zs-ks0-IfVWcO0Ar&-By^LQkeP;YUd51$jHR4Pe&!2fN!+R6l)ti!%L%}6B)F@1f4|BYP+fx z=e5p&-_A-^LHDu7`h)kOJZlHD^;xw;iHuff{iZ0#>;!lcWLp_@ia|C`&t@PK z@i54|)|mj?`}nL@gQHQ@b^*q&^VSu@#C!5^2z%v|C!LiQhAW(_yJuRxi z?AS|K?P8jFcSG4ZK4J-=qIo7>`En*T|Ep;mu3Ezn#F^c9) zZ2I6VXzy@gXUrY4O=W3Bv1a&A)r=?NZz?2b zd%=C+E(BGoBM<+Z;a>}YsShvsQ;oo}cbyUb#6(yW0p4H$Qe)XD%cbjS#ak9SJ70T) zu5{bV_jaHou)^!n#Kt}cU9Fma>;X8v%J`oXvS%$st&I9XdENSX`nI#a>xEAsRyhOh zel)Z@dL4{4SJ=%9Sylj!1weh(5Ysz_%`?bO0Tl&Y34rhJJqKW@7n9K4Lx1>Q_E=rL zAUCc7$#HW=5{Y8+!q<1OU-P}@6+pUoJM=;Qh$tfk?jcZ9egU)>o{uuz@jUbT!@LveYL=b3y z!Fz=AJMZTse&?&ccb}h+_?=(uXW?C9*q>kO=ipst*q=WH``Z`}CbQj5fmg82!N(OS z(@;_SpRrQ_wwvIaJGMofeRQs)Q|vsoEO)ADxpM|4Kp~Va&>Jftk{iNKDU3ZuW_eXH zDvG^OEi^}?L;({S9r?t#rl^b*BgiLf`N%nL2Umm1)017cmj{o~%ea-`O#nfIfdA;4 zRwJ*nOX+aG=Vp~Sm&|W-J&CevVyg#Nf8YaUKo00 zf2r3yP$|LcR-uG}=)ypBje+O|e@Vp643qci+s=B`nd{K}!bcfqdPB4vk>&4N8_ONY z4=k}hV_mvR?G4t!MsN%PzLV*-MAtcCi4vryz<}+DB)MM64en;fPWTlR(#=MGH4(3? zlwI_WPWV-kvakz;xUhonGSPguI$3soS0jWIDCNkPD&>r%`I^XK*o_9{f*Em|4AOvO zPRI!1Hc4Z+?K!?D+y-YnaK&*pm+ENBaiLzJiw+F1kM|BI`CYS-4l3Nw=fF5B#2uZOp7DWI4TDaU*Lnkf!$zz+A zjGy#k@#0}w6R(-rc(eA%YxNZ#4~;m+85z@$n!R|}&2 zM9gv`X1?p^?m`pF-6h%kIYXguFz zA|oA!Lh8*nX(;p-4i=GOZ!r-OL#3(*5f#i;lc~^q4*2~%sdXy3bI7=}^lk9nh^A8? zi?T>HbaV`C@CacljoqJW@_jABA$DSOpA_RjFX`5|7+kPElGLH#>WpwbzzQ=%d!;&f zKR_IVI2LlTV59j?eVfWp!E-~5Y&|a{uc@N9Mx-`7DdGnouFfR#&}1vsgu!Y)x!^Q86ZlVV73lAaARW&*I9b{o+c?$bsQ z+q#_5L1KLv0tPMQyUj}wP09)_F7HV{lheY7uR*#NBedm4yGC1X3CZ;5mj-eHD$X( zBqf|dg6@O~OeG0TWct*zvbg|EDy+pD>@BQ?U9A8*1d`271rW-@s;OT+`Kc8*-QO|N zhp>6DjZT$$lBW6%etziMO1V&53rQ{91pzDEH9vX=(Q75Y0999jfu4b?`*NjRM3Ssu zybBt$c&H>rBq{nuKg%S3e#^824k!zZB{Bda#y$^`GL=BJE`o2$+WIUa%nwtYFdu5T z()!$jDX2b73fdupBjR5gNgUXQB!)(uPGgA!RY@sl$Ige2s+2o7V>lTVPCB1X>3jq~ zSl&sNcitt{m?q;eQCZPx^z);(5q@wW8R2)LVmf`y9^P^N_+?K$^;D(Yl_pd=ye{eR zl+di-#ft7an9##SolofesBMHE94Lf#fzU1=vz~@fNYC$K1gN!Us{Nq;xTm-0;QrX} z^$*b>tJShrfSqd zq-(hdfnDhC156$iBxiQ?)RjX6)XS1*Fe!N<))XAo$zU3<+FI4qL(3%|NeOmD@h;YQ z=;#YM?T=c}h5evEJykuNHC^&E?W7JF?+A>Eq?;@{d%)F>taO(lIkooiJ=;kSig9(g z9_^&JAd2Q|_(yc*CA~{T9s(F3QKu>aOM90}RZD<$xnT`aWTXJ5QTo$8V!HR!P)`R9 zy?N;oXI?<_tAJqvd&6=O4yu}R=PHPV{K{R4vs5W}*8*tSZrbhLs=+WTlmH}gm){Ll zgVv~~e5Kq|+v=BK+4saLa&TjoNRXDGlO)U}mas0Rqs#9>Ry}@?pF_X*H&C<1sj`pE zx~9s#y=A|+)9>2p`#b&Koxx=>tNgMKQF{wv1>OEyvuJ_&26q$_(Hn(HFqmAw5Z(?3 zZ2mZkIuHIwgu^(BmZz#Z?~(PZy^DG)y?rtpS-;M&$f{HQRKKq>vMr4Wb6qJ!8ZEpH zxz=h6g}j;}u)>ox;cEa=AzmN=gYGKyRn_P#)s-lm=*s?ub>%YX%4Mo67yFn3IfSlU z>}z_&jdkU+dAhP6xcd*%l>znRCH89{w?#kN){)NGv}5$9?Ob<*Bk)=1OU?ivqO^lc z*JrG6+ZOk?`E~yYJ%fqvNfk=$&~TnBS7hK$;$_bqt|QZnZFnEY1O|b!-CNY@CRT z$xn&UVYRjxr(?A!e#`+8VE9MQ3FEY_);8muuv)O$T|2mf5*>UR7P@U8@NUO@=1PEq zs|W|=bur{>19^Q6xrUH|-hmFQ8eA)GE?*gU1G;v1@O*%0uEV1>0(8ArrDAiV2xiML z33db1sF(9GJK+s>5he=^UT`DaGuxr7WXqvWxP?c<51Zby!tcxAun+l$0{~XSf1xlp ziKy{rAP;XsdKmr!z@S$Fn4lB@EL8<`DI%=(&ioLjCi)MvSj+)oH2IWXx0Tg7bOhX1 zsP;`I3`w@wIxDUYcgLPe_y^)XA2dag=#C?4fJCa!ych*)LB1gxO*)TGoaja&$^>Y% zup#*pT#*l(x^Q<;=dP-Daiz8w?&*YZp$$Rq*XW*3r(U+3Xaa5>+~Qjr z9eU~+BI!G5!>8t%v!eZzoL~GF@_re`+ zLFMju*6z1b8%JrUS=Ps_^23(nqL=1g*)M!0+OL$gFwJ5?Nq`5Px>r{BL}3D$8Kdmy zB7zD$gr!ElT(7LChBpIgueC-Rfx^P;0qTO$!getg{2`tiP=Q52n0C!Ch~WTBixTFB zFJz1h@d$6hg9{lF+80EPT**pXmWRoCr%AqZhP`dLl@(FQ)-ij6ron9!Exh()_}EJ?T7b1} zqsX(h7VI5@{{p-VtSbB<=Tu78C4gOnf0jV}*bX4-JhzSdGJB%i>&xE1NmT*(MN+-+ zYN5red(k|!?1tsy^+zGx@m^}uq&EGLR%xA*LRr~%MkFiH@Q8yh1Z$D+N78vq1s5p7 zWO1PrYYVpYs+f+G;Y-nTMzh#h0Sg|iXH(CHvnV#ANr(E#{-wQkx>I zA&-u-Htyi^fQ~01QWp9&SPJV9D_+1jyHcoa1gS(8-U?jnR;mX(5#dYG?1e7^AlwCg zj3sAG5DEXZIAw&4e6)t;43dDY1@d+zvN8j6)sFSzg=sh+G>o;;a8xRDgZJN{#?zDu zV-_alLp9haLWu8#nvOn6VW>IBlo%9I88R4*Te`l(ngEUH^uZ^JM^anGS{W*5E8-VC z$uwkIfTU)=k6~d)x@L@y_N{>pQ61#0(;=$^A|H5qW5>hmhA%-jONR66SUQS6h>)eD z*mX&=hDz92!sv^2iw$^nxhg$Rc05xNa>sN1RE;L9IG#*!blN#Lggz#f)RlN~CB!u9 zmmwghTNYk|iOb4?j^HCeqbyaW%bAOC0`W#}uXNR}*8Q@1K`UxR@Sb7ta`r$c=rR53 zXAEZDCk>S|4>NjU*)5SqjfwoOd@8qa8&SW?lwW7$m3TM!Ymf?i&#_=n9HPD^P+qMs zXLzHEeZAMLiRl_^fPM??SPwQ!2s;^FY?TnCm&*O?gDMv|wZHVOvoF>9V@8S8+xNA zv%zknD{#l=09R*ZaY$2AJd1Uv=trpf8aAu3jn%Q6iEl%QtgMVjxc^dQ#ACG8HcU7~ zQ`*yMOs3ywTw|{_(L-T3k1cP<4PTCug3+8eA)k+g9&pC)fh&^|lucbJPmfbF>wQg1 z%El;+o*s7qbB4E;GE>+hw7z4ogx!puJJn-r`eIgBaAZ|ysuL@|vO1BQ>dNYpPmhyv zcX_*ZhWe#1o0r2PS?tBirbrUyIX)?~ zyYA*cP3V=WR##?sMO!8$8^xHW85=#4aBnK|DPvSAqxyLKrOegtm~0L&-DbNYwIHXiBfGDi#mmxh60 z5Lo%19z>eRsW!+J5S&#a_tS8>V;AD-=n%>oH~?{$QC3Eklqk+kT`3RSgYjy6NO^kP zN#%$y+ZbMnG`_c@u!Dxq`d*Y6x`S0Vs>OZyd$qxQ90Ggs(=P>$3W$|Bv9`06*Ua#}o3M zsuTu?B!a&~qvQKid_(ZK2ONVNYqhjvSb|vNOHS-nCvU$FeOmEgP-hm0QZ~Rwz9$FA zIyztroSsEzjqaYu`%GvGAhGnAS$&C#RSicFH};_C_N2d0VH|n47s_NMta5pCVg5E0 zkL$UxtKIreJ>6En$_;NPytB76xEPL|y&3LVE$rM;#>%z?VRN0#3^V988icJT*t&fu z6Xtp|Yb1rAn+{$J2>pykKlDnWhD8QJSuy#QdQ04oRR~^>M;W>{d;=UNu)DXLdltDx zNsx0{1HETVm!TmU{Fvl2A~jPJd2DMyGnS!Ir=Q0>nc3CmWh-|o^Kx*Iaa=5{W0_Fc zpXI8ciC{oE1u<&0WSD02XDAP9zx+u@=Y7$BB3CYp!lV{#-%)UTXr(>#CKRGYkAg6F z^a)7L%v}J7cjK|Y8&ZUMnF9CN7BI5bctF7X`K#Bh?k({093(SRS+uP)5w=!j@slEp z_2HSfAPel33Ezr$HiWh;R7d+UM7$TS@NIa&eIMQEKY_V3*M>R9nYBi@C{B(CHv`X6 zX*|nj{tU^9=YG5+Jh1!Tv0$!IIf5X`b3O7zZ-#N6p}lYBohHveXP$QWuEXXvFraxk zumyayN&f7acOy;fx#acCd+0o0oPR;*1$62(&dhrWxG)8H9|6xJAb0>ywU7tqkmo_; z`NnrSK?aM)#9HA?ykts26IVi5vL*_u{DZ9W4*(Lv1u&_pXu=hQVrXfgjo&2sGnPbJ z;ivN_xN7pJ8~Oj$y!=_XfmfC>8~7o~nSM;J!B__xdMY83z|BH8x;x?`(ZAw^C24fR zmjm8E5Cz5zg%D$$g1r)&eFBg3w#{IWU!a9ju&dl&hWSy+HvC(2P#40Mwn}JLef(H%cPC}rI3sl4s2inF!{@y^KDa-`zrwOS9pTP^_Ec-04 zz#A5Z-w)(~>G^_UG@WS{7i1a7&+yPoudOQ#1K9A3_GPobs+V(>5v;0&m=*BqZnaRe zD~0xhE7C`(PN-NXT%dd6X!{MgmIb^p4?-}p;OvEh=m0IC(KFLMV^2BojQtm%;b##k z{2U(9+NN$wN_Nh}a*WH-IuDH0k+$8WTM8nlfG~&955U;lk*gfO>6-aRT93P88KJ5BfYQEN1vU&7jlVT-&PNC{;*tvycX% z_=Scxgx~l-A^dI%p}12B#eXoue{M%ObcjNsq-^_o&X${&!E@|?*Cv|{zl~gQ5?=2n z2+;PaOgP_n2CIVoq|Bw&C!p)mhP|0DA+wn;;{g*mg#18IKnb4zf~UhXKT<8fiK?ZT zRr3N!0yKMU4F4)3@=}355+cu#7d?b0;-P!w7Ql^){XgJFSvcQx5#Dv_`yu9>3xt%w znpiUPb-X)ap`a2Ax!{|4n!_l0U3W;GQ?P??Nu(@fcji~G#1$qcGOS7k=aH^&naxhJg4OZ z-vK<_k4M2<(=}!TcAz}_MF*DOjmu{q1$<58RL9y~3BD)p^Wd%;H+GTj?l*2spzmI6 z+`HjkV%)dDz0|m04)-$Sz7y^v#C@PVM|1{j9^v4kz+*54=ic<8IWA$VMyP#h;jqq=LdaRWvq9v#nC7~v?8aU0LIkfhh?pswSJhj% zD$snSO~dFuOq+h1#+-8k9NO~Mnx;RmIh*!;gjK2~?RorEb+ay5$?H)rXYW25R$jdu zr+a=5x;Y`ghOM1#oU1wMbEI_)PgVDw2ZknAQvnBCphS^~>$e|5nhnf*?7eU~x!jB{ zbkPMfDjMC<=<|-ec!r$tQDoJ&7N}e3R1a>lYFFs|x{c2Lj<+k{sE1#L5}}vC3J+%+ zF=etHXDb~!tPp!-{o)Chpw@#mv|qr5nG=l)|1aSm`XMIwv8L7oNzk=Ob7bXL2<$>K zC-^m7#cY(!3H~EWhC=&sGRl(Dyw*D6q=@EAtRrrT)_;DVIz(q8b?90jM!us`hwp<+ zSsT(q+Cq(E{INxZi6C7`?1O$eeEs48&k>lHU`jyx~1GVMyZV9r-|*p^|KIST^n3AvvHur*aIE9N zpN**7O2H1FqLlS^RAzAKyKvhbVBCQUpJ505LltKR{d1ucE94H9O*U{v=cui74e~kW z8RauEb2;#I<{Dp!^kz=__z<~bHSkXnSRL)lNrxdAyR!ND7}d6$1I|r?(|H=&cVzn{ z3vACgMCnHC0LAy}g8>Tf)mN=3;gw8kOS8Y z4l8qF+$VP_%EX05k? zGr?|^D-5|_=l&I3o<$s?`759qIw+xe(O}N3JLjzpTa;$v znK}s1h8R!tLL5fdv&H>c@yMCtLn8f$g^(hDZRdGw>uE_luL3@X@;Ni-f#2+#$a3XZ zb!zZhdle$cFLnfFGUy_$R7@FOLZ#xiF2xJ>J~k4c0zU1+`*glF_G}bePWfmON6zSI zy02=u(`GQjWWw+{DeXbd>ef$N&MPQ2ZedVT^JHAEdJ7#`IaK@ajDf>Faz^Ni>0qGSi?e`gMBrJg=GS4OY#PXd1ln4 z8PW5j8gL~%BAa%mbvcS=m)sU6^4!6fA0)!_@lZZkUjCpva3me)cAJ+YvE=0FT>u^$ zT)m0_=DLTz5W}~EA~9g~pq2(nD|A-CH|LzUN$$+w4JrUSjU&@7V?*2!h!#!d7yuq| zfrMo$gnf8u=8rLlYMevdR+xh=M2Ff|n1e0EhA0syH(AaXM|6mYB|;mjtR(=9^hrtr zI6K`iFa)9esGy_CFlyXnl2(EyBhjKJBhgHwfH_UCT;C{;g8*zF=&P;crG2ntXm205 zn1Q)#`(SHfHl94A)^Y^-ze!t*6COh!+nr}UANjJqER*B=zju3ahqAJ{wpM9CJMOmk z5w*4^R&U`xZ>b*eH%&^~=njKZuMBvmIFmL-OvqS~WlW1U7YQVIZ0jW8Ie|PI%D6*z zWsvW>y=c&>(+_g(F@4mPp^Xu0$H6 zyZ-WN@Yqd8u8#INB5XiN$tc!vn+h?<1SB%nr?00Yn+ndF;A|XhJ423J z$4ykG5bHB{e1G_JKsN@$ldEf|V;d#!8vj*Aj*kZ4(L)8Hf9<{rHHgAStJz`iM*`1LeLr^yU8SEtEL zHD{;Ej5T*B#3U2ehVl5lbr##&OM!bU_VF zuaqa=1y}=}sWz1I!wqUIRO1Ay$;g^Gn;x+ygx5}Edkhe!JB_PifS_<1J0pNF31SSs zJJvLj;Ky35$9Q7RN<6ld-Dpf^!2pQI*4hv|1*(mrFSUqAhLp{@)oqEp3VYE2Sgc7L+6FLdjih5vIku3ErHW`>sLNhR(#C+8aJ({)N z4ccJcETFrEDRP)Z?tN;rcZfZ1*As)fSC@HQnble34Zc;@zKMbpK9`7Y~GxKvoy>L(C z$$0Z;`iyFe1+r%wc(zy+Y}ebx*AXpJt4K7guCW+qb(G2W|y+>cl45SyQ0~D_1 zbHcNcINTr8;7ARIKZ#2>+;|Z*#SF~3!Y$;TYq3w|Ps=)Q{M7HkC>&;BY!vRHSGndi z1`!_i(^M3WFKk5$>z&3{=5=OUUiZ^$>ZEa2oR?IY)3})lpsUiz>5b~iv0kX8TV@nj zFrzq9t@1C1sFJDT9xev4N~SFgH8Dt>%JOZb$v{bxwhEJuBx$RNOkC&(TNW)QpdZ}5 zdyyFPy91i4-mf=Jg+D;GVB*J-d!}e1RR^!F&ap*Q=h!&<=h#jvbCg(^K+Qkon|5Od z=QwD~V||Ulf+uUCB>;2y+xYk4U+ZjOf8XxG26kZMd5=8b!Kd^$=OYS#kxeDjsWY1> zCNs#?+!>?**aQS{T6LxmD{U=kDLnHgM=VlesE@F4}=R!88tOuIC%&u;q>uDTg4?S>bRC(Aa`NXNC=c2+VCpOM%7~ z1ZLiM8316f)xn(YV15&#ia!z;v4zbYfa23i}aO@BZi(b)_ZHYrUZ|`jollmfD>cl%u zT)Cc3Oo{EaayyV##CAunpd0IVkgE9|UU~Q$$jRbih->UyGVx!K)7ZLAjt_U);UkD0 zE<>Bd#Ry%9g$;ceLCIoS*a-s0p2UzBn_)@!*iYe(3ta}wUBnpb(Mu*mUWm8_)H6D1 zFJlW`w)O|}N_G_Y2PUP;oIAQGfv`wrck`Xd#3ZXLLxxtjM?$jR1VRG~*K1vj-Jz4* zBZ^HpWcbKSYiIBzB8d^ex-IjFshCj1W<_7?Ylmv?ep1DweK8X{V^`x%P zcK8_Ny*lw~3UZWlIhSq*rOm4mh<&Ki3~&#OriM{(VdJMX15D$mnnB()gRWFF7&--U zr41BuHA-y|SHpC+$$W1D5uwZ1HpO?96jgY?P+P?#@QLvD8DCmhaV1yG_t9eP#Qk0c zXLJOzBodg!8Se8_vcY?p^X($_jH6LNjrG>ihh2Fl*}?dg)# z)50~pJS}*my@vXnqgQ~6=tbu%IaV8hcKrqZG#&VZ)B3uDH55owcP4>zF7|PR!KTKt zhx9?Ft5EjzD}tdOXKz5Z0pZ*6t{A`(p+)b|-al>Eu4lq_us7~yd z%B@6kCU#7gGCSS;&aQ{9zy8tl$Q=n&VIu*94wC>j{iddZQ4(K?MS`>)&cUDlrOP%f)g*p~md4wwJY zBT4>;r5j!B|Bmhs&eK?@ruflSt6+VJS_=GNzC&)MbN>-lu3QO6z6Xl2a+T%?u4IXM zvZ|iYJb}DVL*5amml%#<=T)qKDjVEtk!NEy_wUT-M`bW;*2S{cQD!Xnx zRxIoF9N@yb5^zb7BXus}ju;}v+G4C1vRs&e3eh+Ua(!?x00;u4X#0U2OSehU;&Lsv z){bkrV8Aw^D`>#JkZZqm!yxR^<1jYKfXG+krW96= zd?fx}5czPX@PX+L=EJn%1Jkzn5I$UB_(0rkd=Nmw2j-dZfq1|NlcmW`_+azyr)d57 zR>(-Se$3i!TR+D5D%L}~eyr`T`7Y|sH{!9-Qm%-qSwBuh^>FLQ?RdC;Z18*lDB|^F zdJegMEJ)4zaXV6!HPVOC`tcS_#mpQ9-N9Kl%^q%O}{pF=-~ah`8yWaKM-g&VvS zEC+iTMOg0M3v8dKh=VWQt}ef@P&m%iX?PJ18SDKg zs>>AMc6RKgl&3nn8>JCLZ8$OHHzk8f?3m0O+}Lnah@HT`vIccI@Ot95I)&uSfK{UhA!LqrCS2S<=pK? zIC8BYpz-Aa!L+=)36Ok?E}Slm;^aJu1KT=^lgmmRh!Gp-miOvtsyp3(rP@Wtxc>}Q zmuegUOG8m`yy*_tclF~OoHP;KKq(@U!JE+lmp1ZU2zk$;+K7+zzP1rulxWg-YIZor>&EYHI~ZP#K-P2jJe2Y)~O(@0o!+T&jc{~Cn7a31{i2L90$ zobz2*AufMTJmL|a$KI!e;aDcjIn9SqJ@t5K^i&NiWuFD8^&>nq#=~tfZe@(mF~+~+ zL7nK~LS2h?ix0~-#<-@O-|l0)jzz%;_SBkkVY^@0j$M~Gu{*)Yg_AtRtWNIW&B+~{ zJ*{x^LtPR4~K(sq6oJ|>z4p>LzuIuLEDwHbh>b6FrG^-iZ;??xNA})wkbcG zId>zD+-A4?S&UUSVMtAP4kML~pdF(@xIMxlu-kJ(eR;%pFzb+K>?NFksP*L;o9A}{ zMO?S!p`b9essHqAE;2wJKM6AzQ1DCF7c4A5TIW)~2QyOcL?!l5fR&cqWQX%qGSQTU zR?dn>#hzC&RaM)-AkNO%K3Qvx z8TVXkFyn?TH^4kU_p2#tII2Ro%=f+$G@K{04Jj`;dYtUE#D4_DyXlR~rj>iOvv5%zv+B%Mg%7yq8QiEJW9TTO=+)7q)K>{#1TVHolaD#B zqvO_R@lB0D84s;stI&8O`dojF=HF!5;zXX1c*B z^Ujz0cspR&J6@d{6|1>2;yT`l-AB_JADQ4`ftMlms{WyqFZ}N#^AqM8gSl(*F$3I$ z_ELlm#UJ1pPd>o$NoN=G+!f8GT1XgYfv<98W#2(Qz*eZjG%g`A-c91g?6jZB$_A>e z>>QJE#f)sw1Kpg9Li@8WI5hnoG5Ju?mw-lnPU~JHeNImXnB^+yMo|o8Srl0r7Y6_= zw*VrCFa$@ig3U-*oy2a-YKxK03isSa6D>xygE55bxP|+?my^!J#Kc7q*z4kj_n{^N zd^)8d7cn^@Yi8sJthaZv-Y{iwKi`)o8(J_57_n#Zr5&8CCb3*ghH%Qytar`ciRdln zZqL4sPSR%2{xO}T%ASRR=C(+WJ^LCuiI}@5k4Lscj#JCb!s2&ZETldAS~^LI&23SI zU@IOoy=W9IQlrFG!a7Ikpn`2`S35n96j=#!gkwsPBg|5g9O0AF2&=O3j-pS~Gmdf?q8g6?@W^^;Z zgn>@u$1yW;%`EGDbgf2=AOmZx%QDW0NBNcV+oqf(jJyh6bVk3rq>4kvmt;a2!?fJn=MAwL#$ zXSSg9lcP=-nHsuAOG4nzPs;RCu}-SOO}Ehon0zv`fvYJP}-Tp zoo}qWdvjGyd@roelaOaEo2^L1?BL_oIW`CB1?Jd@DsvS6${bsfLfMGa#WuwKRfRdW z6)KTa;E^*DC!)L<8=)^INOt$(j0ER5PlG?6-|S;upAHBD&cJKP%x^A=rzOy!bnd3h zT05$J7VvzAjD@oLd%ijEmzvM|EMU=m)|tpNp3mYrg^NHFZA9#Q!g&bJGm)NRRyVwC z2eLZODWLTpdY(z>H1kXeoirSg|NMETXkH09NApV00YWpcMDl3&orU+jc_qf;yb|9! zuY?UYIBYb`Dg0%G>QtfD!1)K}hdJ?c!GV%HdT~~E`qVG%!)-+u09GH=Z6fuR zPIwNYGGd|Aarf*+HOPm_v=?S|+>_iA>UNlQfGfuCRXy{RgiGTlZ=dntlD{ zpoe`>DYNK8C7LvyWN4DD1mRUA0aGhyRz{YY03j7yN6!?}#kbyLjCF1)<6UTwd{ok4 zlWt}3%h860y6EUNu$@|*g|zu~C5}k35$Y#sIITAkSZ&=!!MyvBiQ1xZpT}h=2Y;Hv zkr__e0!1X@`6#&934*g$wB_muCBSJCmmqYS#Lhea#1TFb5j2SHU<_hY`6qCLBK(lP z1(R3;#DoAzDgcv0@)v*Teui|j*M?SRhLpRA$C0EqnJRoBPVY1sIXp5-1}41~fEFKc6T6T=$@(|L zuzxiSW1PXQV3ehbRm#o*f`{ZN95G~G|H zp|Wu;!x!q_NdTmAH^X1uE{*gm3!TO+!quKI^f>WZr+KnTN^IfKZ6v@5DDm6M$VF%s zicpJfVi1ECTSR#9AGCX8jf&AX$7<}GV?FlIu_9M;xHQMguFkRY$!9k+a|sUs{*wHE zT~wpNui$lYm}&;Zd9C*|GWN2`TifzU5pSKonJ*%8>s^S0rKtDl>)m`+TklohU-H%0 z`hfbt)MzW|2jRr{!Rb@{(c(~_;D-%mTZo*hLr4$tgw#ih(FyroEk)~u9^Hal_i!lz zPLl+*gbJrg^esM|CJ9P5j?$OfI7+W<9Hm2PH`~NIpvdVv~A1& z?-wY+a_RqEsmb{49lF!T>rso39N8{M^qS%|?nSuD5n1c4D2|ko>H{&9rLASbc`bHo zucDb0dMoX7*9{ZHmqq>`3OJj~`(VFb!eo8lkwo}x`5 z8b98@&yw3(pDN)js@-HUjZS7M?IsIm0IIcFFau!WY^h!W*iF{RUY3T(uw#$h4^H3A zr96G<2oF5dj`TnlOK1Qrr7dz|EOy<`Dz!{X+2K(PMcasjZKA_MOOv0ilXn$c<~Re+ zZ-zO{n1pMf5eIEuY+7aSK^wG#e*i(@2O-axMM^>51xOSX8A((W)(dw+&4woQ0+tiW z_zrxc*#R_2_67zZ8Q=5q%V1R67F2AS-s*gMop5c6;H@e0obcVCZ$4QEL$3$1aV|n_ zZ;Dzcy!deBnuK6BD_oc@4glU40mK2o0}((R08rK?i8uhD6bV2a08q{ZAPxX1(gF}i zLu(NmS9Lk9V9UlHEDK=cDp-6DSWAzoBsy`0E@g}xN7W@W@RSyUC2Mi~m5J9Mtw`J& zJ7!i&y-NZ|_M$O4B~OxzLzaUXFIWO~H~5&zt*FX6z?C`z=Z>AXvBwGD#QbM) z+i2mI*QEkDg&D^R-<(9kN!}O^BQfzf;oV6rl$>m>;|@+Pe_;}nH&F~ix~RiRw@STNaELd!3lxM$lDqg8YWTM2;Y`msi-tL#TW8Ef7g4L+L?S9{O8&)P)ld*&j7Z;MT`ry_v z@i!z_8gKM7vKg-VBX9zry;IM>!&Agp82K4>3Beb3laMZC6?QW#9DU1%J#=NUZI%o4 zvZ)R$R_20LZn48!wFx_mK3plMF~x_^=0!_hDJa&*x;A6u4%7^{Ao*P)v-qA-0iW3} z$xLG{O{tR5)SHdSF z3%;KlIo|j4>yK5)5Fp;jpzmkbukpKA89}p-!6MpFpWl-PRM0R! zjTBYJt-}s>$(1r&?nO?1?-q;?EMyyP{ypwh^rvF)^Sd3a!a08TcE8)jDx6DKk1p@^ zz=cIREaSO8++O`?lwge?i_NXW{l2eJ{q6W56Qb4;)!$xQgUCmwBlh{e;$cL^1DE+4 zow8n6`}Sg&r_3=xKCK-f)@ZRF)n544f%Xm2S`?S*3cR_Q&3DKlg9_ddnz6KBuR0pF zXs@46-U^R)#xHJ5Td;ZF>zU)hRg1E(Zj`GX6n4%^Q`B{C_+I37vCu4C#Xzd72rmQ5 z@CxZp#?c(7cNS{bi9K6Pt*Wtkb7>;ngT}hE$eoRI<0hQDBPXX*o+;zzR!Z!8MqJz+ z;z^QDM?VbDZI`(n1p?!+!St039|qT87k>yZ;fG^kI^wWre6d*M%6<;J$yDoclGb+W zxVndXKoVGo-DKX?(X$+^CPeaAt6;*7$-+LwmewVkwhLTxNFrg9cY#R7+L?#Ri>>l$ z^NJ%Cn9LF%^(5X{ zvw>!p^Xe1+p2pLUDMu{JrgoT2VJIxEKStzkl)hX)LjA+*G+> zcC270^lgyA`dOJqKO}7lKLXsrFWpxB!fhA}b=>C{tA0Lm_WK3h>peXgffoC@$Qewb zx}RG+vn?;|IjY1NbXMbRJNx5=or@33wo~)sRH)d2|AC_O7tJ!Lq4HTUx%Ck|&??1@ z`8)w1O#%LqfWJuruvp}_K9&MJO2FTz0N*3vQz^iA3HWph@Gk^>CIxtifWJ)v{)vE3 zqyUc)@bMJjVFEsx0(_Z(2UCDA5%70-ur1RTegJR&Vji&MQSn*EsGwv(#03^LDlbbJ z6_o{yO3AWC1!S?Jvav)_u~?X>L@Y;C7#1Na$Afh9f$AXPNWlzVyhl?2DI=>hiXjDMbw&UwEvqvENRe5c z5kShy+KT|f2-a@|5Du_9Bfx`jw^*GKKzPCG)UqRc`Dw81RZ|8`px-teX|m{clU$hi z(*(-$+fA}!g7&1zflRQQu^aKN9QSBy6 zJ&H?3q*1Z3c9Z2Etr`%EYd2Z)10C#2t8#HI>rXzAGPuQt=9CY@6DENqa(*CTlemrJ=3VXNpeWq*j8Gal7O86n#F|*jJ=eGV0&`7_$t-TIhtpl@}lJ$sY%^Fkg zJ0ha;nI*u*mWo5PQ(^SqnO>$P*>omz;<+0NwkL6=Z$VU{QaNB8$ zb`+L7O;(rInbTx_X{9+$QOi=xPLp-fk_vH}tRAf+r^(7`Ne#gmq+6=VX|kHMuAC<8 zswEZXG+ALSsWqp`TGOg?nyk8()SuI2{b?mSP1c-NpVMU3Y5h4()}2Xqw ze;)pq;D0p!wd*hhV7r?6YnXw9k!JvwL+p74<_TlZFEM6{J%5+>Y)^aMl}>X9b8DOj zTViGhsJ{hxxt@X#l*v!qx6bCqdoaCM0BPz@<5Mv}8oSf@ zY79`xYy2n%NK1DbPZEGuQ7|&t$mJt|v~#D?9|NStJB^JoK-#|3I4=gMTsAI`0VrSU#yDD@y}&S`v+2~KR!kY3Yv8eeDlw)Sv(O?x`@;;uHY#t(>4Rzp?WajkcQ zk;i%K3Y0`!dSr9!4<*qSt!Ds~L|as!0ZPK_loD2X_HmH;S;w&+Qdgpz2BvNUKZi8!srByOK14Uu_peW!7becx#CeBta_`O(Xh7;~J3JNkaKT$L z+=Y7_^4_xHjHmbeU{Eji5*M*W6;?!7aUNexCBj7`R;2GFoceI^4rkTWub%wWikt55 z*qDQDLiL3)tai>7^&&1+NQ;aKmw4xs}L|kf7SvL>|0Mw!a5C;H{MgVaD!1gUM z#4#8fV1p?qqS5G-XyO2XaxVaJ03hUZ=Yu!^5E=;(2LM#Il0+N;P&diizbL7SSZ8Dp zgB5zdAq8HfV_YHI zLx64p(E>)WXdjkB8~{-NNe1EofJ$2c;sAh3TL9t!fJ$2c;sAhMh5*C?0DA}lhywuj zAp#Hw0Bo!R5C;JJBY-&2Upv-oq_+m#+*;!5Gy$&al>Jlt-ijcZ-D&+8t~ThonEPq{ z1khT&4bI7>gJYi`#kO(!fHE&*gw{`)Dc9reAA}=&!f%rNQCVH^DIRX7nKsaiHT=D5Od2K57W(-8Wzy>#T5JUMg zp>)dk+T=@%`SBYtD$;y&QV>5gn2;{tb{ZUkxkz2J3&;8gH^D=FvCG{B->F)0{7fN% zJFXw3&8OdxCT~=C#FNa!SS}MCVh3Cusuv8ipBTql9VK9w!L(!5mfO}pcE<}(**XRn{Tl#>k6s2$C0Xu%wP#)t?CxCGMZ3p7lE~&iwJ+DymsKO2CrpZfg~~- zVccGYn=h27<^1eYjFL*(=`yfW0_`Rxb86N9yz3WVKKsh^#;42J)K5?br6+%Ota{&R z-@B#neLv?%4mtOQ1GMGNmDa?ve<*kPo#oPIY(TMaNVKFPz)i1AKmGxL;U*HlIC;W{ z87mw7IjF((C#i7v0!~KZgOuDY zh{d^AqtJrZup3`tj1v;+2`3R_l(y#N7tBD$52_Qe@ZSq6&qY|uR zl~(HiJk&ovwx^zR;W77z`q7b!)f3YPT%0N^`+g7BBYm#uArm(G`mq3w+QBPWhTgp2 zH5Hu3R-=t)WHF}n{ZbaETqGm1hzT!l#P0f@j>5&^0s_>e&)5$1T>-u(OlJYT2!F{AaLG8%7jbxXf?1E3*m0Al)<7ubVwom4HVG{z#4~yM}q9n?bzD_j3SWYx6v;9o$WUkX{osXt!xf5}t-y!C9t1Ky2(R z0FrnWh`EI4veJ6dGm=dfF5*JZfQ|V&n|R)iTOd)8v zxJN5oy)9D-fEPGDn+g7egiCqQi-5puK@Me3_r+go-Sow1y~(jK*0rvQ`O>aD zrHYQiHAY{@l#E(cTpGIw8&(p?Bp}yZR9n*)jllM3OnA*jT5v7xu{gUWHM`}FpiMid zm;)P7QJ)7ZW56+pEe4lnVLCKByaZ*P-hrx=p@($QrmMHZOC>B_wVh`5YT`8$>sRNb zaSiq4!dWnAD3=T8SZ|%?T>mRSII+o+yXlVueb)+)XaV5dgc=7}m6wG}xl-QpO3R(R5`1Xu`S*i4mR+}Kg#z*yo%;>+&;aodI-gdri@1;O9hSF z+*;kIw^k3KKnuje*HNdSE@;$T#$W(=zPjNkrL}T}htdQ2R zyT!@O^()oURot7dwb!Il$4Ewss~-9ye^2oE;Fi5|gKG7;qz&V25Z6_bUiaG3$x(Dand$m*+dss;uiM z6WHBc5!;aKcx;FfL+NMOf5c-ampD?VbxAl_tV5h-&D}JSUBDlo0OXh zy_Oj3`5oAvm4X)mXUF2ZI4+vqXy;^?O1!(I=q5*7xLAIO{$LZjCu#~&bi+J$OW&^A z5?8+#&2OIz#8m2!VB=+z{?yycFj z9C&=av^x&I$NmjsQqbUXkPuvfhtW+3$BiX7iHk-V zJO^AXM62vm4-WgDCZ%lQ+SiF=A~F!qj^gD|y+mec%}q>SuWL#&8&StyrnLVPL}utY zb^SFsk+#mRblQ#0MQb;Dy-{Qs_ePC1$b`|xx=2JB#*>&&B_qA6MKpbe%SBV z@9K&!Wa*oTFHTuK(N!Cc;8p_f13bRK<%#i4Q9-PUu6&MzD$Y^O7&;vHY3YzL+5eja z5dZPO4;}7T=XW`xM9lQk_#Wp5%CS7;?#<_6M;-Ux)AQv1faje&ZZ#ox4D_(~HI5@W|?l5rjK<05w5N2X(u>e|V>zuwy>nse+ zM1j#j!elGPYNN1UITXb7Kx#y|D?2^46UzO^3x!PX#OH)`mSGXL`QYmrB`8#>WD6Zb zkAsgp(2!8u0jP`(@(hSsk0}t6F$HpnJM!&l8=GNs^G^|o0mU%lGaloo$v;GVT#U!% zbJ3R}=-LTcMh?sjwpWVBw98WDz6I>!#MFG=L59;G|TTFtvc~_ zoo~D6v&U`1caNxS2TdG&%lej7D7R#Cf-M!yGYvm8?a3s1WPI-DAU|HFpL?Rs_Y3;8 zYJ!QmUnag~6LY_6^Zh#U^-awEN1N|I;j7I322c59z#L};HP@HkGckt|it#=2@`<_M z#=Zv|6LbF^`>fRybN`e0auajEqwkG(cHgq;J>MLkJCKAuI6n6jeOt$G>sfx|zVSH= z(?zRV`9;ev?YUxnu7kesto_&#g^l+3oDE;&cys}C4qd0xg;PDom7!~rFpsXQ>B`cz zk1nVj@AOS}d zDRyuTY@4IhDU3oVH@M7E&?gP)0C02!5C;J3B7isms6_yA08ozr;&?_e zuq9N{iUUbtcql*|TNI1kWbh=mQV9-?H97cd4c)0wyci2FCfzxM<;B9_4|u=F*>LS@ zkxKf`6CX*WPt5xk6VfM!d`nQD-=6slM&$+c1Bi4=TXv_%+2J&E@Flb?%nE*)FHLeo zVlx<(ZdsM!8*G6iGP0@!-=s&^3C=Wf8}I!Rs@;~58>d%#6E6wIci~tu{5BJ-kl$vK zn!~D_IV3N55KRh;Xi`kzqrJCd9$u+jWtk&9R|*_qvF6|ipCuRAGk=dVC;?9RNxFpy zC;U6QjR*z%Sy1SoZ}SbjuJzY#c<1Sg2hsE%fd)+PuAo=)!*+~AZiZ0Yjfbf?I?*Cb zK^k9<0m4rl>ZCT37{U~6orz-zPo2j1NyEwQG|($$Wf>gn^`;E-bPcK3W^@~gZd+<6 zfwyf-prlmg&1^>?r7G42ptqfnN&_IJD(VJ6O0|{VMgj%j;I&XSB0{F&P(*^q$bPJI zW3KK@nXyzI#c@}KI?4&YhZs;vB(s0i@3q#5(7RK?IgoZtMMy|>bWt2aornGG60U_d z>DW9nQ^9jhI!b~`Cm~TfhI`aMf|_JYQ__~CrBbqihjSBW;eDgh`hB6*3H}XlP7APk z;>GN7!-( zctipR2~ESqs9+;gIdWx@J$np1n$IjY!Ml>wp4~*JpnLq_gdgMXL`TSyIB=5?&BYNa zB9ek%5IGvx&-tcYZU>L}J1AQ>__!Rdh!0TX=y=2Jj5=l#1Hd2w<7_=rcI*I)V@RPz z_Rmlh@@=tC#F@q?Hkhe$)%3T_RJm$;Q)a5{lo(Kh$Tw@jl9C_CDA8=S@Ra(u8P@%1>Rf?NxNs~n{VR{ZrNu~3!7Dr>C#R4I$d*;K@_w=@4- z5_j9{0MO`|STE%#OF8Y?pNXGzrce$TuzQnm6HRTSP{(WH};kphkO+cE5Qnu52ls7 z+K}iOksE;<-+BPw<+mP(`eggH9JbtYE<=+6GY*MFQ$v=8sCuMRqr&dNdWcL3WaP$R zEWYLC>_;^8<_|p^Mkvek;ndx zehG_!C0zu3`c$5zioPY2`h3jzQ@pu%{sM0Fjfd}nv23eF2wbPBCOGB3d&0Q38^O6h z7yX8Xu{(r~x}#p#`m~!=kl{U`dnGN_^ijecfS^k$)Ou83WTtl-h%9rl+6FHLWMbPK z6>D{linKaM%}ND5#y1tTH~`oZ0mK2oaS=cq0306y!~wtw5kMSbuUs6JHTW5bwr95@ zid>Z_cR zxcpGzN?zjy41YEBQ?gff553A1r}4T&g)8xm_c43}mKBrq_tUEd0`2VccBjhltGEpD zT^THc%7!t=NVLj^0gy_S4Fe!qDjNnsI#f1N<3HY;jQIiLhq6lbS(wyl>#eq7{>Dmo zSlb>0AX6Xfg`(1qAQ#+;zOAGS-P7oxa%!an9ML$XBH~HcBHcjkSR_OgO%vsy38F0G zgbVaY&u{A%r_vqY#^=CD;`oT|&Km9PwcwZ=53U3VA@LGO5^@znN}Y~ZDupNF%8ODL zloqaw#U+Fs-z;6#wvk;*t%U0(W=3g8d6Nb5P34*p)zO;tnLr zI5!+oI?A1oOXg;=qik}RKeZ&8|=70 z?AFuyIPk9hA@Ya&~Nq^X*{bA*4dp7i)6@QW6w;tb>!mZ+Y zYvfSc7cMf zXv>bZ4Fekpnzap&Q}KQU-cdhF`1|n3W)kba=fU3&IBmf^8dMbj9Xf5vE=ngi**U3; z-IBW47oisju13?9HfnS2!fHUI6zGJ|Sh&Zr4}&SpHe-F!3;VFn5?n(y(4m*+^Ls?N ziP}dakBegVe{@+#n(yNPOUd*(oqS)4k8WThKiGy`*Kyt)(#e|}=iTk-BW`rJqg-jy zCv-Y82E-bj-0esj^;;#R5%(cp0CVn;-zvd8d11(Wt0bX0xd%LNZP=psPVyUwQ{YEC z9^N6Tc$ybthfVyg5_%5#tr9_M?vQLpiu@10L(;aYfbT*1oEabd2H%C{%CG7a_Zc}- zS9_(do)6|Z@*xqet6!juNF$%w46f6AoHWD&050MH%Bc*@*6)fXO$nKsUH@cp2+AGRDt+%9_!!z?CN-!wHn% z4t628|A)CZ0kf;B^2JY`bMCog-KtwvxwkThgoI<=TOlMNBmq*DgjqyTgj*G6h#FJv z3AX|w#l2q8R=bOZR0#Ohzg2m#{oo8L>yb+vw04*&+f+e`>nOlaBB$R z{l9;IufDJD*?aB1*Is+=wWqcBUfahKBZouC)rB}%`f9ixJS&AceQ-3gt-(}+oCfyR z{=~dx6yu<@&W(ctIv0+$2cn}gjgHNr!_e?^$UEoIar#+kNTe4UI?`h<3&pU`hs6QF z@!IgGjx*mA!U*C(gcpVYaR6{i2oMJVr-lG=0B~Xm5C;G+3IXB(;It4R4ggLH0pbAQ zsia3`(>@C7>+-qJ9V2ZDy&)HN?wztwOix(ZITpw z@=?r5nk3V1ol7X`v0LZRNlNV24mwGL-NH=AOzK&3yM;NC+rklkw>8EPvG_{`SUXTOg;(;Ym(u%uw5;MK7kUHgVmTXC80kQ=qHfCp1hCA$YWf3 zl-gl=ZR^)s+_1ck@KULFgpEqRBTUxh_ur<4S@Y`?WB=j zm2;k5vAai`$Q-3v^wOm65su;gKP6=25_Pgd4V0D7S?;K_~v zuG`SfL`jVE2}G)ok0lBbMq+Xk=_!pQ%SJ=gt(b<_E;XK;;SqZ={wfTHH5}| z6H(lN2fYh@T`OhJ4TrsWVh@>6{>STe|5DJ%Q1Wtm=$L;oplPrO_2F3;;>)$QjzS@a zx1!VRvrJ*Q%V(UY$4;|;ZI2|&jD`z|cj%=7F>-Ua~=QGPc{Gp+d^~CXfs26ZV-!;WD#O-ZYf}~xO z4bX_wfRBM6$Vy|8pF544?AsYkvLxwXGY~(KOV_X=?jykuJ!AW5rFGb;Rt{W8o+)b$ zzRVM^T_|>F%l86-$;E~_^rGvZFbA%R)qSYq!bLzHkloJGT;tXjIfKDvK;tY3F6U=3 zcnv>`gDdc1oh`)v#;fqXisf~X#qmgK`$ypyPXVyDb*kg;PU$XoumwTrGgz!jMTpI( zg%t%GmyH$!EKzK14=A#XVL_&b_hp0y$XM4Hqt?>%Lmqh6?Z~VNIt<#d?CIe9;9G%j ziQ=6aXkXZ&4KAF#5`Mg@k_mo*VBBe8b?|kk!C(p@eV()1-R*OyymDOy8N$F)wtNF! zCGTqaWNA3DGKejOtB?S%N`ZC4V!QyBc>*t#V>O&oisR}lWrn&Iq3vPSaLSu>d27S6 z!yDeN<={?c**_6n;F`#@zV&>{G~bjdQJ4ctS6o&TZCc}bex;Xcir9HT4uFZmgALZ} zJ{j}i?k_11IA*=qwyYLq>N2bd?cv3#%If)*UdJego*e1zikWxTt@*5g7 zE46UlaecN%$7Ni-t~}u+MEGE;t_o@rjbpC#Cd%9-;nfVDiA3Kkf0Z_N^vsz&uAeA} zTiaYjsfvm#sY;tmsdD>F`9;SM2qezLKvw3A9*QpMN7`JPQNXvhx#}yoxt^=ExxgcI zq|H?yH;S-3s|#as0B}JF5C;Gkh5&H@00p=v5eERT2m#`lZO!`U?aQt9=eO&c^=#>? zWWVAi$^zLx=Tg>g?E13vMYl2Q@XHRZNn`x3*onE(I1!wR>th#VKf%)5h zo%r8qw_XWf6VoWJ8+Yz##e|Q=G=55kR==G@$R>v!B}6 z80DSxnqoD|3?Ibk9f$9w*A%regmBG4c#PE;6U^^SK(FwC2Ix_^#B)g^IwBLF&?*s` z*c6pL+QVYmRcy@yhJ{tJHESLgR>jsVq*z!LTLWNWZK<_uSw@41MYN^<41i^_r4mg# zmdTcyG%@t4B+@Q1MBla)^mAUdG`BR@F>m9gD^4V2vI0YBB1cYeH9CDB?W0UPZqP(6 z#epY7%3@!OVbmWokJllO*6RTA+xVe=^b&CE3E0N-^x;=+lP;w)H;eAN-dka)6OXPN0an$#Z9 z0}UsBb>8P%?+4v&$(v#)cgoluVJhUGifBR%t{AO=jRI1WPm9hfs39yZ#x@HXZ;j(s zyD~ImAQXNN6ymYMn8G9~uX+e>D-ji*8{{e#*Q-_rRX9qnw_{uoBZ44$L|+?>5pm8a zbB-m}n$xtbHf}4ezFFOzL!%TH>JPfrt62w5a1A~%2>yC}VE*?F_;iy!ln|YoIzFed ziIIoh{ejli{X8x!VuQf}ODrnF9Ndhq=!!b>&!6UBFwMVE{JQc^R6XrT;BGrv#F?zl zvH=j5`~ob&+pIB5W+!ElX60ZKc{F*9HSY0-%04gnclON@Ao#s_!%{3xP=&YpWZ{t3 zg+oRc4%u+wpmO&GYsq6F|2^wdkezG5|J1-4MHl~gifJG>50B5Xu5sV;FnqAM#oguL zLFgZ9s}zLCl>(?yaya;MO5T$?Yu_+H2(r4n0`?zU98i$QELWH>1=*O*Y)FK#G zgLT(4vRozse+gQHe)kDKl*&{Eo0pw!FcJwRW<77OIP( zAkg+HG+@qSNN~OU6AKY2_4OF??08&sU?^?Yeg$i2L!8Gc;M*If=dm=-Bf4-rTP}~z zmP=Tcf>qn7`JAOAF}ec|kET07nK@VAfc$Zg4f`J)tli4tPNmmQ$;WZ8I|sj8dbGa* zcH9!>aEt?JwZ%pE`d#TG%0)MQOMK#dEl#hcZ>jP?dwb+|$go7erJfU#fOPE_%cvQJ zXKr8)D=+1*x8rJlG8GsEXS5%I2Qn(A%xEb+BUmB2ok!7!V3!~D&8b6e0s9MyEq=xB zH&|{Xmi-Vtm{;!_@+Rb|%@yBRaQcIP`3udA;HD{=O(P1&ceCjDui>A2sY~#WT`%hb z{I~G`F8qHE|D0++gMUuf*W$lAl+9-gwv*2Imoir`UrG2Xd9M4@GqFPG&&nq_EqMrf{-Bjz0Q&CLjTm31%K>lgK zRGw-`O=&K9w^Vu1rae+}ckk#=W7y~F33QI_#?A8*tleZ%CGSSjWboo{7BRTg1@kmU z7Rhqu{(SW$98Qr51;ul1*AR?_oDVV)pPI>1em4v|*P+zY8wjOTy@cbFvLFbSARb@e z23PAeI9dGGW9xS#Jomcaz{pS6n2P(Do7*l?=JTjXDd`ODpUc~acUzZ7YxZg z>YG-meiW*|gv?N= zZoEZ-LPfS>N(C*{^_4bPD<~Fjt8fWaZgX8zZgUM(Zgcq~XVex#7Wl_?!JjaoCDsKz z#9YVxbU(Vh#3X(%z0JKqfbcnABZK0Q;oMJ{^^6+EWrp+a&#Y(A_L%g@U+)vVS^?KY z0Blwf!;K+ecN6MwLOv8jpoS#mUO;ButGZmnf5vc(Ac>Q3d+BYy2H|=q;X!4S@aca? ze2UIxlTY>(nPTSgy0bzq7=Ye#VzJ^=ZsC3lnPH;+Nf5EH<`#n2L(D9!0kFXK6w4Z} zP6WWB8UTxHPqA#t4I~!Qo??k4D-0Gqqqrw3m%dft`fosqih`?k)MT{pG4f6KLalb^B#zz?ecmx1l%jBWI2RpIgpbR1sdvSbD*AH>T zDXliBTL0zXjez1r)779svkQ)qEvmT_+x}l8@=llx<0p6%bM%{V>tklyv~p3#;2y|a z!O0vXC2t0zOz=0PCoScmZagLSkN;NYT9`4q!WC)s!yvPEh&@G5N{GhW?J$&v^vMOa zQX}^WU_DSD&hAwpR{^PL4^8LwE3kmpb@wXszyyxdfDYFhMxFm1=!niqMsGj;1vb9+ zV*ws4Lf-vZoNvwgi{UI}F585jymnY-FTdR|OY}Ej(|YsG<9&%Y!M_21esf5+1j&Z) z#biDEY`HI5z$ek?Hi8JNFDE&0(x`d7w&Y4CoS+G|-QMo8G=F$1ef{?Fep-=Z_A>%7 zEh~9r{a8>Ub3}R_BB>>%(H%P{OJFnZQDA5f*bw3H0(Mhyqyu(RAca(K-i47Kgy)ik zgLP&-eu#D4PF-Wz&?f9#(uD=8@^u;bdM)^x(p^I%q2u^cU^kqS5ZTcXI@SS%S;$0_ zuq0O4>_*37VL&!!aU8HSmOvwzoDs&GCt))l-C-zT>~?D-G4?H7{qQO{oZcADo1S zBo={iKk%p4aIf-?`sl4>BK|-lF3)nD7;Iw$3|eDsdznkKGK3tBOxsrhHqeH$3gJ*o znW+m0j-l}u6jIQ`-UyO!I0BV+DSt#ahP^n|YfeE!;z~TMkNEB+{V*iN&&ph-E`GTY zC$6ClEht!k^Jd_@QEfEDxS(cnCEVbmAGr7tTy!Wdx`xhcK}+Kf=O*6;WNo?yFtl19 zY~wp^q5BM_}deP#2>BS57rI#$(mtLA(n%$Rflj9tRU7+E>mc#MR#K89C$QnFI9bQv< za-=EYh&PY%@XhnfJ1F7+pgRPJ1AsyZ5C;I25Fic!${|1;01SozaR5*x-~hw2gd<)L zM}WO zx0d0zwHQA-k0Uc}oyU>U%J)F#aVz21d0gvIIHl-zYZaX=m)$yyPL>GM38}V=aELF; zyR`@*u9OaqfOEjtDCwA2hc94}bfw>s>eMrHLa!qgE@$Ta_kly$_+SCF6pn~TAQL_z z+mRYl5*^{c(&z~5g-8nH7uX`PTk<)6g`c;h&*$G9YoYG6gq z(_cSV_<)8d)?{pDoog~SL_lr_4&mbNCL10gcN6kb2zs+m0bd~?Pk_i4ELlVVX*0Z^ zG?J*RS=ME&EA3~@>PZ)BteB348f(G;^avbTZvbS!_6i0E*{{w2T5{b)Bh&3Ea`;;E z+CX(JM!qK3Vq~yAMQ$eJlCAa>d6AhjpNJgEVx`SCi|8d(oc4bpsv+}#0OwuP*A1+sf_D1oYoUsSvB%ccn06OhZTxoQSPe1DY#W;#r2{T9Z2yDoLHrNk zS62x#%z%hF*CicExH7T*_cHaz(dX7q&W3A>{*+Tn9W)-UD{yNiaZX1?Ytd@xK3yn` z{VyXA#DHa1)Qp@xidGAFs~~-rb-(p5H>sQJlkb3!IP+FACsnZ4N?{vG z)&rQ)d7C~##@kX8_;6mj{;WP*=Zoq$lShrClSdQNq#M&YXg5w)Xe6Dw+ImVctH%5Lq&KIBUfiUV0i$ru$AL7Qz z`!o3xHLYI)wB)5tBpZfeMT*_vD+mBCS#*6EGj8mg#WQXk`7nDuySm2i3+LRa$hN+t zY_ff3s_TY{@i23ok(6oA+-4@ncPo2=hU@bX4S9U9Xf_}&(LmdQQ+iyZ{s{v5mJV4= zE*;(K8l0nvIEO1o=!0?7-b>4j44WjvKukgq?mPhQxJmAW1+@yv98%Sb z%?d~A1`jK;i(Hk>><&HzbY|k&zI-$r`~&>s-j3bDT@1(u>?^VX`wA8IdhiiKWHyof zp1z~-j2V9l4Upr=j)~y@S<|wxCN9mXQyjvyY4o=`js#ze(wdo6KV+YJ0?*oyF|&-e z(r3YV6|**GufRd*V(Rb_Cq6`FfO3r0hR`05poxwI6YiYWesTO0Nk}l0#OUToB3*7> z?PTd(-@b|yQq9y1bRQfsxlfEAFk_QcGC$a)9Rx_1bQOOke^d6OPR5bP2skklbA7i7 zXU<~st0~J0UEWbj%E7&;oa%|sfoSYG5e*6=+tnoq^hurteUycMqU7I&)yHC z>*Fm8E<%D^0fCs_#*djmzY}hqv*pvrry&D4$wAxS@`fJJSvaI-mEc&8{~-{~Ra|4B z*V^Ox??PHx?f@>s2*l+R+jkWD?8cx(`1@pp4`P-pcW@hv5I6&ozwZE%6WDRqBY{1c zYSDFPqp;N5!WLeX+a1`wl^o$1oRKSl^Bc=S4;%}Uh3H31Oyq;$g$-Zh`OrAW<&H8^ zPN0psRuw!8I39&_m)%fVvH`4eI0P@-x?}gw$RRVdIrZ_fwhQ&~^c+whFAG1cgkbtA z*2fcgV0}Dg2f7d5a|z#mwy)y6&wn?F#$rb;ei<11r?6wdG*N;n?j|^H=yfslH6vAi z5;_WK@J*XCeMImc0C9hTla=5OxOH`djBX#@F(C$tVcNKRFk9El1gvJcOh8ifiiET` zy;3JCNPuac<#WeRi1WJ?(V0pFC1*qEAY@tS z>xer)f+pkYGtq-M(DuUA%G2>~!6gIi}~gFJv{~FJf_<>N4;j+H-2>2HQ*UTWK%Fub$*yMiCm#-z(;>;}IOyb1 zMk+_dfzlii0>l9Ti$C-fibu!l)t#6f%#aA5!5DBs5(2^DAx3ckusQ^Y1Az4*KpX%Z z83M!sz=jYY4gih{0pb8)V+at(%WW~v3rQH=Dne5(m;K+c!l3KSSu0iKph~1t9vTOl zw1Eb?bu6&7w(w^JKmPxe6x7+~OlAKV2B%*Gl3(NCRoZ8v4BBM zso$d4l!}5CKE{&%rpZdMTifv4BrELJD4nbcn0um={IFZw=_DuY)^T)_2X^atI$3&K zNt*1zHer)IwkyH|X;XU7oK?X*+xIj@q9tgKsuZ1dO7?A%Wtw5ouBWI3g&@AV*|GIRv{`lXEBYr!2$yrbL zs5mq}fpFFMD#IH05ikfrG+b-!j{wTL##0eM`Ik8GFLH8#BT35NxwY-e?#7>)1Juen z!`p^eLMe{IyeOefr`Oa&(yk+A$!V-Y0xg9IDfIQkzzw|w26|1+G%jEQ$x`IkX|yAN zNb$joC+ftpu$?P^)9#hCHegB=G$v0Vu@1D;H@afhf%f3$jsoidrc;s509XgG4IBgX z(gOK!V(957GTb0lr8+rm0OYbg#a3=~*5tDGx!01pCYmvC8Al^;wO2G*k+<3_CQC(* zYS(DclcU--3P9%ip9Pn&cUE$paU7&Rou{c!5AHt8U1&OpS)YwOFt%-2q)`SZ^fe>5_8ze-~|HZu($^= z&?w?ra6uvEI=jcuDq;X;GfAD6DNlX~c+MEQ1}A%GAB|xa5K+-a`_W%7Mmb+AbLp^m zXSdjAsa6M;EwwpAN0+s*gs|R$xSjV8bYQ9#J8$ClWc?3=jv6i`q+sXlKY*rCRS$-N z|D@LmK7ttC4h$O8!LcO+8=kng!m9Ue7!?Zs0Reqb^NNCJCuALqX#P?!jqQl=CcC*^ zPN9NoKj2k$9Z@kc=gg3|%_lz!TvP$8BnAHnuS$zpIaP_6kcV_s7$z`klT*RR5EI&9 z-ZWqvobWjlog)tP@OZ^B!xz2$f0Ae%fhO;Uvp?$~Z(@XjESyW>-T>A6St#$bs=U`G zsIhZVY$&wj*g3?`8C5@YkwDRTlL7UrdjCyb^G*x({;%kocU7qO&szTj^?36^)}vj- zqG8raolg8(u;Js57ouqD=hekc2$0zyo_Cf8CcMO+0;(K*U~oVm-z&wXN~uj(K6QAh zUxM<6pVl$VqAo9)I$~!!nL6K>mqh(gwNs4x(@B9UVY)I?qEr;HI!K5@UlzI{644=L z3PK18>5#JQbScY?p=e;L}5uA}Fy;z<5?o3>J0c@V7tPvk%h(Z8gub@)pE zg5xx^icwgjk#unLkA;nGgWSUY2}{Y@)1&K)(sEtSv@?l45^SBl$ttm=75a6h^`PFX=|Sq>fs6`CRlt&HgEr_rH@u@~=On4=aUu z1bJ3Q5QgtV_^qhR9#_W(T+@I-0uBwis<^SC$&mqca|D=s2g=7i%G_?;DwsPOnX1tW zc<@mrD$PT9EY!k*^eHG zb(CLa9p1(~C>BGT=b2uuw=O%xcT%>oNk6?CnLhwRBXau^ePg@iZx;RYPskQuFE5ZK=USU-tp3!w?m%0JhMvu zb4e71n0Fy(ttZVH4>T3|5`h6m*fnR^C1uV!LYuGxat5vW5Lz%f7rgY&3<}6{7d83n zEV!yg_MDxr38d}7jD_Porhf74)2y9+PeywVy!?{r^-;f}ALinv=Xr-P_=`~mGlK_3{TBzn1hEpS=ZC=td1tT@ zDs)CXE4za=jz7G1G&a)agp2lmwzN4fQnRB}$PBcpk*Tz)lBu+*5-GQ-4Jo&&DN&su zRU)Y5_1oIV(S>8c$F`{up|+0?C-7=GEc9Tx>G_|hmuKMkh1}Ry4K$z+$305I%DL(w zrk)-eh9t2i(kGdT??L1pol!r4 zWAYdxU|ysvEw($iXd6=nl|K##x>F8`Pkfp5$Q7XI}0%XcgWsOOI*5t)e2#PD zgG2Gbd}0@h#C~|;n!NP^d~xNpvoDYpUZfz&5>#s8I zzheZaJc+A4UO(=A;wkEMU#2e`;EoPbO6IOuyH-QgzXF@pV2?fZF%afrzW;tAPjy1| zjRjbW65~E$+_c1nJt?h@`k`e*GI=CXXq!F zvDM@T(SYPtRy_OU_#XmH-{CDczX`@L@N4`=f%M^+|F;O#^YkADGv9;y?z0^KR;0J5 zoskQ&|QA6;KT|=Eg)-kgLY2=x4H;zJa!-CHUYQJO7pus;Tf{oym7#nG}J5kzHY-EHczn3#i}9Gm+LF^6_ad(A@_({oM07+{wsVjjkb`| z`UCzmX})Lrb>P{t;#FVR`}RQ>&#LaqZM2bn$<2<17WVy7#)JjWS>p2g;k38qXClOi zFk2+E4ZCr4M`!-m94I=(uQPh}tXc9`)R4z%aUpKftqEK47At9$xO;BBacgJXxgID6 zaa@P*0h4+t*Ia)8J$l*>%+cekTd$SEuoJCq!GbKT_@%50}Cb!89TUxj`a)dP7E2rV3KjXfvrAM zka_;$KyKcI7e+}CIZ@Fdsj{-X*FCff6Dxl;5=g)9TE9x0cYhr`rrY}8NVFkv%gra@ zaP@8PyxVecFG`;4Hphd}OYV>tuygLze#G4LK43wrpWrt_2{UfM>898Iq_rN(15oJYk#@3c;5_q0`hjDH(luCB4bjc=U zaCAdNpg*eQz}7BKFp<^>)Ap|s7Nya6(B*^d|3JWomm>F0Zl$KtY=tz0+`60?4b36D z_K%D!>M=q6pG1AKJ2(xs7oqN8|4JepR3^+om=T2kg&5mEP8LtcnVuczw^N8ae{RNN zgcBMLjzOdC!Nou zYCp$D_*_-X30Oy@*{YT^fUvM31I_`=ZearDZhw-6T{x|33t`zz`6J^0|EpSz)FJ!A zs(X4QZngVOKXnJ9h2gVR?KMQxS+&(m-9^*N*BzG6mZ+Rnjmv~WhD_7Z)Hb=Et22*F zz^>r|k%vd(K<-?tFFQyhxB08t> z(KPrll;9Nn@529I@fC;hhg1d*mj0I7beVkn(Lo9-9WTi zqNEWg8D$!VbjGbGgOk|PnJfb%>|w5emt2HsK)5i$bu1wx^p`-LELCtwVd}>y#cp~w{sXokQyTrCP>c8Y*sfp=`{N?aFfd3`<_wm0L|Ht58 zMF111AtMZ1BG=O+Ln~e;e|N%FnfOw9;!7vM^@20Lb^F(j|J9bE75{$f>lXj|v6Hr_ z3h+40qLOO6p>b+0ph`7RWBh06BG5kuO_t+h{hQl+7R01Ue&fVFQpMtijc?uG$I5VvIVtOmh)A^aigb)v%h_eCaHeV>t#ZYWD zUnI_taHe<~ovj`CZN5yLucGrTIxEdH1o8?(UP7llAJall>bB0~&-whhfIk=U=R*8U zzKe9d6fS#;zX2{Gdaa8YdP9C;}#cz47CXNpW!&N{*IOeCyzf)nYM5+>ha z;ki23+KE0SF5C2XyrJ9G8|YHoPU9g02BB0nYHz z3BlvVeF%STXE?noHn={(@LFd$y(&w1>zMC=3qi^teePvXX99XvOq@my2=xe@GG}hf zU}P9}Vz)4X8jQmI*eIsLR5lDlGjIl)sO*%3L?|y`NQ?*JDzmz9=o?8T~EaF5s+m0L; z7Y6_&Ljd9cK;#(PFe=ahu-jo$_Bs5r@cgpO{IaNmK^y?E9t9wdwT8kI(Bfcu%l1dn zS`_94*yGYBPDEspq!R}KtbYNBgLP}iZ+A;kE22|YY!y)7?eLE4% z6%ouU)3`D9Vkzrn$kF>bPRSu9D96ZE*AA`&wOZa9&cx&T$qp8U`%eCaaP^uDWhW}Q z6_K&!#^XJdOYVK`#PBUj^~qW8874CBbz!IiG;mg zhxVwrpAQ{Wa4wN}LYw}IjwraEBzJ(ROA#=(hbU_}i=?G{Jqf<-BD7!3Bofaxud_2S zNvo2ZUT=ra%m%=f8K&(rFWuw30(5;)_$fK)Hjq$go9z?(GVGn(weCdTwZ)q2a7L-9 zEl&o{(@Yd3WjnHh-6d&H2FQ{fg}f@lo8N(k4+(Gl^TC^8w4C5#IG}fu@=pB#(QBS? zdPta$y(<4aveB?JpO%RF8?&>}0gl+&J*{YiiEK1QKn}0=rsTf`wqrN+ z#<`3ax=WM|Y(DF)Azmbt5^SKqJwiW(nzHdd#Cn&?28+GO6I_KT!-uJ7Dga~{_tDt>Xb9Ao^hikU)RZ^Y>r{z^ zy#$V|L}*$`G<%Y0BqT(`kXkC`F`*NjgGBjM8lDvDH*I~zwk%v6M4M%MocbARGo^1( zDSas^5SlA`0mI|KWHX?3R3q#J*M%ZMbwJX>(j9G#icue7C6bcKk zBann(N0q~ogy6I>&&bw_Flj|(>AK={MFR0?x`HzXtSJ8q5y4#b7_KNv2?_BFSEeIY zd|E_wT9x3f5H%j5l|wAqdYI8GKp`zA@JZU-@JY&rsax3+j>gul&o@YAG6^FIDY<11 z^RZb$0(%f=8M=$^X^ODut~`x!8rPsZjVM~iP268Fjr=6cPtk>IJw1lJhWnBb>>#+c z*CZj2&l+d=rP?oS0=ba5=R!GrXflqVyf_JAw!>#AL}r?`keIDGn7q3l z>kZ1xC@k<6aZL>@k?#2{ELBu|IBchOj|)_4AEzqZ({x-9;n=xgWV{dH$+<~rkj)rm zPE)@Kp;N!XPwfgLzYl;ev+zPMaFKzs7mA=8PE}|GkQZq%T5<*f!4{;4V4Lt z>lj{aJR;=1I$@bw0)jSujm=F4;Y~RD0;jZXXfVtO9$;DIjiV?6e=vq)rR0s9u{ppA z18+E;_O`xOS}!w(wRiS!VK4AMkP>}>VELbLKPDApHB>1A)mA=Qx=(BNaCl*du} zydDziJVOn^>B1u0whrlf;xBq$+Qu#I|LnF4U~aG$5NRD#PSK?s@tXQg zBz9B3hI35&0X$tmQgF(Lf))xFhb4giB+GCSHx5AKJQKDLZkO;PY_HjU>Ctq+sd;|)CzlAetkq7A)jLWsTbhG^wA z#bnoFhTY|V38Etxgn=<4g#8-V6q_R&sZy$8o++v(JLLwiiYbxBQLQKBZ7O&R+R9Ml z7GwuRJYV?Nz{5Y(&F!OY>Uvm9R{I>d%M?;i|N$E-JTQ89}Mb(fnQo7t6*q+4-aZe@1`P+KaGe z13$1VP3NRIFQb$FnBBS@zkU@g(r za;BJ%X68&WBhAg3VosW!BhyCB&ygml89D(65Y5q%36*B)Offsn6K4XcOv3VXa#FvN ziL6SQ8`rI*Kmg)E<@4*{F~_%IA}pnJOok>elTDbz)I^9t9Kgt#m|zqK0Iv=K;sAiW zkr?6tKsdx@yEp(~`6Y%pz&n@g6#1L=j^~^f+@CL}MZHX)r_1x8Ck zI+Yy;Kss%oeYsJrkWSlYUu*!R)AlJH20%KM=>|YLmFWVYY^M2cis3Jj*ZOdq&gI~S z|8!kuJc>g@q#YqJts#e?V$LHNEim62H+NW;`Iw|PwMPiK-=$~foIc@svfu*~?{{#T)Qws|A4l$PzO9saw?I}}yt zx;5*g=FK9W+5TI=afS(V^O+khyYPmJVHl3O<;q8#r@nIF!yEJgtsI6;A94JQfV3P` zojMmR4R_-Y9kpfTn()s>@`9Jy0b?>|Gv`PQ;zRO`@$=h15sA1D=u|eoOc%xFD|DbE z{i*>p|Biz6j1BWe_XC8_1NyB}=G}B&jcjR|p9K2p0ACQ>6 zRvdGKfF%*YqT84Us_zzgBdy?`HV*$Y%zjo5OQB*mOT^jv9+vVx;zB;urL4mD<;bt- zP2_rDbF0VhaeCYy+SvRgqJ?$h`Jd86Oj9#yqAuhhQZgNc*upWMQZcm)_jSApb8dC? zhSBkSoHq-c$lFVYSqr_-Gw)B6>gQEXNkQJY*5^Sl_CUd#7i&>L=?*pZgX=geU+gXW zm19}s6Juu=Q!7vL>SyACvI)bK)8d#6TYAj+*qP9iNGx}@SPYX`iYms0Gm<7xK{N&~y(X+~p4Ips4j-Fz6L*i+$A#n-7oPc&!*0KI%cxVSPbjGFPSn$NWzu(U z9vw@CnW32VBJM1LCbV0e^sE=dFrMYC7d2-&tXYrmtx$GtpY0ZxW}?34^HNHlr{x|t z%QzzrgzSrmRZORMoe73eMkEr(5{$b;NO33B%C z3_Wimt*I$a^|M(P*SZVi^CuYJY?N=@Qa3#XGJJBRF8s_%*P#G!EH4}x-QPTBtG1*! zKl~F@D}}I{aH=;`&lqCEIveXY<~aA?Jc_f)C3nTk`S-^2Moo)EWva~e8+)$+;0~hD z039OCP>n@ZYa*t_I#mqDV^k7-EUl#PIuoigLq~0NZ87o;jgD2JSTI3PkBsd)({WE3 zT!RDC3fq0R#4tVx7h=SG7)v-cLmnG1WnLBbE*8$&^9CurcZ5K>sAVic%SfzaxJ<27X}`ohwdYO8f~;5Xo|_8L&`I9`sMvdkY zxgTLy+EQp(RZ#Rakw7l^-)6$KPUB^kwMP$IouKFw5Kzsyj)rqw*ydQAS71E)AOeQH zMqfYlNFBXI*n-MyN)>MmCmwu65hYe{m(y4e5BF(u<+V%IXYZ2jo^=?Dg6|>#Be=b5 zg2Q>^m?-k$MW=CGzRdsH*_ST zPZQmHn(Wi;z%;v8CQeSD7M7MP$qE8b&bkEnBQ(s@-EQ#*wNJO6` zYX7)mZ9}5|zWy1Byk$K&u6cxha(&U52sJcElD@R#e~(QM3&HEqS&BYQ8~|J&0>sg> zr)~g1md$EM%4>WEBtRWVxTF*ZQobbwh~te4sKIg+F;NS_EC!zK;!={=EguUNt1Ho1 znhdlhpC;oQCvoGcmbda#ZVKWLh3DxsiL235&1BSyMpZrTMd7W&ue@Iyu^iLc06L}HdpQU8sD1^be&)^G$F7#e+JObU$;3`Ub`V3jKUM32li?& zNF{vN?^-k8Yy1pI62R1LiXW(!AVncl+nz~c%XDB%_!Qe8fGq(GvB^4!I@pGAIOOrP z^ME`mrgrqCl5u=*AOuKNJB>mFkXm#a3kZM%@1%Ah(UZl3$^vQMigHv_-4YF4!)c4dBL|6zD)3N?DK}5 za@AfszB~B70-38b+@C1U;mF!8V^dfRoEyT_xV|AJanw45&=P4mSWUZn!C{K|By^*= zRZy4pYDyHvBj-K)m%s1|F{)fN+gshy; zp8pv#_B?DeBy2F?G{h`M7)stagex@5KvN7@3WvrRVEh2a7!p!7(-;HqZFHJl$f+>~ zw-cIIvkTh!hqf5hLPG&I|5U)RB34IT_3DnzSB5-@R*BC#_je!AR70^;ESI{CIfWuj zS%=O+NT3(mYADXnhVWTUHQ<&t?sl6fJ(UPy&tRCYXMPf2Pi(4Tt=&2rmG85o#4wA# zDP^5#>MxvD+=|YtbsK+f#*doQeFt3B(8GL7ZWyomrCRS~gm>}h7RC}&yf?u$^!{+Z z5O~N$P2Ng!C&&sqz;GMlqqC0C8}xlCf}D$hrq|PlH4opkaoRwm0SVdg#eF zWQ%S-)$68vog##2bkY6mv-0UpReKe30uNG-E;^w%)fvI5_6Q&fcev-5z~BOdK%yAf zUF?YAA%;Mr7@EaiLtIndf58mqEtJ#x_$I=rxzLu14RBZO3%5 z4OQ4`(}(jO<}q94OBAe3OoWq9Ik^dSFZg5(z}$dphcan(!JChFmlmvcVFb%5_omDF z>L3UDG>GChrU&CtMZ{$Xl}^RmTWcSK5Yu*bC^N;5(5|6GK9$vr62AGXiwWNX=Bhgm zY1yKM;ZxWfW0usB7A)*TT3;gFvB8*blm2fIshGjxnqtO4w2Ml)#$F6eIDp+Sm;gz30Wl!1L4B@fBSx!w;fs3()=li5XDoh8fDVaQuPIbigBzDe&aB(CY(=)rG z%-#)4swwtPxC-|p%C_450f%wmDp!P>HeJkCQh|khJ81KB@^14>=i0)_9ePq%nn6u+Oy{vXtV2pCZw?%^G3hKY@R~re>BQ(xAq}Sui(hB!4`H& z-R5ubW6tLM45Ej;K5?H6m9ndN45vBhPzHzz9ZDa+GBw!~{Mq;rDs*X8fu*p%7wnY%7a)%9T-VFF`uh;Ww&$(PHQx`H*Su3w==yR5X|ib3;ds@VA}=Y@ zlESUchsiW7M2XIu#3@$q4jNtYoVCBv{2?Bizp=M3bE+624X&BgxRAkCKk3Iv)NIv{ zAxt*`SQ(Q%v1}*F*gTh9uiu4vzilxe{@GJ3$-H&E0CXqtdG{fu@_K#i7@{ zFc^mtwH)jCtTcl(u@-dQa>Qqe5x*ccfrGh;_%T;;uSx=Je;w#`?!f+pV=XoGirAPc z_tzs_#YSuV7b-jGFRc57b|Rg(F5fs6ZGG}lvfZE-N^Z*~D{~!+I1fc(8kJIX)XzzB z$WI#O5q?QUV~=6By3SWOMoc=~Nmskdc?`g?wqh7@I4zvh+&TGTVrFi;Lk4Lj)vH&= z)_&ghH=*RK!ani{HW^IlhhF8-+?n0BjaZ{x{UqE)Y~ z=OAe>B+F2}>z#HD^~Q zoO{5Weta1+Sk5^$*SUUI&aLIp#JwY2{6a;r@is&-8ui&6lZT=(1!s65lMZ*)x$DBx zRF|cJ@bEmIcf3^rJ(;%c5j%Cx< zOs__Fq7N=pTCqrKMF8o>@bWVN`xn4OWJ1?PuZ*8;cYJ?htI1>98j9Fetj#0D#IDWX z0Ek@$I9ycuKj8EU_!eSJI~HH`ml%f$bprTv`RV{RySBguTcE+OA;x|Sfq-Gu&JF(* znu)Gw%g{V53@t^5XXv`ELsqLw0wc)@lz)sQ$7wsz3>sMAC_h#WZSE)x<;Uh5fMzb? z!;B8rC+M7Dko0O~JTw5J7lIeuWB-b@7uzSj-PJ>3CYkg?*}M#stpFQ%o>TE*RV6p< zdW9#Jvgbz}q5r#}-$~LxZzwaQX8^O-5u25M<})@UpCjUY4naPbolPzKU^<{@FFkW+ zdjD*h0)LqAmJ>t1O??n@B6}3#ctmcSx?{x@mW$&k7fipxpo!OHsO?ivrEm9z?)$Kg z7&^gjLC?!`WYE-}#tOJkA#-*kPmh?Or$;ow%P|6+C;16Qldx;6nR1r!Qd75Q@GDB( z3lN-~=|j@@QLvY=PmYzi^a|-Po(d)Ig;A=dc_Xk)g@^J`x=@Kr{pqP)mi1MUQ5&-vSaDT~B^_V4#8P%CtayA*a zbkex9lh)>ni+*|)T4irh*TSRpt`6U}z7!?6pL|x)xfqCtWuChW;uNxdNELlVvQ8#|?iz1aJTVYkn9bC@jFKc*Sk2v`6Rd%8y>n(; zqt>C9#I|QYi#mjM8}m%&?*zY~42VT)R{o2nYC--wJZuXVv`6%DYfCKYA_AuGxiNgX zYBKT1@Pyxw;N4wFXVkcM+CvIL))FUt%bq(868bnrZ#r1qZima0*zJcIPl&? zO0{VnAx#9O)ku2LZQO-caYixKxEtRk*KtpY8vCrs_b^aZG(U?A*iwz(+!WH~{#E5Fic!J{khV z0VV3)0Qj^bjq!lvFPg0Ug*w!aBixzXLpvi(q8M8L2xo8+4}m*P=4H1&hTkT0v0MK{ zr;zl!a78Lclr^Rkj3GA(=S6Y4C7BbP4|gyb=ERlkv?rifOG**MgT9Ly?tkwfoo zFh6r|gLx`nO(*R@5BoPJk5p~D0~H>1htP>s$~uE8j)U@r4B;sKtxo!Ik_{skv2{|Y?6s#)>Kl7_$#EA{Te0N33d~}{Td}3UCU+60A0%@zaXY#?SaYEEK{&K zcWlTsn(4KseB_z6r0LWIGUr|Y44wut7HeRlYskYZd&`@=+YqBqbCzv01*0m{n zTqRp?MKr^L1oZU^uA(<+Wf(a(=8+QqFzz zpywy}rk&s^dA=mua31qzl&*;z`k0@5g-#e3fb#)5?-l3Q z==_p6ze?vni}QXu@1fIaVlha&Znkxnq7HF8Q^c#tohc$#?9LQ1D|%;&s1?66MckT! zGezW@gR429Lnc1yp~V3JkqST@01$%!!~p=&2tXVFFmJJxCB-XC*6jUBheQ;I^m{jV zlxNzUYAt3bdOudU=QK`>0K!A3aS;I+lX9l0jW-e?XSal(PUCI>W_vEg@SyPphGUMK zSlr%8ukeLBNylm#8mG;;tdLTy{P(s+HC2`L_UhcQ(D23hQhhk(VIy@Pkl(c*(OY=_r$R;U%M|NAM7i*2eT~ z)72CE(s~;eYaVm_spMYvUS?f0j^Ld*3>5qZrKsVx4G9xaDWz03*TdQf&+x!5E}Q4O12DtR4Xm{{Ix-0iwtyVtJO<7&8X$>gFGm)l`Ae?4B%WveCxxp@9hg&PQ|6wxjndgH?G?ko|gt+bxQ|lxJ z+np?FfGK`%??OyEq~L(Elu}f1#s3)dMa6xTU(SzTWcuI;nd?uV*r1~DqQIX0M4Hf4 zMtLCoPf@)IbEV!AGoes*Ni>Gcc_35MDTP{ZI#;?5H2sUT8J1`hJ6%n6{jSOpm*W`f zCqO;AXqjNkRA-g#b2;DzKLy%&zVkEqlJkn^nD6{tqQ~={Ct!00IDJgJekgMvxVR&b zJ29O5OjxJ*)db^nR2<^nAHWzR!MkUIo)9Er`6yvhhub!y8XGP$Vwp zPq)+OO#O;tDO2YFZP~+Phg1I=gV2<{x(P*HEf4b&k z?=lO^G&mKC(w5wUzPo}Gg4o*`c{R3dv_4qyUmyk-Qk>Uq1FheV>ZG4s zeYmxwu&0ehFbeQn{OWi=jihG~FvI#e1dz#>A~|&4I7g!&!R5afnS^hPRUKqc< z!ptN>G}^Dm+DB<;SRJThpQ5T_bxc@O0>Z2kvEGdgby;{ET*-Z&xh!~NMEW*bwG2_> z0N^_zKpc150}+YS9Z9{)SJ+POu$><05zyWz@Wo0=zI-x|{yM+DMEtY;T=A$>OwrZ` zhZ>S*x4w(trV!PV?KXsqDrsi;HIc?fil^zF;2zMZ2rvjeK!Py{eU)EGBgdf=v|Yd+ z=d5~~av1|q30+5G0HW96Z!AZ+&Lo6N8GsUdXQ5(-Xq4|pF~fAqcQXKSkDmIt??_+A z(nEI4evT|&H8!55S3F+uOYnBc37{g);YHNobbP}wAFIk-@GC;_QtH1lBKxw#fqzeo zf2HX_r|%_6c9Cwy^n?G+x|JoBHtSxD#6Jv&u{VD?x{xOQi2xkF&+Ux^AkL350y_7m zH~@Hr0DQmCFNTmu@hu14kTm*!Aif?R21Fc&@k=PbKQ!MTneP+)61g}K;mHsn4gh`} z0>lBpPeOn=0Km|xIK;8Vo_ZXBAcut#ud^Wn{X_q3W1_8(_*roMdHZs!{rT;aSlH#n zWLx~>|57BHYuisV3I5PS`s-07yz9yHkxjBd?b%O$irXS&1)eYk3z{skJ()-3CJU`B zz&oL*bK#bO@F+7{P|4*cc_=m5N#NuaOiUKrlOCcrNwnSiDSm}FcI#(!k}H_$v*`9D z24=5Gj@tI*n@AS9YPWul-=^?F+T@qC2(*!VB#Yn#k1;M8j>Q2HDpp50tEe5Rr~p$b zlYw5GvM1jhWrKME-OL9lxtNiIp2`|0;96($(-ChRk%UPcRWKZKL?k8)5ufy~!{lot zHaJpmlj)Eds7e3Vz>4qWulPl|I7h?X2y>Rg^)~`tqT9}4QoEjg&LybAM4v;i5b7<> zuZ8qAMj7ea&Peo1#a4S~R5f%hOo4TC5-q)2|4_X!9Crtk;q8*rG zMAWc_2EhIkJMz1eUKM~?WK}2h%Sh&2e^XQ^(cuTyiS{F=11JBrLooodSDTIjke}Le z41f&O&dC7CG3{UsfGo20FuJZ&$On6hJTMDR@C10Wx?%P|1*LAx9SARn~Lxt7J3 zta$g$mh+g{kSw)chxY8Z@xwQUe+x1(oOQZ)78kI0Rcs*K# z?T>(qI0>>J=z_}uscnVH0C>v5X*|#Fv%Q6GY9&H9RT0LmZTiGR8;I-rryJ;*8$CS^|ZX3AVL@1d^skW*NvI{wN~=XaBJ(vL#7)wOh~>i=wO zbJxdRokJa44;B-j~vlwAe`Z1aBhzr*)@de7h}m(ER=r{VHHT&L6e~mV)@5V z602FtBE73LR4-a^{w!G6;>J4@DLBe(eHW}a{~oEJ3#LShY7y%PabJfrn68Pt(ExL> zH!m|JC3BvUOel>dVn*VGV)xX6#cp0CcA7Y=iH$%pafd*qX|WZD#azesDgn&bnP$mZ zst3kNyJg#yJ;8MCj41Y?#s-^yhXP07`2y}p$tkdQu*f8Mqau!KB)#cpkdG++0z(8@ zhUnlnIyAhOXIR$(l0X^?j=7hu9uhT%QVOx5*&LQxl6!{NuG^g5P865(O?+ z4hMU{R?{XQx0e4`*bWD~mTwN*;e31bi8dw^Y`44Y)i&+YWL~b*i&ReFRdKm$x-Qieh7x(SfS5XXAmgC! z!<=ay{`!y)`>eyisqW1-Znto_myV2ci=mw{fHUe59|XVCkuzhb`=S z0Z~Ox9zxOPCFLZ^q%u&Jb&$g4M54?o$~^2qMJV(2d=@wByAf4JPNFO)B}Pl7MeH|a zEGn?a^w8KpXHRZKqz)+(e>?!JMQqJt{+*8RUjApI;L3R9|&>3LIt$aSPD0Ccg5bvk;4 zQg_{lnFUmc_JeNUK=AMA2}?NGu5*#Pwec;X+<(~G_|wq+=C0!&3j4rcb{$vMevfOs z520Up7vvvT;V*zR<~YE#N4)yDe!cPfxI3OlC|`sb9)LrHHt$H~32PkboYL#N6R>JI zEfF>p9rK>an2CD|JCh%vAE^z8SQmKVwG%v3T8TVQkF3rWUB!OZP%3n&zWuGB`_ZUB zLn}xHT62;?5RDbQ`4W2}2hx(M&@V@!!M& zzkWYUteF2l4rv2ff5Gf!CO+kbi8pD@QqZu@pA2zLekNX+`OXEtHo4#%Gvq*P`u7ADsZ6beiD+{sLJ{GMlKY;w= z_BP8rKGD$TMGZ~kc{=4bZH-gzx!c;jsI6(*w5B!9jcGfY8_im9UvC26llXQ0Q&1lC zhcX{pVJ$yCY=h@p%db{9*UGp4KUyC238n;LKQen6s6)LFW#FG#PnktjyteYGt}s=l zZZ5fS>4)kKfgA1x9{~Jp*f@_87~M7##baLc37@;npPf=ty?Xkfn0V7miq_4qgr9 zQ`lPbzfX#`Lhg#Zy)9mxt)_||EiA=Xiyjf0HAYdrh(VQpK3!X3%+;un7jfCeWJFOm zyodc_Y&zrb-(ClkztVR>=VtvhqT8@AUQ^Fm!CJa7epEr=(wv;tso5yVx$ayDBu=NA zsVEDrAl#BZQm1g9DVOG83gwu6UkJ#6Kv0NY;E`&0|K%8|FNz(OtF?BjD zI8Eoyd-ULvq>^e_`$fts&lR14?9&IO zZ6vcoGrbZ1y@H=R4gVMTSyHn@5z`;y_pC42mIXaPDK&6L(VcguYEd@b1*p;33>4;d zm$BA&bv1*9iBAJjn4!?5)q@bQ+Cl{t-BW^7Fsr~R{ZqO#!HW?@<=Ub(i)??XBw>m_ zVnD*}S_Pd^8M3GFc8oi93ubCqB#QfHxIKMV2>OHan!v!(m%6o!`nrs&>1CXvsvhda z;8`7~_^!uyj1$7q9VUc#qwJ{@sfKzI!zJdKCK^6BAhuYx>Fd&i(|FR)21zi4Ox$v5 zqI!u|5pEW+G4*39B-7A-SUO=w)`nyIx?n^XH?~r85v!DggA;4PTyhtznHQnQt43L{ zxs*AxG9+nWvdUQ4O3LVUz`zobfPD2rl~%lD#rBSAenAQ7F6nA&!)6i?=H7k4+zZS- zueo>QoupD8_Pz2YXw5`3z{a-rEs1qlKW@+hA-RFiOAw5YEF_9i@o)#%aF4ahE?;Vu z&0Y!Zz`^<`70IjvqG3k?G$nWbntt2q>%r~NepFu#*H?M~&(vTqDSKur19w^Wayf_n zIpkP&;h@-*`2}=|yjMDalssszGdK)<0^t1Kv%cwC)-RB5AU7``wt-l~s0EXQ(+;0i z6(iY2pwo6D0mgujw_^0#PhPS3ALkv{uZI?&-u0)^{vNq;C_m%T>1ok{(!gb$Fx8GO4Fu1!Nv$mn>?I_)9d*9x1Bjah=!u?9ZxJx>r@v8Ll62FR z;?6m!g2fOW?SP4pjeTJ{<)(-V4X3 zPokJOWHwM*(zmp4S>GXg3#_!PbjXH6X<7gB(jooJO3V8X0dqzq-h$E+s0jMS?qdH6 zm{D9&S~xa>)vTrN`mty}7A|)!8^KzLjzczV+xVR@8zwJ?$qz;7p-ld9jR|-CSkCx? zYS{?1-Ex`!nWULaI+HeC z(tXGzUD7s9*Q6_@AQW0klCqYz5Sn&Mk^&`77#0PzfFdFwYZ0gjih`(Y0xBSzD2q#_ zf>5A>P*iq=_xnBfKF>UprV9Rl|M&C0pU*Gtoaf$iw{y31w{y?U+u5$Bu0|@6X-5M_ zr#2(Ah}FWtB%%eW$z>+X#bsm4N=%lE<3%m;idJl29l+t){F-1q0mlToG$prZjQChI zBaZV5ZwHoYvK9oEc9Fj9FEAEnj}vyn?LzK_pbf4|jK!|b*!Wny+(CBlv`(d8X@MV1 z4SGsPl9gonAZE)n?_^$Iw`3h=oS399!sCZpZFic&2JV$hfE z@9ZKnra)!}cr(V!D4#&--hi?_G?)d~4vxj#DEkWLEHxE+V*^`8c&`_4a~_&X_@93X zkt5^*2w8hCmQR6Ck^?=*XWVqqtS^|Eim{i%`M3xe>PA!WW4dRe#vx^>4ENyDW#smV zJS35SiIE#2{Ra_wfsnqJ{17(YLPCCVNg$%hkMtpbAU87uSs|ap2vgWO4#9+WZoEO{ zK2LjzA5$X^4JfH3MkK8H*rill?ceOI`8w6SA35fy)Pi!o2gdzyoS9MANIW0|)1l`4 zmt#Pzf$bbiSQZ7B%BKZ|iFsaL!cu@kZ+m@9e@>htv`?8+;6#RjP#me->qqtH!~wW{ zN;t4oFZ8uy6iHa*@*7XZ5&%R=BR&RZ6bDw(Wo}iXrp6(PjHszQX zm~fwzS<%9V6r*~gW2@FPh&TsD5dT7L;J7jrG*~wkARgrI6a@{yG+y0PD8XhVLLmC zW=Y-7;!=1}CvRt`(k$uQ*&3L&Qcp?S&Q2g=Xg?GfC+ft_{t-M-h@qg+eh9((8K=M9 z|0H#kGZO=3!Wgq4!~lfQk1um44q^b}9U|5uC=EDJ;74DZonj(?&j}(11bNp%hyjRy z5YcPdE;F8(S0J46**)S8A?av}@-u$*zFUU<>WywLfiMo%IbPu+T6G249s9fwO9Igj zH}WFDmkYzws_WYBIE?L7?v0T!1h}6GR$ytJ7oxg>)}9Zw`^$8%FL3XoRaynY-wbz& za9t0J1ekr^|Ci$CKF-CSGWY-uN%tkUldud-TO0_EqBCuEGlxe0hq=@hZ4k_st_ySF zjM=ifnTM^+jq%3J==NX`q+VC5++4!|DQ_=fT-*)b>{U(3nWuE6sl&Z!BWa-2)UexA za4lVX>Bh{(pPq}hG}e-P2D;F?Zw0#6hZQdAqbd&*d9HE6jTTtHKK2qeu;3d&*x56Y zdXYUiy9r-W7!nJ^7o~~wp)o^fhK&a``51^yW`BZU6&GO3*;j0IYdAihc>M9%W_n?z z*;CiFkE*C$u>7MDk)~GNrgLlYsM1u#u&(vN@7dG;7*rx7RTfpFIZQgi#CYNCcp( zwZE#7>ZJOXy(>sN2^66B9t!C#AYLw<^Ja=%r4hwBm<3652^?*SXGnGe%OZUhtC%LJeE;NnxXWSpjwZ3Zb4tsy6MrX@vs7?68JbtqY0WCz|J5? z21$D*_I#Sa*oB6oP(}82gbJB@fHd|zNea<$rW3@M(;BUeqk62-5RMrw?}q*XH2f7b zxT~M2WBRnn@?8JalR!?ZV&P5LoU>}ig}eAIhMt=9LCX~LEeb}m=fYkOJHHe$9&hyF%xuW|FnVrpZR%zMnYh1;2Hswj(Q;jIT}y~yB`ah;G~7!)a(qN!N#rg} ztqR@*MNymcgb`}N8fQM4Q%fGzW}YWt{-f~9cRV0ms>w7L_E>hE`Xx^LVYNG0wnn#- zT312|Q30aB5^B|F!BDMM=u4jvJ2*K)MrGyuD`Lr8TAXk!Cgx7cPL4$H}cgLG;`CJRBfrA zi}M}q15qgaLyc6;&2j1k6Q*FCDW!6s0H(1lism?ds>mt6jzB%=IUh{rISVkAvjKH@ zPMvsu>Q`G3=NqUKD5&Ihga90}Zy;b@QO8LbusZ(+Gn8}^Z)e|x1!AREcaYZfa^$** z){tw(^JrE#z`>uzEIwLP9t+jrl)pUt4s5Z^SnP;n%ko{Z3<}jIJFZ5%@pn84 zC@;%&u7uO+;hJ!`_dS><#~j9YMsa3ASYm!^CX$lL-Q=?H4YbH*#P{L7I8$Z(9O3#2 zk-*|iD4fR33_Z-r&ymQ_n|tTSD#z8DdF=K5 z5iHi|AYL{6veo@KCQoiI$76Sph-!B|sp+-UM9@17^ro=OzmxN=!0>1kQlw&m^3Gst zfmiK<-&1oH~2DibVKva;>G|9ct=)C}V1eOp? z>cTc>zwZTTF?|G>%q$Z!2_?G^1L1}*xCBR6dUsuQXy>{c1GczV+ozznJ_$@s1(Nl| zIl9TDnFQVw+vp}ItzR=#FAn4Itu1%*vZqmQ4nlqh9Zm94vtR!DAWD&O>6BW6E2&vA zN`(i!k(W8fBxRfp{dg0z_wR6C>6Qnzx=MiY2tY$yySc7NiwTCgv|GT2D69J^xZ_f* z4rM%!Zd%#%DDYM{p_^#_ekMYTd$Q+Qc;NcWQVWnh{`K=NHX;(5TB*jD6~|E^s5~kg*)-hj#at{z)$_CMXNV~5Iv*`OJP!C^$sG2G+4cZX=LhFZzZ6= zw;WKv>%kN)e{T|>-W0L&jt2?DT1ZmY{2O;CVi$E{71VD@;LB~@FpuamX2ClD{` z+SxXk_0t~`Idn@A@qPLr4R&@tjC?Ij4A~Ent4*><6(SsBz^SR}j-2{rer1e)8k7xV zUzf7*=waavqiVjP)ri;(v07qeHlH1V2^R+kS&iwenD;UpX;Eoloj5(1ABfg1u*>Uys!k$$^MN)XeShrRkV;l^99d9PVS3 zXmV$9CmLoUxkoS9K>=GqO~|g06_X~2S_MPd`#}{{l;fW6X~=fWO0$WFMLhysXG53` z`t3k+I21{r0;iqPyxP&7QLhSwPz~f8(ipq`gq(4Iz}bhsSPi(&(S6XXCfw?nyVSy; zI`@-8>MTpCJyH*B4F1-2B7uo@c@YIDbB)bL6RK_-!;8ibHm2xgoDZ_S)!@tiiyPn- z_;C8Uu!x0(AGD6Cui$qVek^IsAQU!jRTqBS@cS`-kK@KqzB`r;SmM`l}Z68U*k#4+CgKqIkd*>Nn5mwPP5@uZ-h>YOwdid!24o{Xoz%t z44NCJIf!srhsYvdl9lipjcEYOaA}sv3Vmv#!g)F&L;LvxL`<3}rF~OzVLsA=0(Xj5 zszT>gGLktK7)@@Tw9J7{;cFM|6Vnp5iaZl8mi0atS2(>BD4_Eb6b-ZnW0&ny783XZ z`{O~jQnx87_!}NzNP7;KVk%iQjdf3c#Xm>WdatSP~ z3VL}^!e9?jD>X(phNJ;IInHZKq=P301Z70S8*9}3mMC@J`iX-|p2UlsQKAgCVXh6& z)`^}?NP8!=VIVLE!5t^~W!W##kb>b58Y+^BK@Gn|OY_TB-qd6qVFu!Z%EEX*FQSh* zGc$0cI>>Vh9S&^zE^=H`md7892oe#TJG(xQ7EC;^f`4WUQi(_03HeN%mEo*69Y>YU zm@RJFJW1nAi65i!Vjd)TGkH^LVklldBS73hyv-MJ@$=CpaIvtq3@62@1uxSQj#tdv zK?CkR_>pUzs_Vy%+KS={7p0$IYm0>l*D|HP4a%&2ZCZa++{h=;Js@uA*lT*7Ez zOQkJXO+`;d-PD%pCeV!z#2!mbnGi)<{Y_`CzTe4-LZmk{fZSH=fx zRvwF2LYcL>K)4fV*an*$i`pwL7l_yRARHYJLdJ^p8CmanJf;VPx!8z6gH?|KKvG;mp2F2*O8gH28u0yVA3XLh)CAx#?(lI zvuCvd_-P(I_gAnnDSsnHWh!TzFkyd%GfAy1bs$XTIZh;#InE@LISwVs9Ot2_9H*eE z9A}@}_Yjyci%Z+6EU*pP1H^hwVX=XE9I-ZAgn0)b024M=I?qNQOn0E$L!WsNIOiRJ zp@={i7~|4+cnjVE=tV$I8n^JSgAfA{;~az-fJi$CF`%fqP>{886UyixC}(-~0$n8opLRBaJa<7F z!zv0|Sdyb*_|soU$;b;Gf~s~=5XAd#__`MCk zO8l1K*YHCCJG+&|ZfC!YPYkEh+|4l7VhC{4s^d@QNfXcR-bah^j{W+;zw;l&&N!oM zi3Z)en(FQ^pMh9N%HD|)i*+*GFC?ihgXAWgF0&%$97TI6-O;=*Si3(RyX6ohjHK|QBd9$+vo-1!NU1YkMNs8_*Nr% z9fKx=b{&VIBcJnnM2(z-i#R!lb_;LLY3(7vW*-|AtV@!^%_&GSMkd{21H){ zUFI=aHw|6*I=D<2Z*?;!(}d4OM)^b$Bg|d__b4okRxEf|#I47C6DSJiwLzA5Vr-Mf z%}90QMuqDXq{3ZWF{>XtO5*0vme6V*){!_w(jbG_ygF>L_JbZZec+nKkQ&slP}I2I zTty7@W@S;aK{*jciF89DCG@0 zSR{wyw7}9&5H`~Oy4dCV8Rjn>0;kNEr}eDvWpENn=R8t2L!EaCe`-pVT30^fdwY7J zU;XdjB|HL^>b^?|YMz5#p5CLF?PqzH(7pHIyi4dJ`h9zE9~-<6Fk8{V^465zaK*-} zmHs8s+>bp4%mm{*E{t;zG9bI1EruKKX`E6Y&>NwtfOax2&gN@1&J3nB$!BSRqw3UbfClsi=rj}R-w+A@wzn>}fZn%G2KwE7HpNHR zf^yRP$e(+ca}gT5OgF@UPCK6nj_;GuU*0GGMHeNq2HWuq@wG3ZK9y4RJ*ZXho(l{< z^YH0}&=wSZ=rPJx*wRxU^dG*==NF~3Jvy{s6wdZA4l5W+LnxC|GD);HWTne!$K?Sy zbEpt2;^sKUv_3={W$KfG7opyv-^&v*ES$cWh|>wiqr+A09Zu8I$2XGut?A9r@OvR% z9YrkA{KOK$heTn=lH~%Mlp)LRWWd@58KXpNOWAYRR18`%DTHMzCGBcMgYY3%4W#l4Xq0^W)o4qpk4Tq}4Xb2Q zeqoy~)d_fWQHu8hoFDm|Oi30~LXa)5rs~!D09>)=!EYO~$G229wiIuQHxpYFOiuEL zk`s$MCV~NX4g^i72Sc)Ok0pyU!*CfTQ;KKe{YhEHab?eQ_)y*JkyJBY@~PXY)qg;* z%mwJD`cVlxAQnrCg->;f0SX5AfKi?Z-164D4tUSVgDxv8Gy4m;kr8ev0iUw4_+@b= zB09CQh%{($oX$P8l1JA-=bvWwek(k zx(weWBz^}3@pwsS z0$wJ^+w~F(Sw0p~m7vhDPr+c#lOBr~!;1SPjIMdo?eSP`TGDaW$TleIm235rWU1j3jY6DLHBMG#^Dg0Tof3_u78oNtKX6l1_g zr)S6!O3)uD+s63_|1{ZsCO+r56CcSup3ZX-QUrF#6)r-j(6P-$2nmt~_O9g5?CCqo z6B)ZDxiowFj9ur_AQ-#Or9m)uol8B%x)Bo)h(NG@w29TDO{^VlV&#x+nl)iS{Q%V5 zDLh~qoRW11TiwgWu+Zv0Obj^xBnC4bK)fs)#fph8d{mYwyFJ(r{!CQ+$~L?co1uH)Vj~Qx|lDQ_xT~D+N6XrxyhU zjaM8dSRuE#u{NhaoO7db(^P%4HL!;Ud>AJ_5?(47?VO}yrADIk`Dr_7;orXvCUoin z1?5_)W3eS`9xNlp;KO8f+P2WH39yAcAq}*4zR5n!@p&=Qma+(FvUOpm!Fe+%7Lw~1 z3r33x#leA-{kR?)zy|NyXefziXxBofMkf}MbYysI!f>GXZsyX2{@(d_rWYN`Jlm>E za*RA<^~0e9n#?f?Fw7h`{l-vvEsx-{7s9pY4VWBtwi*LL5H6MrLP60DLUF=X7%Cr+ zgsMm5V7Tjq{fu?iz%zPrym;Rg*`rmSWxuP#F>jt&lpvF{K{id@58HiqfHM7A225PPjUT& zfebtr`Bwj8-UVrAE8wKx8y*A`&lzy@67LNoKR9O?44iY$FdPy5+jEA9p^AEOk(Klr zh=Y2LWsVX@x}P9j=%_gn32?#8G-TWoldb(tH2$nK)g;%B#rli!q*~HBe4=@4B}l_! zrX7(5@iH;T))9qaQ&!RhN@Ha4dR}W{hoUZT?1%=_idN1QIA<0E5X}xk3_!FH(Q4Ng zb=-#eXXlY`2@0t&^CDB{n+_#xR706NG{Q>BCGQ5UC1v>IJOli~ z+>4RNx5r}Jv;ntXvdh>PnSCJU@v;wgF+I{qU9{@V+8v*uc%H(>zV~hqtvcU&&Tz0! zQqlx_GHJSZahRsAK_kt>G<6Lc1QXGfzLjz3WqhIv2SU`X1U}J*KJL%H2oB91S6;2) z(|8ZppXjmeKZSDXi(R*aJhuI(%)!P73d+Xl&)l1E#*2Aw1G@AuH)C4a2VjJ%5H`W= zyC`+E@2H{-tI#8ygM#Y?Z=VTyI&U_@e%55D|D_b9WqsrpiHQ+;EAmq}-?q-Tj`;JC z&nHP(q~WX|F&Fjwh%WB)gpwDfSjQ|?{qSBS<=|MF23HaCTwpP{K@QI2Z*XX@=W`>K z4$k9mNNBIeb0b3?oX4Me1n{AbZ5UB=YXuKIo>`)o4DPMgfJ!zOd=yJRJ+ z_$4b50}#hM2r&S$+Chi`h&2vE3_z@P5MsdVyED;)(i7O}C=0pTY5)2(#GG{0+BSB2 zPe9O~3(%ab-VHP|4Xbw@jU>qGZKsi>SiL6#_O=n61fV4$NrKh89DZEQosca#)WVPwzb!e`34p8AF=OB=5Ty~`$^CPrpx3wHD}9b* zDCJl_2y6na$HPW$pw$r4F&6IG3i2JvH5d^QA{Hrm|2MZ1Nnvn!aUG`c9n0Nlq%c4e z*DM>|^6;lUXFw(9rB2eOYk4b4GCYSA>DFPS5J{149cE)gizPC76NIVj#vnn^PXp>i zrtRZcE<%FGxMF)-Q6;X}o{@{;d$SxTrlDXnwN`pGzO}`;(1fp+J#>74X9hD6?i24b zU~vTVcwWW%gO6$I^QtLU`a57lm6G zI(;t1xY4|n#kjGt2uDgZV&L{ijUZ8x5ig2qHBaskCL52%L04qSkIm19hq0p8(ond# zbP4EztW;2T!re`jnODINkVhkpJn}q^JPJrH`9q;P<4YsfdVGis5N>$6J_NA<2(5Kh zM4f5|I!T+xqUPgZKpAD=`!lEXA#}JtP_as9)W`zix}12CM}-q1t;^ZtMvAptI3D)i z@#F=x1>EB*L>~g)I`0aiakYV4#aj0?CMwZ-=w>a%K&4KBFP^+jDey%M1<+V=wbLw* z#1W3lAygDh-_Kfz*zpKNpP(NoqT5AfJOYuQh$Hd?B|>?}I4*Ng9@G2O@i;9*g!T=| zyEKUXQ}tYoGH5#LdDa8IxQbLD@6!a_OTdkSrx?uqqpTieUXiveL8BPp0EQT7@g-{E&Fzf3;eyGlOBBtOg5{ ze1Dt16y8kJP6Se&LolIbBMl|P6)ngcBh8xk8&4?&H9Z}R|jR)6FdRxNC zG|CO(L{S;G`OAvrD8Xo4EpjjBsrq{Tc+FTFVqnNKE~!R98HyD$(9+B#7IDJ1sp|;Q zFi*>X?rpRpTrp&8#)qvpyBd_kTM%u*Jfo(Iy(Y~jCu}GYv*R(RNFk0G-I^2gq`gUm(W~1*CPi4hfU_VzWvA3 zuBnuDMh0+y^CqNKGa@V+i~Is7C&HAtz=bKo$$_vRizlPUf~qO%9+dl{Md~XMaDj~B zUVO3&YAwzv7MSphfKwup;wcRd2!n|tM@%#3G`eELP)A>Vd1~L@#=aQzYNdBquBBH)*0g$K~XU! zM{8%J2Z~_|FdPzUGr)*pWPCsk6dI30+mOFW(9Tmc&hyy()WPv+ZIM5_6MYoRXR36H zrV`19Q_ut326k#XfX}@?mirt?iWyyGS#&6KW{bfhxvIC=H8dHfVh9 zP=iJrCLT77C61>9w5QbMO6@D61Je?A{EX3l22Cz4j41 z&66c)m#l#vvJUf*MPDvi1M_5!_mJg{DKk(z44w@7^8R;LVpZOMq3^_8ArT1`hZC4g zMf-~J-a;KvF-SOvpdd{T!7lF{g#PvwHpCiN_5p8zej~QNZE`mkkycSIFVW4`t$0z~IOZ z$>93(x7o<2m%r80uKC+Y7{SLHV)%kxrSz1wkX@zzf5on5?ZvKAm`dD;{@aIJr7(6A z9Ru^3;xeClm-$S|GatUW+=u!6ip*zZ?$XPafcfzID4CB&fca=lRY)T$WA?dPi}gd{uqx0-kb3dy))%yJZ^#a zVLBd4$co0=LG`_cur7l zLp&ky9M7y2+kGXWl5iTsB!s(ENu(qy=2)oqG%R%YM=ysfKlPJJ^kly-f?Yk%hB6@- zI9)9+375cIOQa;!(uycTEh{0~lY!97NRlZ5$S)*-uoEnz#b4ZyV675(C1ij~(OgQy z;o2=F5uLPte7AmbiN49SchZRf1D8Zw;(?L~VnVb`JeZkOf-TJc6AvVzV`%F!Cy@pIYI%w35HN*_B;g{?47WLNN%54GlR6#U`#OjgxYD3-AN%oXU)3^?fZHV zxEo(*{Df8hAkL}yLsOTw&$kXvfIW;hE;?b8%l)!8fO;EQ>f$=qUQ68u;DXcZ0Fy-{ z8Lj)0i)}Q-MzGn;t;LG*b_M2yLAmSGnraq>O^0Is&~Oik3&x`|sB!x$IOE&E*zV=7 zh#%`m=#RnUE?Kya%?xvV>#29pwQ`RHdlC6L_e{JKi+XhD;4`7A8PX|QBP{O0$-LOF zFIYvLkp}Kq3X0C*Lido2M8)Wrqq~09h&^!Lb)@m1Fbej7*}EO=0ke0)XcqcVd=YV) z(1^g&knCxP(>R`pI<`pqP}mx-VIZk_C&Zx1jgFtf5Li4g2-kr9d(cs44$<+Y#v2j_ zPoC}g$15s()9{}n#I!Gx2+&tN3N8$l(7I9^r(yFYMD4&%^!TP)B(EoP6aP#ChSQom zi}dzqNkF!oq9xo}G|oLAlPmSD;|PbKKbQ5+qOyRgX#-@|N`O1sl@;54=*7*0mq4a4ep^%X-N z{b&;pcFJ=+*-7Sjw3E#70EnEtF~{?tWR3?s6*-;|@yu!?zdW-N0}v-W2r+y~uJy~& z^1;}kuzEY;+(Rdb*-4OAtM?nI!`=?y^cliJ&{_SQ2R#a@%U@rE_t|j?brw#e4`n)b zb}nL(6kwiDv(SShqa}s$EqWBoc@&9H{71A&KWN40pI2 z&b`FLaJo>;$qd8kLeU2?$cLPZfX79-UQK@iBN#L|V0^)_sx7;?>M=*YBURIohvq|~ zpDM?A=E;-f{pWH68wfTuIa;32%|s7k|GC^{#4(Ew!Z@DrA{YmUS@_7u@!dki{&TMY zLgxn8i(_Di z`7LbSA=`vCaq7!CSob{#SF8IwkY9m@;-7IZ9ir+i`02k#0YZTv;x|uV$AjS(*r2*W za57~aEOUuOgx;D!0932%E{k0?4sVWCLdcjIH#!a(oAIL0kK<9}u8)a5?gkNEV?5~d zVJGVR_@?vG&3(#FEH9#oBJ6DA1Y;!n{?0M+BFrg^^t!80zVi9O?nxNl!j89)tJ6cj zyYr4O=CfovhZ`NfPtw}Fh`FDeW5-J6*za%)wPP#VV|po@KF%Zixy!W){=9d&PNDsE zR0IU60lQyDHW2R}^ZL8X^>aaz9i*MRT+2*vJCt9MJ(`0ZNv}F#ULN+R8(pjs#^`gG ziwh&Y(9-8lT=%$lxj+qD3xu_kbANrQrT=!pP z2yq|j(RaIAs-^g=hiRYH^{E_-CY57>knx?$?+RSpq`^v0NBX!iBG^+3vB=#T0%H}ZV`A>euc`0~7Km}M~FfI`gOl0lDegqSQHy9CCSV~6M zMB_2ozjrD_%hbR1Z}SwuOD~c^KxPoP2b3JR{J)%wK;O(Ct1}O>P-X#SUJ%~Q6H%D? z`QFU)J^}YiX0eayYKO=k@3FC!=*Gl4$3?3`KY`9sUQhe*GrG`EKn>n29>_%U0ff(!tanZPZG$^%rL*VFtz4kN3Kd zLz!$k0*$+-LR&=hqMb@(M32_`gRz~%`QQ=)NdI^U{R6M4Rk{6RV71)w(|seN=IrnE z4O`7cy2b2so3QS)kaz>43}y+(V;zMv!AdKXoAwwo>^{T#Mf;MWxdt-~p?SEWd5oOJK{ACI5OcG1bfLqP+G9e$ zU+Q4r@sQ0p@zAUpPeDBS;y`)$si#BXt{moiB&lLTQO6Y+gtab98$zc-5~G?LOh!|& z%w`*Mrt}IaR@ZG>xe4B48bzulmN|sYQ~Ttdz~SLwbHz%$9;#c2Y=+ePL(z;Cipixw zIRl*#41?N-o zn02^mB~%Xnr^1+oOHfYc@^nngUxKRsAI`i(I%@*YdwDXC*Xd2UFq3!>$he6u8mz6R zPGej=%;&EKvgLRW7%%#Gap6oAUOD$vDg-=SyF?ZaJ6Xn&GuD0uk@BG`teguX#j{n2 z6pv@+iwI&Ju3aL<<;Fx=Jd#L^>MsA@{x6<>PGh3H0T+v%F?LU5yxgHv%&qRI3Wrwn zjuZA|n&Rb!PKEx8-MqqIU#}1v!1GQ-Ltr2fk72!w?D<=8A17XnUl3v_0oYpNC1M+Z zw`ZUci5^*yVb>$>eWC;$kI^_i;!ND0R&K}3TJUsDS<6a1U6W{OjVGF`PN{O2@;UEe8zRmr&u8zeEY*GWRl5B*eU;(D~s0xxi4EgHXM77so`*Fdp}ZTk#YhU;V;P4GeMr%0#&HNEA&1q7CQ{?O3}85=FT6 zoEe3FfDLLq(1iEPWge5|+5ng+->!>c;}wq~`Rjfkr*1i}?Ogf#bO)9`R`2OpsPuGj zDc5@@jh$lbrtxGkZhH~&&n{TFhS=QbC=37W2qeTl*%zX$sBW%%rO&MD4PW$>((ahz4W;pc`hl(gCqFT25+ocS>CaJ+canU$O8HrCP7brv*!jRyeI}XFO}4)FSZDj&p+E`b zw6ec?|32D$&Cqx$oa}7STJ{t57#e_j(O?{7E>CtMo8c}E3T1vB?A{I zqRm17@JRFU$WfO!SCV4?5Mm6BLR=*iU!CU@u;&1d?{~B(2eb$b;Wvwt-r{q@c)Jd9( zJfg}nW`tv%JJ!)nTI==FzA;c$H8bMY<4Osa8CQUY0%+>4W{j@KR*6x2(@q`^g4^X! zG9P_3UgMzy6AD5y+SL;$RGey}wQiKrdE#~a`3rK=){I|Cr@*gN3~uX0rRua_Y2zF} zEzwyf`z>*Ya#{jQ#A9b5^kOq;Y%rk zSO|t@AiyxOSK3@0F0f-Jo7vw4$vdDmNko)eU_{)VlZaNh5w%D}c{>J5L;qN$q?$_n zt(ax&BBr#oHg=zjir4S77>w=rX$|3j=&5G9qR)xtNS)44lxvpMI}P!HEn~{GE|W1mc35LuNI-gE54yH zPUAxQVbE|$-UY2WD9}-XwmA3#$h-Kc92QcW`UVx=$&E)GV?{j5{dKUNW(Nh8q>fSF zgUT!^hzk>xq#Hpjl%yp)2zvG`!u6*@EU`$sRI9 zItruWL`58F!|a7T4tj~)LBdY|0MWU2;9cEZw)&L?K8LYU-!7hhFRU*6f*zlzz;E=s zfOCG0s?><29OdQT7EH~7^u*q7N!@(*3bPkNT4t(6Pk8+)s_tRBeUsrS%b$Rs{$o^< zC3?Q34T$nw8M~n;*o&twUE%6f6dXvU_rr+af7I#q=;k=q3I1nKvWT5DZLfHE+_{eo zBU&eOnmpY+aqIj|RF!U{cz&AG_98Gmeq)&dy1NmXW2}F4j3o!nj?HwhUdQ>5R1L z8rpiHi)JwNViJ|D1(*Q6^xAzq-Cf_%32gQUY;`=!X&}l8Ve!UMK5U&eu-XZY;=~z= zCn{U3^?|AZX!r31nuI$;ZtA$$t%C%!lMsQ8%%n$fTy#f+o^v_1St6W?+tt)*Dkx54 z0?@6`eQ+-_4G{uPW9Uiyl;u_i*gnVIr*OH21`^fYWQtNF-dMWs={CJt5}nqvsn?kl zjpLssT`@0Ob(P>)gL#|?$H{=`;>Mq*qs${t&@t&A356<*LR-FINmy-Z(&4mavI!%- zd#ni~7U*0$mJx^rLp4i7EY&I0amS%}9N11j$Pone@;$`U@STZqc*q6Zmf^yMq!O9NY z{Xtae>6lO9uBKmf@Q{Y#keK^<f0R4_5Kd3Z1D(9LEU}dYIMq;-p)rb)C$o2;b!IRm&<2qo9IUhH2Rb)8 zq*O1GUg7V>wKacQ(!Lo4>zTbgIW5nkXtM1r#cJdhu_hiY<2NJ?8yCOkm-OZDGWR>{ zAB`tPo;m~M=O0VyqSd6(adkP|FE4bbRVOF{CH<@l7^|)ibF-Jx=xpm{!hL3$FxE;l z4_GE(VK5aYE6sPc#crh)m|}J_wLIxIz6pgHLHjaucYMrDoiqD3XN^!e;cvs#m&Ki} zX1syyTnSGkYlz z+0}HWoYyeSkdolD|88FHnRMfbjQf!_n65iwrkD_0P^?*Imswg$50x)coBHvT*~tz$opzF459F;a%Pb9x4_JbN=0#CLwok{}T-nD|oQ4UZ~P z7zbO~W{w<90kyagRpI(_Ce_hFt}hqmOo-7jx1vHgwY`{MQ8IEmn6#^8M9^@)h|ZWj z_lN=Y0P$4^AqF5WbP!?y;sOUD1|U#}I#MwJ@l6LI1|V=HPx}x95LY+|F>w4S zQV=PTKSy2SweZu!G_2ll)5!F#-fc9J`uu?Cj*O|!KcGU>udu!K-T*(nHxk@LfLnt8 z-tQ3HOz>TTTL^9i=zIgMu7`!BOECK<*fn#pdat9eD+#_uuT%ux!_u;PuOjklf@|m^ z??AEGtlsYd_AvKW@AqkBzOCNdXp|gtUzXwx{Bu-2W`7U5dsva^7cbE)nTH|`nkDyk zb_dOpeLK60W|lepKM%8h8cImBvwtRH1iEIAP-JKSK*Sz5pSIMJrqPxP(nQ))H=0Ut zzdZ;=?)N6YtS3KZay@kS(e-!_THRAk1>EfhT1{R$p5zRpf-?-Ap$@!^Y7jbC`+h1) z=VRYbMd^(1TRn_auia6B1fLpTKw%fHrX)L-Q{PM@DU|xNJGMm}3Z)k9ju+fKn7Xvp zy}C%0(M=gMdK_39y2ET`AsE)pGSK}>cB}PsS-Ni;1jKmYgBdnKHVT3{G>CbEV3rJm z+0tFnAeax`6&)t$Eq@zoeiFth&6BpT^38vp^fJ{PdSV0gs7jr7|sT(b5{#x02v&lfWu+|DdMvc ztP{V3@xS|<09tF&SVSjx=^ZDG!T#x}cigpOK4mXUL~Ah;Lh6t?I%=KH6OMk2C+?sb z$CYqoE^rQsLtmZTWr(vDXSwWq8C*=yV@38k`v-JKT>~|paqOx)K1e3-=2T&B%i_UN zsHfOKP%;(BzJtJJMc4#$3p588sSFd!bwKsV>&kJQXW^(QK>c08%%M(lLS_k%l=+}j zF#7~z?vEUl&A%sy2YikS+<2&Q4bD{ko2DTco&)XTH@g&3*Tzttm`meH9C(0KOze|n zi7OQoPELZ11Nj-ZTy&ee13VWSc>xGN2DEG$8mn`c1zUycFQ~UROTXIyE2cS0VbY~= zgLz>L9->*EQ9#9r@m_v9*a(sdgnY5r7rjx_t_|`Jv@391ivR^O>kk` zx>ouN)#HusA|#IqQFO*894(^Qg5EcNL9rC+Jne?}fAd8es@C$Mlhj4`YZ)U8(pta# zlQvVo8x#d_?>JuKAGlmMZChqF)3HMUp$(ayc$z$vSF)+E#0*i37JK>(-11BT7IyiS zZAFkkw~|HB#LA9T2Nmf2Vn-o_4nQLgTQM_Sj<*=dS#Yo{wGqm6VcH{IpF5k<^>MWV z$|*AQoL8qIH?RAZl>2DM#y;e!oLIOvkV*u1&J9Ciro=yHNve6d^YrwOP?!ZzPa{nh zX6@N<2zg7An~!k}gt|O$R)>7xl;_vfnPvKIW$vSpmtqqa@>`f;TQ%jK^TD*BORkI7 zY%g+vyO+9093;&T0oXQA3gXMQ**gWbpI#3l^o+o;)R8>R)LaxsJg38I@M97_ld0f79<0O3dEDxd&(=^_Qr>WVE>eh;n}hUsGd9DaX{HfZ#rMIw zN_9W`i)LM=-qxW9_330VdenzRN_=54tVKkbAI&5B`4P__bhXJZGf`JiEHfS}bgs*I z{0|X%W2`V(`hswKETb=i@M$*AOQ7N&E=|q|5{HBuyCQD&GXPXl%E02FapxIWRbOVs zV-dI4nsJ={Gc+lLcW-IZ{k3+s8Rl^z@xMqX$8}S zP`eJ?uAk#|hTTL-*?><5R)B|wl5>XIXuAU>O9DjU`!VQEre4b8Wl}FZYknE+Jb>?b zd=IqvW{*-hq?hr8cZ8pV+m&!T8{akfHrx+n*#J}oSGe)wmh@LpH)W9TkOQ^!$*_UJ zNHrndU~`K+58rUU1cNW0l9@1wgi{DN%u>c!f-uc4if3|y(mHY)<~gZ@$ZrR@UYF`GW1| zO>JMfK!N|K;l=BxIUhWp~Z?1kb#Bss3Dp+v3+CfSZ&7-a|ik3J9al1 zGnxY=X}AW~vNBu}r{^I1Ij)1tb6f~lswZTxTb9Tw64={|iD6G8ZvGc~4u&|XT!@*4usIeS+1?8LHsbeX{2IOpfH+wa zCTdD$rt8sCq7AvcW86vEam=hW!Q%OVI#}i)*ToHGTd511|)U3A>rO9D>-+~pZ@qt1dKMDbWSlNFVHk1q= zKut=+<(Dlj9;il;$x$59$Ab_N%wUe?pImP0TQ!A)#6u`JQ)Y0bO~H`CA-#~sqNTR# zo>3N%7Mz?=UWT`in1pnVX6=gP@JJyy@#QUYZt(M@{89E5K_{~(Vm8%x13!eWqqo}4 zrhYm|%p7jM>#cY3af;Y05Ia~MH{%Q9TAmRZBy7v@Uq#Xb?qV#$cLZax*GdRB{F-a0T3W~~P4<}KDULhF6 z?NH%4oADjqw~TFEh{3IMmgmU?lD*DGkW7u@P51`kR8eN2MubA^ zB%*aIua%2Eyk@h@Ep$UwzR(AgES?)K>aUDH15Mc812Jv_rdI!2P$r%(g*#rDjz)C@ zb;_zo+PB|n-?ntG^iYzvvmheV=e+S{=OelxZuUIgBkb%X~8WC%~b4eG84^F>~3-rFFUcj$xVa;8uW8ABd}M{L#t3ggWXMTBi?Ax z-UiVdt>TRayPH)c-e|}|fFjxzXQ9(7-lPugZgMQmw(M^5D_yBz&pcTmdmhd&%QBdk zGj~D=XX!&Kjw;AL`cP&ilfzdJU)|Y`VOI1HNDcZs7rKp03cl+B&}e5D0W0IZU;P#J z*O$NbIgx-mdXd8Ps)KiXBt*=^mHTj)k;3k{hv&K267ihp-axCV=8lm`=XgNZX~*xQ z2r#!W0IjAibleY(;7`pjbf?v{kB%k~q=hhKw6Qx5a}hG$*d41$@(5I`=ZKkBX$;8H zX>@0`d)(bt0QV0|og)qzk?f8n^6(TJfG2?49uVMSMu?3dkNXX@N^`Ju+nj3Y##d{& zoJMO_r=2u!!H~~Xg`sAKTe>Nxu>O}OC#}rGRP2s#ySZnlkhW%XZ%zLCyamR(wxkbl zWfhx~5Z18O&Fb`GS+ACK*sZKiL!!f%1SieScg_OJM>moox!r^ zEa}rC7o%&=l8$WG>c8569W(|HbIC12$q;2#BjBaV$k>@+Aw&0LK`Z_W21(#4?((en4rHSpC0?sXgK2(Qa3PTz#sipk{`PRUr7x-8O7B*02fWt!Kc!SWrNEi8Pk z_I!k{671E^fe&5lot!vvjzFc$(19&ZqA;Rb=P@rIwa^>aSQD4_lx{4Huqfn2=y5l( z8j@3nTIoL8Nw}~!f_Uz0AByiDfcO-+v2^6+vL3e}`IDuL-f7MYGh zF_~Zn6LYj+Li&ACUe_k6&RhyYkqXjoaa&7smD@&K%XV`;8SRbiHqU~u!O>=f%dr%qM zg;6~M(aRK!&3+bRt{L^26$-n9Q==3!)&n%?czv%(srhgrUXVA1q12x&8X|uuX zVnhg0m!LG^m8Ov!bO`4iZ1XEq*#qE>fw=c&T*F3vb_>wl1T$!ezKFFYm4!x-XamHd z6$$}+*aEQ&%}&pA&V@FxAYEajNiD!Hv#Kx!Xabi6TZ%G^jSD`euW<{~B*}9_5(a-& zh5j5LRJE>sXkJ4d)%_>$$q8Rzr4=`+nLLg77$)FJo^P@me@x!v&qIPOSU?fBA4>URe5?ISq0B4nHMl|tZT{7 zYlc$sJh?rT68NQ0^?5i#-}#8EXr+EP;^0({i~dxO>-|)YOMThvYel@64N$I^k8eH- zJauAdlYM#A^RQ;r_<79rLsOpzI)i!nbMyG-n+V3IFHO`CWTdIjyO<_UI7*z(yOF8q|}IzI7kkvicf+?aMZIcCA@I{;)Nj*-ahCujzD6hpjno^_q^Fs`Z`6XFJ;4 zR@GFkSkuwjmfbMBZu~_4tErm5sl97c$C}w2)@YU!p`?QO>&xMuV6?7B4@ zX0Mty?YODOPpzLcee#qw6C0+F+B>mL8@k%ouW?dewx)}0#@W1HI(Gs8pZ`hZ>aU+W z@}A5uhCN$(OvlSd?wb9|HN%d&WBAA`ZVj&e)$SWkn%q75 z)E9Pa+`Xt=GT6B&#Sln%oo%yz5%#pxzF@F4bUu4&L-`sV%RZ$x9@uO`?=kBU;WBAezGffk1zPj_RDu)x7C+`e|UcNC*OPJlKUpS zdehyzzVZ!U%pePFuUzm$ubTw$`I}$6`zJns3n20n|KEP{Kip3$QL6p+6LvXIKbZ(? zpMEj}sD1R4gW$ARKUoE2KmCNV(tGujEkJtu$wiJIZ$Dw@Dd;EP24Wxm;t z`hqR;1^s-3;s5jF2WXT(Gk%cfdCQi}^C>+D#9%*#gV@xG)9R+tX8_C)Z}vinYE-XM zCqm?~{Y#jiEbBtqpU_Dk&wS6P?ne_Ib@(!M$)i413f!pu4?lpj;cvr!&=T0^wP)G% z_-CQ19r5Qo8zU45`Or6+0wGMDZ1jitJq!NQ8dho{+;4;(b0qaUeAmI#JbXvvHwtDy zzUBBGj^8g~pNVkGV1{=YtAh9fEx!Q1a28#8#GzrE90%2{!OMbC^{e26fI9@fAkYfE z3QQzK{G?EMD5@F-bD&@j7t97RpDl1(=%P?i?FwBM45}$%!p7KT!4mc0fZGQIRZ*On z`ZB@`6U0A|ApT)7pIv_YfD%SjJJ@s@dsJ=Id6#jD1g9tlou*3jZ zqGFX#r^?jU%2O(X>ds2ypAh)6zz+b+RA$I2l_hG(P{w(E74veNz{5r?8d0JyA92=* zsHz{y+$|RP{ut)^s<96u{(p|!F|I_7N|UNb1;(mrzOIh2a~9!%M*2BqG0htVer?H) zaZwdm%Cw#V464UhlKw?UUlc-Vtfv1r0i)`Gwb!Mhs%P!GwNdq>wU+|^T;T5nvqxaj z33MGRuu)*Mz*7ZYBJjQwzI(!C-)~QlT0QZ_6QXK_z!fJh#CcxZi7bbn6HlouQx^%` zCh#%9sCxZG(mAv3LHJqG_FaTs4RcUk)5h|=2{5XPPokd$U`ic+5)?73Kc7U`Hvpq5 zzK&&e?sS%LBg{c{$U5TJu3NV@sBVKfsNNOKo$d7b>vq=Zr|rZ?*AosAI32J=9lHK$ z)cg5>LG?4i|3=`;0(S$Jsdv{iFUM_Qy?1S33^xPr@B8xx(iX`QvqIpp0?!h731Cz` zlwEjYRQ*=q9{@oK@Fi;4#?O<8$68KRE>#n(TR%cXo32Yui*I#uKF6tue?qaBm1l}Mp zu<4=@N_7*{9WLg-ox*wvoJz_^oXSwcPh&~$e;UjDZ!kyI$AY=)bhfVFoPO`=CF%n) zkLqSiJG`6puj)PvZN96UZS_LIpWJg~4|-M)V_Vis{5b-z5%@?i!)9`f|8{{p0ZY`2 zXEK%VpS5FLQoVcDU(brF=-JHasI%$wV!#r0-`Na%>^UsM>^Xuzhxxir;M2gAsGct} z#jk&H$2f8%&6miD$wO0W9C$Cy792S8MX>Eg2$KASN8p6NRiv8<9AlaSI-!`>J^8T^7ZC7tgDFrc@#WAxOXE8?uta0ixb*I`7 zSua(Uu*KC+RK3MA8!NVl)GR9>=UeK#60SzvhWc*txQ+7NCvNrPmiGNjY>lv;rY8Dc z_4u6Xdr#c!S}w$ZH_N$ld_d?8>Z&@ifmH07PgVFmD^{+wh6X4Y(wolWrz3< zlyJRpdqy4YJILd9qOVij&WGD&YQ3+^<93?wVoBjLi8JT>x{b<3F2|u2e9?Ec*zOS9 zg~oQT*e*4;hs1WZvHeDDw;0|1 ziEW3mm5Xhsu?-R1%f>cFY_A$yjo9`W+jOzLZ)^+17POFCl>9Q-E>jWM@})b(dQ8^} z+~O*2J??RvX$AZYw;FC^)gmh>woZviz6-xHkhH zveIf3+cslc@6V^c(mGk<{HcUnXLWeox~#2|!h@2+7V82(+sp4@t5rSLH~bOw_ua6K zf$egMS2tF=>X|Ag%_b+Pr9*gh7wORP^lDSXo!5Mch4f1FzGyUHpJQ^7tHl$`_pOt}ZI<}F z!&>KY`?1w6Zu8-Gi|;OLi`Wj9SnjsY_4xd`^(}F0h1+@1$8n|DR!g|Yts4WZqjuP~ zt6y7pirZKW_F0PBx0B}UXsRw|e;mzS){U}Qw8+6JFv z)N9s|AWQH&u-&HqY7O=He8U;eUf;FvJ|~fNd#krJ+1!EA3I@ z_7dDqQ^(n(J#Hu1P2%<%+^+Ry?YSPe4*OtndmC=c!F3PuxNWjq#qB+~m8nzhm0}A7 zF{b)XvwJ)~Pq)7qV!4#SZM!|cm&4Qz4s75n#|^uA@kA7a~WhuavuoA-hE+yvW|){kson5A@vguBx& z4(ChhVSB!~og;3K+6%DaRO+i6Io>{Q9~ib#n}6@3?MY*sb}DVZwU>o?95mq^&PtxM zSB4MG{UEen9h$p8v>EVeK%W{?`VQc+rI;hB^Gh)|Qokro01k@}1^hyM9N-t@lK}6J z&jkD=z7TM6*&%=@3%o?&y=BC|C2(AV<~4~Hz;6zG1n~NSPXOL2@X>)!!TcvNza`L0 zJ`GG+l72=BoGI|2Bz+zw=5~SI0xuMJmB3pB-X-u6fjg4xk(a+Ee}}LiCI19ioFc4B zG5$J%3j`jKqR-W0K26|Ofe)rI`%xdHb^-b-b_2#L{suUx;%&f;m>a~rM9eK>UN7)m z!CWHd8^wHwm>(AN(_(&E%x{a?9z?221P&57THvHX?*Kn@(1(DF1uh%J`Z{{hC&0Af z@WQ7~8x#UOdl26HR$muC->lpS%mbC10DoV3I^Y|XX8=ZroMQ!p&s1$zD{_CT`nkO# z_ol#41P&az9{8anpYpB9WkznsEV&LC>?^iF3dcx;duLA}ZW3ZKP^qYW@(f{x#)o$yeKvM0nE&=>_ z^yPrR8_hBajk!92k`b63b3M#M1&$fBJ>cZ!M3`6P)(bpUU^k#ooi&D~cikA4*v(_l z3?|j_feQf72wn{MQsA3_FZjt_PaMazI>(Xb$H#5P-pe!NUJlyolX0&C2GTSa3uG>A zl}r=vmwp5IiD}061gK4_zrs&atr$l0v*}l1-XWO32pk3fx=d^p+V3gEP`uyyAzMmj zMUEwGtHt{*cq`Jc@0UT1z z7&6sQn0mC;%xXNFtL6dYR|g4PQN0=aQMyJ}31)-9Ednp9t_VAI#M<$xD{5GVy2eST zUtL>sL)fQotzljNP~g1+9})OO4a@U6fx86$Q{cY@Mr!F=R{I2E7%DJb%RWA_mMxQQ z&z6{N#n^D8TOd=hRZ}hD5`j#`R*czJOvRQI(V`H)t(carmWj`!1TubGogl6q0?!wC znZWG=?-KZ!Kwa0**ZwVRD^h3+wYE@htKGG41HY&Cl7Lf>@7J=OSmQ}yOkh&rFo9#o z({;SSdcn*Sc*Xd4QEMBguE*N!*(roO1)eUL^8|iF;5LDG2%I*RZQ#KvPXRt8m}dmO zB=8>sKNT3OXPkotRtuaZ@L++*)-#5zz%vAXRp3Xf%Z zzwWt7!Hk&37Fa!vEpy5=(m7k;Vu5?NSlxHtokE{0B<$a&vWdkxzm}iuIZ$6E1>SP7~Q5{3?6d5z}u!@0P}r-e)Yt3QgZ(c!rKL2Ch!J8 zCoT4Fzj}PeK}CM`^o&CRcg!Gfd|}24m|vSgO5PJl4&WCK;8&5EE8&`&NeagTCe<6W zNY!U4e{zOG?x8uxowFFv4vFUpfxiI8;Y;t&8WKa>p8Yasb+g%j(}1>`J$nT32Z;H| z*<)coVRkjtR*^#{@t)_1SW@-QVa#{VA?7Orw*fk(_wzZdg(n1(qx#hE;NPcS63AR* zMlt6m#7XYwQys7Yt<5;~qrO7IetKOz? zv0q)-#2UXG&>2l`n>)LBMefG}?-@n&{QU^;7R=8D{zl-ODYJ{6P%q9c39rb#A~04> z^BZD*U!Z>;F{OZx|1tBP0H#{tM1fNTP8ZlDaDPC@|FU^2Ktrp*RRY%u>=3wFV6VWl z1%6fFr2?-MxX#BIUYxtarFr|jHNe~=@CO2aEbu;oj|H3ftLxq zL*TCj?gVtg{&W6GpiM0xEEYIW;AlX{Pu<8YTpw;AoFe8&VRl@nEa(D$CZL0vyI?ag z2M9b=;7ozX06Ko!7jQ<@1?XV5EO--`^8{WY@FszG3jDdi-wAvL&^5z*7Z&QQ+4FUMKMT0`C?0n7}^(IyAhx z@NA^JM<7Rh%!C)RpX#2dd!_D=p+&^U7g2-ZkVWSK>K-~_5y$lDf>|h#eYUW7AF_x& zy=4*OUk?~pXDQ=hmV#pR1rcsT=CkPQ3JHunGqd-;4uSib<%;W0lN-t1MEF; z1K{}wb^=~}AgCM%NwZ$#cteu6;6x*Q3~HY1=vmXJs*YxiexGV)Tq<&hnB3wDm3LShf^Ur|8gd z%knu1hX%F{^a2@YSn&kDAJetgJcgr&p7ZBtAW5a`BEJUFD?ulfzbt3W zZwvf*c@{lr%$Uu^m_HoORyy!-)_%?5@1PzBuV70{3#?m_MQdq>&!n2Rf*jz070fl) zn6^3wX0(47<5O6tKtbxWg3&duIn}5m+JLV`^Q~MF_gz8T>NWA{T^077wI)@*uCYp5N$xs)CCgLS+Gm*;ZHIrSjd|xqdZy&% zOZnN6PyKOa2$He8SF)YIE6_fY=2AdihEDxz-Z%zr!{Lp(U3q!w=%YA8^R_E*x|$NU zE{7B9R+pz1doo9{wDlU3D?w*$y6&jkQY&&d3jF^6!`^#`S54e?_VnmvNp{PXS0Vyhqhzh8Hhze2-7^;F1DFG1?1O${O;w>l&QuJMG z);c=}5pJIQd!F~XzU%wWA9L;BtSM{Olsz+h_CC_xAl*;FRm2y)J_PscUTC)y;F_X- z?-lTLIvHF=wCkN4Rglh6NT+vibZbcWA#mXtb#II{R=Q71cN(~gn1gY}J2K2N86I0^ z@Lq=^a(koYe*%{*c5`pV_8HtNV13aWM{KJP_S|B9%7*Bo99)x~b&=BtXZRbWyH&c| z!7V11HbHl#beBkXEx5S4861tP8+6O}Mct}Pw-LC38bqA~Jpy+jU}p?=A;8*w7ewEF z1wm^!T*n34z^i?+b{6)>RG42_#EhgALrf4z%?;R-BlJArnUl#a1VN%wA6f6dbIKHf1M@7Mj(@qUeaO)zT7{e<$6jwyzwKN}05u#@gc z>5c;T<`JI8qHIV_7jHpoIKqd5i=%E}txfB0+z)}hQ|T^A$LpN*FW{vy8l`upAAswx z(yznJ@OV0|9B~dt<91+I0rv(?(QMFQ_^@6Ja3jQx;cMa|l$7j0TPWh}Xxs~JFa~?p zh%ui&ym9V=CqN;kj8~rfSYKpof_nIWyrr*$Adi5YH9km>!0B zKJhfuc!=i{)0k#Jyb$pc(;|o$BIYu!2U#Jpi0L4tQb;UiIt!^35+5>MfsmnMBU2%F zHqdsaGVWaX_60nQm-ejTE&xhoN_O9c=TNhl+Pceu&NB6Mb71Z88`BUs7f8F8Qkej< zaN%Qm5oF6mw+rnlwjFrkVS|DrUM|06jhkcf-F+hVY&*kqM{*Fh%XnY1yf0& zVxlWkHD3;#jp@VG7-UgmFjGg6MT_A~13?xo#xgw)vKW!YGy`NYVk*-@kj07@nbv?T zR=m#iDaeY8g-i!PR$MG)Is>v2Vhz&`kd+V{nIb}R#699urV1gsK>18PJmmLP^Qy#}(f zVhqztkd+nBF>MD~IWe2*7|7r$Gt=)ND=*G78KJpAH4>;qk)dCT3ZfoU1xbyW8inov zSzD&Iq4_{PnRGetvsWSPndK4rO{hEEg!6NNf-L5#F?zz_KqO zWG%6d>ASECKwFqDNZEEKvv4lRz5t4Y9I6%mL)3=nGDOXR?h{%CqKv|tQb&|v$^@z_ z?qhnja3Lj8v}IZbR8Ks_bg*!gQeRADx&YKbyvkIuNNFWWtYWGUbidfi)U!w>rJ*>< zGz_SbxWV*tk^7WnQ4C(}L9O2fYAmWTeNm*5(nPdmIt}!I=*Ltjyp_^ajAM!eY9?M{ z>JZ*hX)cyB^#y7nK4lsU)KYxQ^bAldaamH9Js+sGC|n73$+DLMwGkC0`RyH$LyBm^ zbQmaA^kDiEsI3^qR3xI6@}QW(lnB&LEM`gtYA-f34U6cdbPxxbo&f47E&<_*w|Nl* zlujb88qwN_45f=0;?Yo`GI-b!Lhg?krF0dQna%-q6LXo=$N@?Z(Gp%n$B?m+k-K;z*>}EudK)i`!LrbD8i1)NOBFP{9W(?B)HYnRB7RThmYf1R8EJzz;3agVu zW%v*qT6a%OF*REpX^WJnoQ8OQk%mV=LC=dZ_o#kxiRrJH<#1jovmMC_$1WGo3KL&z zhL9y=GnA>K1XC4BnM^6M<P#^a58y+{_hOUPmqc}ZwG8TSE#&a3sFOyr{MZ!rRguZ`ZEOeib#ba2%Cg1T*kbA% z;wPr-KyQgFlCngp;=R;`BDyDrgt4diK=o~r$<(-b22eJU=vX{MSuD;o^^ug^OU4rq zNt!MxODvGI6kcaWtxJ@6R$VMA_a$mj;yIwSbfj$2xkNE_i5M)&FVaioiuc4uNm=6I z5*f<-VkgrRC4Lm|i<2DkSqS;QNE%3K&V-Ol#Rf^)VhNQ2vWAFeNQoUtyn6_FPcNhkHkcL-2-H8L6$4lNb-vTAlrZfn4M$WJ)&-8 z%2VdXeWz~rXj9zJ>Q;|-#HrfnOcZa2Cz~7RXuCYx6c?fG@d)GXzbW2Vo@{R1y&Ap` zg1Y1>o8qczhdsi0CzvR|?>rgCJLwU|JL?g~``1nJe)eQ@;~HxhJ=zr4R{PDP9dZ4% zYd6Ka?#VFTpB`a6I5Gl1)EeW#;!YCj34eRUvbk}CHT+mZpnQ*Mp&ns8JbhG953r%e zA?L)}S{0AVmOd6lbwO6mll3iqDu{+dDm9cTk7AB5i3O#9(rPMuJ^D?ntu%%A z7*MvZ^mXk%Wv?XEIzy?e9K4B+d!*}im84-9?}W0iw5um7O(proxzeFPDMa>#(h)%E z9>wbQlqnqYkJ6>}`pN|+rA%qPfiieFrs)?UKuO9pBB{&$%G(|_)9+W-d(=j6q#8?aDvZE1^BQ+6(^Y>!=`P7{KU5}1G*c!rjh6Jiq}gIpnSMH){u+t# zX4@~88KgH?q9x^t`DGr}TPUrW-YzpzZ>e-(T3P0hXsPs-W7UvH8BJMEw#4Km)QYSlIf2!`9S3) z}}kWEtJ zndX8lTdB;n2xQsHeUkEQwC6L*{Y+@jXOxy4a_PM}!ml(KkGbc8m6qs!CB>s<`ea3& zK$?ZcU(ugcT&5E7e*#4?Rfso?sY-FC+VMW1c&0}2IbxbpnJG0s7wA5w9w3{p+|M)$ zWYd-AOf%xcjTuT?rrGh)KwX*MfOyX-V~Aw^J*T|kQ8WEHWt~T*j2D#s9+fj@Dd#<^ zZoHx-Jt4~nsWdX?Dld4{&RD3#K8Z4F^NW=7lBoU`DJLb-Sg=@*m|#I;b$l0Ni8ACV zjF+dZi|=Q=@5#2t4>gu6i==F3^omiR)?K4)lH|85B`h`8DBGE8Cma%Ml#q#-3ij}o zARFRQu92f`l|-7YRd$jLdgB&jt+Jo#frRB^tx`Tqz|Rk>>`#q#N;VOsdBFHc*~YRd z3Evv)m4lLIizx}`jV(&j6pHs^!VM!&sq?HPdr87beW#Mlv|7>}rp=PBFddN8ZYst5 zE+GfruUSeYE=YpyJ^0ZGwo{4r$ToK>*^;PyyOil9!#jexOPMg8(lpCP0AKvx!=l?Ps1)_5;O1pWaf|Z+@@*#Pki&NyU7H zLS8QWmU&8v_UL`{j8c*5PmukfH1+6x^G9V7Q=xKinLjC;m`ayhAbwW1d$h*9pk&UW z9IAut7v-;4CCR?>tFl58wZUH%lt61ObfNSHvw)JzjnpsQBrBkH<+hu@1!P7@nR)u< zAZn&x@u*w5J?2%9`j^X4t||6wnBQp!^Sh>OXTmhEDncse*T#ootiD#NtE*Gc@ z)2ronh`*Hkm=>4I2WluOORO%p!4zr>Nv9nwgQB*lkh1KGI^3iErlPKuMD0vfHloa{J-%rlGzQ*Uo>^~3{%VUmw_%YbtwO*X{cA2dY0EMcy(hgm1t0T z{96f!X+-%jpm3%MAhXn1rYRt^)O(p`fy`DbG0g{=t=4A3vDHzNm~d=$)MiY$6YQ#~ zOt=&5s$H0tl#jH0YHy~G%EtmdL?o>gqK@+DvKgZ4^JE!7R@#DpnUO>`3st8`qERta z?KvMq(x_Niwcn8OM1J{lR=67J5xkYHZjnT7FiO2nGMRgns=q1Y0d=&Z)O1OdU$i=e zWKc)lt!Q-=5zG$z0?lC=_V5_B@LK|Yet~^DR!wJG9Y4q_p<_9fnJQFR zVO3Y#G1aM%1JsQPYqN&hj|pqDhB`zNS+}OTR}xvbwtCN^0{KRxG$N$u8<3KL=u&#l{#cG>Phx&qwbMJB}!G5 zcLHT7VW+A^JW8$H;^r*7kNljaVX_6&6tLc(R z7oc}hM!IxW>%E8Mx93)T&+4W&W?EcvAy5hvjzrzn>r6NjbypX^Pw}uV^-w=x!nV{y zUCXqhVvgvkZf4qCF&Ah%Q$EOgse74@fvlH$i0J~zdaEax%u2aH=a@=V$`O6kUzjR@ ztdDw)sSe2cs@hW0>;aJVRYRECRT`=HQzM!BRN7M*7al@ft6necuzP+iP~^Up!*2TVBs z9K<=`EU%&ckVl_YYHdHFuBDLTt4huEVQQX7^YjtwUrc!a8>z-GLrwkSTPW>FwKdbZ zN?q;8)O4m_fkvrEn66jqYmZi=KA?DR90 zMJso;pH!1q5|spcN}VprFUnVb%AV+zVQakK&Qj&yOvxEpf|;$hTZQp(we-5}SC_6v z^4o7$e$Sqwu937=MO#l%w=rQoJga_2Az>^yDyFK3Jz8N;Q!jdyYd@#@)?k{{AD&ku zB;jcKvHiSSToUz;=hgO-XzZD(jwKRU4=<^Un6MsRQP+KlY32zm(d+7vwMaO}+-J{M zhp!9JA^Q!r(nm;?`AouafD&_MJeWhDw%<~VtVfz;x2?KEEKo}^ z^{bi>RF!EI$QG(eOp`&jP)(6Ux-3%nZjfn$X4mYuy?QuT+Hl@cCvKwr-mV(zEKw_O zMj}hRr*8NdiN=Ygfp~=~CB!-(sDErlncrSjHAj4)>f4AmND5=xDJhoefTRSb6OyVk z{Vb`Aq&!Dd+W~3zX0og00}W*=46^0w7^Y~DEm!@L{B|73R;bT0RR!4!brn+*$X2Sk zOszq-QvH@G4P>j-(@X(ob>nuF{k^)aS2kbR_1V9EeluA0p>7G$~V45lg74~g~a%SblMOdmj+8`V#lav{x)YCcn5^&Mi9dVuN6>iIy&n2v*N zvwDWUSZN})B;lS$nv2z>N_8+4pU5xWT0>+oCSTN#xmh7=o7UD zQ+y4dwN$h1SdkTEY}pGe_)DS1{EF z*$(w1CcHE6R6l0IJM&I;2UDk-Eu3BIKBfURQ-F?0%2P(x?BMKHAO8%ko2O*e?CI=L z$1zO<+N(~Kv{iehWiq&VKdcc8s@GTURqhJ*fW4w4-LG^NkwvImwRH ze9}3h#xea^bB8#pPLwoBT&_9EIjXkYg&`*iwbqNyNp;O`B44dHoFCM}dx%!YuXKJ@ z%kL#BT5F?oSxxx@31;H8p0cm1vpm}FTvNA5%2R6B+U;Cd_kT$t8`e7L{H4nObA$X+ zYWd;+WK#~HY?er?^}S4_YmL+$ZLXwj@eIfuZLg$BVp^>Z zj!z3YgdzQ6R;}$$A?;m{esDsyy-f3KU2uwMgTJAW%OPaA_PwOpVpFZ(oCr-hjI!Ax z4|Ivt;v~_`v6u$8J^0bSMlmhTqbRqS_KqaF=R|4CNe1_vl5Ui?mdM5y5~Xcs!WI&x zZD+z&WVE)I30INP+94)fMaF0+m~a&tqn%?KTGwaAYQHdzuNww*jS1(G#Wn3)skM#s z$l_XvB)aF6&|Z*4_neZNehfoSg1c|L8>huF<(IGS#%nK1B1@FjK9S_NaV#jS?O?*O zpse;K)4IC#-E!Jtrmc08fle~z*KOgJ*UmG2S2q>tH>Tg~!r2k+2GjMraCSs9k5d`c zL^wO5g)-TRaCStCVhROWC9M=wQIJ*A$}^P!S!Jy{)4d?8tkq+hRUXccXpNcXmxr?> zT5G0?5U;A%f$2VoS5@o5lnk_*YT5y&wuznG>e`P?-4omB z)ivjYfS=zU1hN`hNg}!Cs-e~MsGLzlYw1ySqqa8RqeezOZHq^L>d9KU?=Vejk&U%# zl5iGuMQ^MnN}^WKSi3HXTEPR_-0!6>(2u&o$CPs<%?9e{HrKYXY)s-%x3$*#B!wK4 z*v(DVIxE|{TDByzL}zU}$-olt*`2jnlE@OBwJVZn zHriEdcbdw#I)0AZP0KtJpr!5rE$$pq`N~_|hqZB%sCNv}UL=`)xL%|+M0<_tT)kMJ zg-m~d>{0D~CZ~QZ&>E(g`WLOC+6JbI^)CZ`%G3a4!?fK@sURDs9boEH|Driu`<7{V z{mVdSiKLZAXzhLs*ledeLR%w=>@rf@Kr*n)K6j+HO%mBHHU`Wb+npRyBw$H-8X<8x??EVx3 zYUj~CzUkU14vBleGqfX2xHI&e*6CMFlb#JeuVoTJ*>(K|ZNEp3Z>CoLk_-v5n!Xpc z>5@p7m$X?V16}I*Uee|=wM}RQbljt6zFAuQZH(OgkBzHhw(cWXi zvEUVLHB20_UEOt?Gps&<44cSl~;PBG!G z%4^zBOt`D^n)W+W=LX%a*R?;H1~upiWc@D7U_ajA5#L-bjOpnHBY>irW;F0w^Rzgo zHyVTiRbW~PviVvKrcEH5uhnPT*&s)}p*3MT+8`IG4b#scdsFMkq$lM9^(2z5^(`&i zqfFmh+NjHc8hgsOP|LW2L^ZZZ8zadtVv_v6MOwBbYP*ZHlYd}H8r>Fad#_4TR>x2G zEzy#$A^Anwq?df}YJIO0-Ip}i_rA85=>ec++VC3`FEweAZ@D%>l3#R6@>wgjgg+@> zdeSoAO6@+T?P05ZtF>g0J_1_f(H0;4{oP*_Z+qAd-#RTs(j+l1=}X^wt-7QvF)it^ zZ=<%3L%y7J%J;E0Q7916FJ4aimv5Uk-J{=qd0MuDvPt5tr0YPnbtLMIpKI$S`RzyV zuNv~Xwv}nz{k4I1F-^YTXYJ7TGtId_4CpA+yZ6HkS3AYD?S7cyYX4$72C`k+?@X6L zwoChq33se_YZknlj`uj+vEHo}CXzLsuO)hv>C4wq4KtLz+B#3RKOr0BDG);XD)4)cTfmSi$IB4m) zOgIi&`c_F<_UK0N>_y+nl-&rPz3BUyUILk;A7NSmGDklp$#1W0v_rW1PfVK{P6u1ys?kHoqW+Osh4M3ojg)6 zsrO{cOWp&rflP;z3l%D*k77CtAxr5?n65*}Qu<0sv+cr-BMQaopEKRtI0on|rn-$w z0eveEG|}BYPCqGS)RyA((avr6Sj#mdLk3n`@MQH6W05^dMim(@9}zjCam{(y*m@udxGAd3F|#Ue}w6S z=02;e{utBx=3zh+B>C;_P@-~rHq$;RQ8|4P(~;)W3zgTGGM#JwBG5jjE6qph74&bI z!dmPxE9l=dm2ELnuc-gX)Cgo1^-GedEmhJlGhth*tY4*&&>LSZR7I~`Qnmu1cM8?g zcYCz5P+k43M{5h!(;tz?%&4c<*B_IFJ#|x|`uYS()cfn} z&y~m8vc#(`_7`feXI4SkOtGxR;X)mCA&;)j6q{OnSE!SoD2e(?7kv#A?hAL<&(x6d z!&}))U%b(>aUMy8q_KbD4S_`tE+_u z=+l|9TZzyC`pZnXT1waFG2v<{T|dBty>Y#jpP8_C4AQSK;eGlc zUDPJ6@jm^K?l8@PG7Q#>Fue<97_7%K<+i$LKCG8v%5QZUs1nn6P`(Vk7Sr!gz6?Ey z$!+bk9?_dJm1-RZlu9J$1w-_~9$BG7^tm2IgpSm=Ny2A!5uxMtx%Ww}AzqG^tmg-{+O5lnKC4%#FVhqaTGtMpp{Fpd zj&Bh9oSx~?gQ2ta618i+*Lp`^%GAB}xX>l~d5@kBeNRtoL?H*X zo))@Hzv9vC&=vZeWRi^l*&2PFN3%oM>gkP1_5{e*>${o|t&3k2x=Fv{(b~{Fedz-z z%Mx>1?+)FgCpRTp)cU*71NuQpe(^!;OQA>f3(ZhQiLrJ6vjOL2mOdg&Gd76;TD)X zuH*9c^Lj0&)#dI9`$bP8g6CD`!+zD9N}^HmSG@%jj*6G`t`rj5#8L5^{)k6a!+zJF z@@R#9O@G;=q_98r<&vljf9Y#U24#34>@R(bBx*f>=_OjCo-`^dMrR^9Dr&|=CcL9t zM$=X@qyKX{(C6#F54YCfp@1X3R~&RJN+POB`(sP9?%!;uvFY zTO!UKvj*%o|pr*-D*bRUIBu$97eoH`|Rq*FOsMg)G*+a$XHsa zv95XzV^kj`8Z~Me!~4oq><%d}huvpnGWAT^A?`CKG7U<}2YP{NcuJ0_W6WWCG9?%2 zEvD%yKC7xa7Q=M$YsJE-9%$6)8drZ!|EA3nO3K~1@tA;mXvpZ4m0Ja zlyvJE-!tK^bUouoCft>-XIx@B0(#art~31xde%3LepFg)kqwM+rqI-7VGWGRObr^W z2}?3+GexE50^QG4I@M?0Z!}}7k{Sloj!3=@(a>1KgijojjgbDBA3mE{=`=MaO7e?( zsUL^6G(Z5qJflr&7c+%vtJOR8Df>ZB_GoHXyPdI(QyG`KJFLCYa{#6JTT<|Y}3nXpApGEOq#93|U0&xCW7Y~wc~;Xb(6dd3jLWqx+# zgSopKsMFr z%k(VBrWy}3%>~&sV+7L&Ae&~4W5VYv(~YN@@cGJgW4a_73uYKiM_?IfEO_2HFA1Na z^e;Tqm@txR8lR!eF=k65%fDvqmxPwzXTN3~_2`=Yx^YGl-2>(s7l`CNV7_sQWjG4Y zH!6?99JZ=Bw!US2B8fCxWE_-4Bj#e`=OEb<;}1!g`(uUQGqlmN9)OODrACNH8Okyv z(xZH_%(%y+bA>-JdU$lP@N#3QM^_53GO|5Vi>xzV^C+aqCS#RHQAIvAJ|?n{w%Y-H z`E#bT?ec;4G5y*Op5_|Yn6&mKi+pC-W2Dx0;r8bWe`XY8x~KgPvE8_rsZ#rVpc+i| zK=!%Okf{~OJ~vV%%@p0*_tSS6k4Ty=2Dkss*=fwBkYY^x_#(TEjZ9CrzgT#WvDcGT zEwaxz?a8hbK4@HL@#8X=FPp0mYE?Hd;P)=2iKRgqIhrbiu%{Ag@oTGGB%k&DJ9 zj~W)aY?RN$RA!1T?RypZ)A*U`K>J}u4AUMvqD*T#=7@Cj9;Qz^<^q*v+6%IQW=kR%Uow<|=2IS33m<4M^$2F4X6!SV z1Ko2THp@uDm0P&+uvuABo^rHfo$!av!hQ@%W8Dz*1xYjt4>kXiglF2ChYvTc$rzH> z2*b^ACR`(oFl$J{aqy@ZX*TmHCHyh7t4AyB$IVAQN(&!nPL@P^jyInt8R*$3e7yOJ zB+_iWxmyyA!cUl2h~ygKDYMoT)QomACYdcI!8b=b4Gf=bPVi`G_*C=wR0@f^950#K z(~;<|J=>h+(PeYCdB&q>!e23q%)pSO=NvOe656vl{O?t~B+_$^IY|;(@HKM{g|u-m zVXnD>3HK7_nx8V^Ucx+cHxuq9%rg%XLHVYK&o@^*C-nq+BYcS|o+m2NX+`*QGmR;s zQ(pL5bDgAZN-pS_^XMr2M_4zHE`{$f`%9wI?ldz< z2Bp0gzSA5jiAuZEoFa*8Ww-eTk-&N69&?|htt!s)_L;|-a8~)HneZa&k|%Ihx!ewBdWQv!vzi zZk@ z(rE=x(4IBZBxQ+_kl$JJAj@!v;H-I;r-JIi5 z<%mDb^j9grkTSI+u9<1CAyNJPX>O22y-HZl>oTNl$*L9UQEm9Q?%|TiN}82PGO$wB z5Y3t>iPF@pS&}GC!&*ck;aOJe5X;)m@$l)mZB?I3n&H!N$67*Uuj7FE8BDlNO|WJ&;kvl2HJ=Gr zA!V(1m~aMP&RWidGx&1WI;LHn(j&@STbRD?lmYZPk*uQ%)-*MDtjgCx+JQxDpucxG8HI$W<)jXibpdd>RB(mjWX(~jjRol z{5FoXjjU}u_P*AN9%KvLHTw^bhP$MqVjdL?t2Gwpt|jB4JVRy+trF(B4x5}yIU=p zux@)>>m^ZX2Uv$C;kZ=F7+{_CC_{P3y5P|}5f53)yMYpY81bkT=}}(9DC-_cr1fYk zfn=cdml30_Dw0U+(bfZ!NbAR~o#?%93+1BGs4}&b*n#lBc=kFt)v8FIh?EC}JOr}Z+7b5)Dt4yJ( zzXL5`dbaZpG1+>L>5b0$K)FmGc7|25^$F9S&ag_hc1fB9W$3CuYYks2%O~G>pJrW` zCGl718xcUuW%=NYrw{&5vlCNj7c=sCYqcb*zvrzVNCr7P2O-@R zG9FNF*leqkB&wBHtXd?4T3HkJiq(L~9@iyDykb4TG^I-}Pzuv*kj=3=F}(}2IaV)8 zRGY6_FH54@eBH|DkSi;djGSj3VA@bA0q7XhDM)j^b%yB*q&eTZKm=M>iG0JVyfRRR zgvbTfAdfPXh1MvK8bmI%R(X^X`Hpo`5|ws|b)IBU+K!P+tV@ze>m^p|D$Ieje$Sdh zB-`LpD`hpxCJA5HZjsBa5^H2g*^gFO6C`1a+-I+`p7H3K{h{?dhr}J^wbmRWxqGnA zn#(fWJy>ToU5jawF6*uCl1P`0)(A;{yLQ(f#b#?PQ^T&0MsBvAW@_0rM{KdCGIj2n z3-l5ZXqKUTY;E!AvB;0DA3Pc#xz#Ga4s*vcOpn}dwe)CqNo2u%YXr%_g7YHt ztqGDy>wGI;5^25HvOgj#;eBPFHJAzSD_>cQm~iYlWJTp-NNT&^TBU&EU_W_$*JY94 zT8T{010A#4%aDG%W?HCu-0H@ZoMr>{XKI^fN1U)Sn9|a|iacSBWEzlm7-%BXqiNT| zPgu_}Wu}GdC#=~_{nc;1Za)EO z>t+4f10ddMD}rfww@ZQ$o6`ZzGJh}?9!W(2fkS!PItfP{srhl|fkqm13UGbxJ zK@!#Uk5=|3j7K&7la;wyrXuhx&d=5wkEZ*6w%*)=GI)+xYp(Bt)$$XfC%b7yf3aps z!gCkXeV42wpP~$Rm*@I^vo_>W$Z1fb%htBfko@BHZoZNxhkI0~sA13Xs7X=V-r`ZmqJ`|UOzXS#FIvPdyqi*4 z-)(r&qBi_>h}30Uw+Tg~ZTLL1q&?lH6fJH~U^?AxX3=}>B_6$1w2Zxr>6dQHiYC|> zJo>0;c{@6vQu(9XXGJU9$sT=Ow1%C|WOV<&Xl;ACM?V)$wAV2e>Hb&IhW2%)lHJ{6 z$@Z>2lxE59*L_Xw7xog(uN7IWnJvBuP|0E~?Y$mVE|zLf*%y%2E7s9o`Xy1d?xpoK zJ7hmmB2YJb2vd{pt%`NGYky6$PTji{>uq=QXkfAa_7IOo6dP!#AEA%~yFXRzVSCq6 zqRj3yiw(2EnBI4kMl#_Z{BkkMqfK!~i;eaOzfY8DZ}=8dp)q8veO?ldXU+7n_H~ak zl<{`^V=_OGohvrp9wdp{`~-U_$)L@@6E?vfLu6m-o+BpMPcmsea)BmGqW0Mws3csEDN)bZ#fa?6J$%+Pc1fnB z9$`S`m~h4k-~M948K>V)WWsY@lkH?CJi{~DZpnmacqZHJnNoUOG^g0zn0ofO4D=Wi z&Uc@+$1~x4_gOnz5}o0hYENgvGdxr6mpSB+9y`P|dmht-9{E5UCDHEBbbAXE?*7cM zw@D&=lyesrmvt@f^x%J-yI=P?Fy+)iWe&q1~0KMo(8?Xs0tJ_Z;L~ zXpdxiu;(GM$R5Ykr{__iNle2)_O?BZ=_!!CZO>wQ9%PH{xlC_>Y_a_|({hl#V=rU+ z7-a9*IYe?SUt(YJC_HM3{pycc25L+1+HXmcYx}5o?WK~k#Fsrw0ZllMA+yBMo@Jw! z+9xH^{N)4tCy%b#%k9gO@}NZ;&PrSSgz@s^{AHD`NusCKt8DmG2FSLm__TVpU6~1= zR!dbQp3X9|hWNpkF2lBnK4vfqBDL zg*MsWFomYpjM`*>&xEIFHrqck;VGKU_9Z5K*1E;M&V*~&Ew*t1HKR4&$95qmZ1W%6 z#h9?iePWkn!XEdDU5=@Guk9gQ?P^Sodew{CY9|uO8rx>K^e8!Mo4wAXjEFq@fs2?s z)!%1!8%bDy=@FmVog|T!KC^58f+1-Q`?7QqN;{36GwMI%a1|BF&E56G;Y|eJPIHQ;1;C zV|vta`)#InmMD;KC7 z6P~pA!LG)HCoO)k6PfU&#W_2f2~S#_vs*F!*sElbAMN%`mwKHm{G;8S=|-;|;=J9T z$?csF^axW_?;YYN`!S|?ko{y&Ad*)4mz^z%#*ly6dztWyC_mfBDWvG!dvny!_UAXq z@*!nDkGg1oEs5;;i+zk`=SuI5`o%s&By;%1w*L&&$^nqo_NbKctKE|bEdO29WqU4D zyK+B7U9nGiAv2V#w)$5fl`BzK?W&R}_iJ_?l0j|$6?M%{mP94GW-nvH5?!}*Ib=u~ zBl^0%oeB5(ZrBHyR`(8x{?k511ZD8U--lh4L~U1dqJ@fWSAMz7aFU7S_p>agB@=!> z%W~RF!f`1>v7K~}BBO0*yhry$`<&-Jx@;A47D=M=g*r<~2IaeGg*s~_kuISQ{A0YQ zOJPS-ko@-Ny?s`=<6}D9I}9k22<|`$(GkuRBB@!FGsmN&BFe%4CWll&Rx>)~O5(@{oxR&`#GM0!?t z-jU?DYxNna*L0RMHR-d*tm&*{YTpOGrtEByL@le9BMeOQw1d0SwVftR*v{%W-6Z+# zzK~`eC!J*SY+4;>u1C%Ey3Temd5p>I>$93Tvq%!vSX0M#QBS&CG*jpG zgs1I#IG-`$X}ey|1&)WiF}=>}ltlJScdkmJ9x%u$5+O?jWvCi9*h%nc zh5e{g-J@~Q!=2WWsJ)DEI*<(7OIGv>j zN^^{JmSm9TrN}YP1tJ@t3XO5Dvn;LeJ?a=ICX(tQt?zTunNB>@i=AGM9_#dF>ect< z=yA>rBK!9~Ta59}J50m-z7sv(SM&gV?{Re~p+FPQMF z1W!0;iJ*LIqMvlGOQP@oWI5f72CTb2`f2A;k3Nm|JJ}v(DAS#HJ=z~K-Pt3F>@vgo znq**?{E!*WaY4Rd1nQY-1VF3Bu7b^+~=F+EMdYut2xe=Xp~XUndkf> z$#3uOnh5&XsCJ+kn1w zB5M*I?AJ2pYiDpRB&wC8&SFWlemLrUz=Z3Eqt05UsQx+PTW2#T!8QAl&m~+lxNo3D+&YP0Rp68t-M6%WW z%V}D-pbQtCE|O@xzUZWTbW~h)HhMHR<`*YV66OA@lTR|p{cz;3&H+i3`>)P&)p>npDaJ67jiFnad(Npzm5wA=W8Oq0$Nm2q21@{3Ys;Xg>+{vOSZ zDdSFH>e7E<%)Rb9rXE1??j@$aKnZU3hNS1f{u5)$x|vKFK;_)+OvC#3bjrKi8&Sv! z{ZrHm?x-e2&x5RzJL&&R2HOCyNMb>faN}`sP=#G{|bJ|4rNsq4C z_1q~G5Arjd2JTEEIZH@#XEEWrG0C0FgzLs6cO?_98}D~NV#0Of{cb*yz}ZDZ_c{~K zE|Oinh165dE*iT8=R**E&5 zELl>XoQbz^*R(@+c?>=^L zJ5WfdP573cJIJF1yOTTKqt9cyxGzhhy6x)DBN^CwZ%kMBZAqj{SNE_a(xscLwIM6v zeq9f@G869C^>NcvNQV1$1Kg;#NL06j+&GV}*$=rDCD9Do&vf$b@zKh}(k+>-G_MIFYpO5OJ?7StMCBXp zrb?nQ`Ej>15qtqRCj4=C0Mk>Q_L!OOB##F9#=5hZ@N1dlT&-iE?C_r%ZmdV8j0tWP zkB-Dlbeni|D&`rtt0dCQ?>;GsG@Ifsp^$dd0dU6JeW;Vn0nQyR(WknZ9xcAG@f5~PH=7AhVa#-AFs(A_^(n z*~{)$k1oZ`b`N=^$If*xcoY%)mis`LfL%(*E_OS6R5kWJccLU}k?*@xNCqwPzS#HO z7bKBg-ghrbBD*YeD|W>+=?&lIZcip`k*nRKO!$?*weEE$Y@h4gvfaoo_!XJ;ZdE3n z#jbbjFySnAy_?E}`-&Uf&P=$kxWVnsgnNe@-9b#acev3V#)PxHP444NILq7QPGnjQ zwYk}y!n9=o>|?q!nQruI9J|GRmC5bh0%!pfeoy>k_dO>3p7_V^Y9{T{3h2{cNY_WlWVK{71M6eWt)4H=@jU)%{@yb?+>542fGLAuWjt7?vp)`sK)YK zza*Sv_VwktFG!*q%X1g?lyxM}L2P%2_mZi|InfTcU0D2AloangO7 z346yW_Zdkzh8z{A-Pb%?8hgh5z@wD#^X?XpR@fKZy&ioO`>XpekG92Lb}vgJTVHYi zA{p3vXY3W%8j8A*t*^NENTS|x)vYZF*IXIOb@zlM8r`nDKYMgI_PQHBEMURQ=3j1- zM`vS&Z-qy{#%jLJ9^HsFe0x1|i`%~R;ek}5i--6|dQ_@-A>Z>Jl`9_Rd()$8#S8lq zMg-y|77zE;_NYnmNT1)M)Z$UTk3H&AJjS=jqh7^hefG#eDg%p`@b&a4)+*(j{TRx~ zf^ok2lCZT#T5-N5lCs1f>7|Ot`AUq!kYvv?zQ{31w8s+ftN*ynLB5ew*4Kndo`v(Z zVG131(Jbfd$W(RUWuTr+Z3m7lUf!3^ls2#n(4$P15;BWd@Qr2)O??vRNhTb7D*C1~ z;n-8r_dXMz52@r^&4lMeD)}}r;kUyo`?fLRx5Fy?b~E9(!>ahcV#05SRq=hx)PG=( zsOmE_skGw;!WX)Hp-htp!WX)HQB1QyR?Sz6=`D~|^Oa}%0A$sD)tNSfth%or5&Zkl zGsSE8ntC*&cun8*v6$Z^@y)#Ny6SyIij(zl}Dc!f57*I zBx*fPeb0~#TF>6%O?{EmArWX3R;h(8*iA$;7n&zYoj5`HfO}q@QF6M)4h-Kiy{~d#St5gw`BgIBq;OZg;TtjpL*A)H1g@1wuR~19$HO0kP!QpRCKRB=8 zYwFj*KEc=PhGAah2KzT3j{XZq{Jj*xVaNhhYin-_c{}?=j>K{m2RER98MxMD3)DsB zJNO&oK9HIMQ&2=h>E4=JGw|0$TPY=N3w(5$g7I%sKx=CPW9c`g^LPKd)93H80;T@D z|J|jA*49KX1XL)YPtp%3L=OkR^) zBIvr=AWXjnxG+{oe~MKWu2oS<`UGE7|D7lO7f5%pbeDmviVx*=aGC|zn%MX^sV27m zqmO=@kh=I(hQE6~Nn6tMCm9#DRRvua=&Xs~r1XZA{w1$zbPe`_KSPDMCb*jLfvbx! zaH0Q#Yl>KK74h%GSX>IS^W9kc*11qgnHQCXu5S$!>{Cz+3j7~EmI7;B6XhWtT~v~( z1^ZY1M;~1zg1;de-6AbW#}F9Tlp!q+dFL|XSgp#tttK9pDP#Qxd{C;hKlU9}pbv&r z!GAQkhQR-<3*4`%)+k2|PyPYvS{V|@D-~u7FxwEH$aHqdbiN2m2gBfu0&~GIhQKhU zpfHre*AQM4$HCR*48#yW$P_4dYVG7wyG7kp?vL0jRnh)&)UX%0su%*UCheez37$_t z^RBSA)Wp-UTGGWda1AjFTvN=GDcov9vejZK4Q>e^$do^n{&c-A==$ILD}rKe_F@G_ z7#e4&T$JNJ8K$6J-9BWnhLjH35PPD^qeO6+ySY9kbH8)jBP&s>E4bE#m5KF2ajEXe z4X(An+gumPG8WD2Ff~oKRcyUjUsSHa5JnY`f~$#4aCL!t8X^m>O)&*rOUwjU5fpNs zye3U3wrSUCz^td6dO82SYxCQo91zK2>tt-%ygY6l79bDeK zvBRyp;aVV&3tf{NTt`Pi9}1(2GxD0;U*TF6G;RlL6?`3>&i}iv$yQ_yT%{`VP7xT@ zvHfTwd@TA0*8=uLT@;u8pTM;$$_8Cm^saA?g&}oO7o>)`A6!#31=r%cXK?xy62ln( zAUzBB?%+Bi9bESx;*#dWK#FC=_J2R@s%avNeb9fZcU^Ed|0fFs zuR1k>|G=tx-h|dBA1oKmg@RpGY?uCb9_y$D2Di`P zYqIkfkd7v;r^~ts)Hsc?|IgcSL3uSf@uoiCa zBRF%@MQM;4q9V8^w_(y8{R6YmTlEa~x%1uXKhqZ5hEM7oB5H#sg+x7YLwQ{moaXIH zL0ZvW>2@o=cD>OiWF#=mGpMkv|}~^ zQdYU{hR9s7)j=NzR}<5~M;HI>AIOWOlvhEE)kT}}s84Wee^2M;zD6zo&i1)`Yv3MH z@H|=Nop7p6>U{+k3zUUyLjE-WTno>sa5aImjv^BZ=AX15LN1lP#DoI-mxF6f;J-fy zQm!q1@L!&9_0dH;&p#l&Qwo9b!L=WJO*6wDGROYl(%e`P56fKcY~fp_WUB&;1y+>* z*$n5_bg+Hu;>G_+w_CLh_6gQJ>_D{&4 z=7e`YUfs?pJxxmRY&y)sLDBFhpy zyTdTSb2*d41X~8f>#{a)P7SSUvwdh*fNtQPLM@6`vg8)*Uf#`4X5c;*JYEH_K!fLp z`(;UKrbpxa|IPI&tu(Q`H?KW|M~pkKvuNa@7D8?H)|CjZN;J857qH$@S(_9$c&&Fv z`Ul_pg0F+M3cjW}z@5i|;P7{UT~N9}e?%YZgSw#A9JRMQ`=6I>^_nc*-^1U$3cz%3 z^`RM^_K!K@OvZewAlBd0*F`Ci8X{hX36|1o7uUkH+BJDa790l47`SJVHG=Ca__-cQ zaYe7mwY<(dd;jeJ@1A@W?3SqV8IX3)>`3d>veNTqWspt$Opm z0j~4|cX`V9cKwLTj%fzQ?OSaaocryrD?;6=qNc1tyjDbG>9&;a>)@*5O>i}__#b_A z@gDdaVmY{`$oX#!V~LG2wOeZ>*#4w#V>qp&h}XeIZE+s{PukuZ23z=h5FYi%z5V|Q z{r_&m|2d1H1#YgdTk{H7^?x@c)|V>lOOy4bLw#W_U|pE9E-bE#y9=qpJ(pU`-G#?? z{`p<&@ZTLWSX9K4~)KlO|IPhA@^~#n^Hczbl;uU_Cf71+FUIm)F$Ju`D!i53BriI!hwq8~I3Hob7$wHm+e1gH}9MB)`z2JZI z`G-5_b!*+x8j#uvN^hP$q7Tl6@SYxgN+Gz{1Yh4xj|`90phP zaQ_AUHM#4h$Y;>x2K=%1b@89oK8=vKYXf(`Ho2R!Yz938r*jLg=~-LwZj2&Gig!It z-tTnz?7-mXU;n$KN^tE2U(@a@j+~nKN!G|EaPOu+2JXVtA93}nifb}1j!rjAZ}p+@ z+S8bCD7dhCE8hY4Ho|I*I17sg z9Xz+ZT`s{h3Y^~t<}ktj1#|L1>4H}ZluK}U92o-7#mH`V){{oaJ<_&cf*V+W75FH8 zUI;_pynl;5BA`{E?h1S`_rU3z+gaewx&=$={MFsh(}Kr@q)FIwaaLxE#&8`N@o4sl zAp^VjXuW@0_0V`5Y-gIqp}!$lNr841JclR!3*Oz(L=TxexwqcsgUcIyO=%8>xPg;e zcpb=bl$>LX1s6`Jf(s{9!BzN77uk(cqZ}!*k2RVgX%H3#&5m8ods^qy`TmI`3ASZw-QjdJKKbI#e>hw z{a2sPP#ICTe>Vc$djGxiefG}nfuxi>ooQSrEsQ>awsEU%@u_9-Y=`FRff;-7NJ1y` z{~lhKXTXA={RJ$4yIQ-o-QI3qO=ZFG|CRMDmGSOJOUxy(-*W3%hii6SuGtN_mN(^E z{+~`Z7L1BFkFml2R3h}beVAbT1joJA|1+rHz;h)W;VqF5KDO8ot|Pty*A>UW4cw>b zB>(^6?LFYDEYH93`+3ev5HyGjP1qnHLxw=u*@+TB5|H2shvbkPa*`9yNq~R|2&hrC z;%IAK;Kor!?Z9dkM=i!ttG42vRUB>Y{Ml;ze!tg!KjS2{@B98g@8^%^dtLW+U-#bY zIUjB1EJg3JIPu}^<#2N5%t!D+>xXccm}3OTl+1Usy5G}x01lsHmh6iBzu5iwf7Lho z`wD&c%#-kS2Ggk9ls=)>`&6Eezn62yi~nT%zu%kEJ~(?^>^*Gd-Sp0Eq1u*VIqQz6 zq}OGCf}J-(-2+g^GIL$6^A9OIjrKO$5^dE>_@f2&YWl=(d_WC{B&bGfO$kM}wg1%8 z|5H!v?}c;?{QtApWr$9HsB=vB!%VeP;{Pc{{x6k}l6yOYmM# zTosEg(KeFPMMCsTY_-ZKC+~!-clO%ER8aO>UyPJd3?a!E!TaN9?^7IUN7Xd${A}|7BkiysrsvZOjxUq6_Sk-z@xIOy=dlkk_T68P&6n^ycK@Yi1rkiR@N2z0Rg<*6Z{Ltr}$ zbQtIe`RlJvRwt^Fkd1_S6zFKsF`#2W$AOLm9S=GlbOPuE&`F?^@pl^jrpjL)=qQyB zJ|Fh;LFa=m1YHQa2yt74xGjRYO8)XxHROxsufM8+tOoqq_&XONoCkUyWVP~_rwV>Yx{hs{gsp~+mgY0_H>p^b>y%F?g_;oYf-VF0D z`O8zcKz=LuTS0Hb-yQh76XrWX?*hFG^d8WAK<|~m{_2PD>kW%Dao*@Arq`vV_QgM}X@I6*L9ut2av@JzuP!6@)I zC#?p)6z%}7vbNagHEzn?V%Gz!6~8ZDt@!(vhZJqk5lN@Ly79+qC-fgt*9qLRq$s25)y6O?N z&z`YrQr>=5xT-pDAJdq(Tb%)&YPEb-YhIgbS+y0&@BZ|bw9hm-@bap!!e85eU-gH) zJ?e<2f5PqG@LP?0B-Po0&LneQiogEk9 zfR~~3;DAoMIRB9WeFOBfZ$O8I-#j~UKg#h5=xl5HuKl&TqUojZ*Xjp?zb<_#d=9=3 z`|N-Tfjdw`I_<~$=h*M7{Y`J6tY2(;MD-0Yww-qFuoi?)KRfNp{4Wp+W_nS2UZIvq)vXNP8mLV;8!Y~NCm%E{eU~w4TDZbx>AWp)Ys~Q1Y>oD z;7y`)H*miy&cE{n%ldg@!@%9vH6xFR?6yXuq*z9qA-QSfQIMR5QaAujrv0iR7Y(#5 z#{Yo&6~3{5K>ZDPP;#;YrSq13P<@#CIcL#0}%`KvNP<1u0vN}c5DUzM=?VEwSMbg)zpZx^~ z1CJauO0W~3tbr^0$dOgtV=E&bTGhv<9&(wp#bA{C{Q_L-g5rK{bfg<={uO zUJIPi`UAvg2IT#N(bl)s;NXVV`v>i_FKIofMg^H~=Oa&E3ZIV}^~|92Ro~SwKr?SO z+DLHh>bEh56s-OYB&V-V1@nU|h9aMX%T^y$1;O;{w+0miHwbPO+#z@^uv6W*`UAxO zp4IFh*kf#y9%GyI7&Aq(LnJ#yQh}JftL4i-%q{C2CDd4-Q7H zSI)VAN+&cQhGc6xI+fTk^^m1hXp>{o9S&4a-c z@x`=*!Io9Mg1OS(b0y~Qi_eFGeb;WE_LcCt`1L2 ziL7-U`2Dlv>`v>z+Wvipi9gwa*Vq0PJ@8wC?*WHND8nRf!z6BSaXJFL&cI_4U%%!XIcOvKWPS|PnT^Ee(v_`DEF2B>7vaWxhG2+h{N&CH$qA}v< zy^;fCBo1RF#4!@{F%t89YyP^I^R3WlgI~_ix6WGkc_iPeUH9AkeKtq=sKwruxs6|= zM9;%9g8dc74&m8r?X#bpJY;${eCQQ?S!zY!kjxDN$9HhV8Q<~O?0joo#}B4w2X=$c z4%`XckI{jn412km67!i7+nEy2Vv!V!q*zk)sHEsoNlUS$g1zOg8O5SMSXHd=70kD4 z)-#?QzxE&|1N$M|^{eocw%-BfTlcL0`HXz)!}ZSqdvACic-n?nX5=Hbt1PzV-$Alv z!{2~6ZTNOZwOV-cL({4y4y%Jd+%R)yzV*ejP%-U&j7LjjlJHKewelyzMaQ>>iPV2VwS-$rP{zULe!7jm9 z1wRn{i=ef!e;?-Ld$Vdn7j6vAu8};i30<`jZ8mhb;GD9T!klRwG5hNvk?p15?EM(8 zt9u=g7#^_R*|;k2fc5D{j4w#(Na#E{dHn4C7JsvAzcuoLgKEDu;esCnrwf({?&@DY zd$-ige5>(-b7tpTYc5zZd$r`=>d?*$)RFr!ig9#njjzh_eCzHD;?S%+{d>T> zFW4>o8;H-Y{=qqn$w|OZHx8RKPs(wgpWS!UfjNwA&fFU4 zoE0RdbBO4##MMFT%uN^>t!0Asf(gNP!Oen~3GV9OyKi>j$jwjoJ!IcL1S7tr<&a%e zbwTz%o4)~$UTgCj_{RAgB{$E_x31cJRAi@UUI%;|>lc=Lb|ARr!|c6QuPw6+_F6}8 zDJ{qj4Bf(dUb}^DD!!!%^`U*sC9vHB$zJP)Vbytit?zC59c*`Rd948Xv*lgbKD33k z<(Vz%WAd$+wj5Q+R(X8kPU|NZHWlx*o&>&caaPPYysh51{y6vw?E`eWQ3^Xz_XP7w--Iaqy?b`$H#{d|7z|7E z%CEgJ{&fQwLt@$1RTk^aF|+ns*GyV5`y-@uV84%4)z;HWK2m3Gol$a*r2QOep(-uxl!q4SDNmr2PD1Op?kLWElr3dA>n=_;eI3GCZuO$OKdCMYdu)Ap)}vRD7qcEuH&&%&ao$# z<>IT~(XxE&_gmMM4HF-N7U$SOi(|ns=@o}b4}UOp;kH0I>)x&zaS1W5zQ1i~`JoWM z8~#;@rEn<3QQ%N$Q#Ixl$lGU%4~3W_w!5+A`|Q`ZwUi%Fh4Fpm`&I6+GlHGgKel~Z z-f8vT{%_#Xf+q+L5G;@9RD6y2W8P>D+kPVW@!JQB1Tzrp%ra|vh538Loiab~ zlzF=)wwBn^hZPllu*21gbCoQ0KK^=RUXrEGv7QZNspZ!5z zmTI-;0?!xRA-D_IKC{$E;_6dz^#xpIsXtmThqBZO_PgOMwbZ6gqhOohE}Q=JxJ@Vy}ASs}{Tg~&fF{0U*kFgMH?4hl1@VPS?fGF;dvON|Ma0Z$Fj15OT~ z37jeVb49;c^vgwmp6D+W{c6!aM>LmJ z?Vv2?_As&CC3r$MH3ta}6TDaOkH9|aFWDP%`l$cO-Uj?8`*NV_^<7}F*UdouG77MF zuf4#WUXK8e5j>&SQ{Z!ZJr5kz>owr8Uhm`_sYdqtBnPF|iy@W^E);AN+$^|5@CLzM zy%_(yg#VS`r-EMy4m*PWoO%RxW(q%7aG^-<68@KhBYRVGOmAv#>&<+)q&M?nM{m}; zD|@p>UDKO2>S3|{sn|X$woi!dd*b#}!M_NqKJ+tJuv&0wANqfe@Z0*lc|w+YRQO*B zz9*P%dV;sh>qw3?_X_Y5Gv4QOsSDe`TeaKO{(|lq^B5pk$PU_XBhh`BR|{Fv^r7rH>jaP zx2iEh`x*P8>XwQM_}3MAb_glsOUihVGQMJt6bw0C^q!tbN_$eqGY(33q?D6(MRp7& zrF)S_jvBg3p_kGgX40<6nn|Q|zXz21q_ihx zxTIZ?V}^+w`FyvSe?5tmdZcuJK;-WYBc)tuWSN-h56$%dIipWXJyQ5T`4G*Kk;8>T zPD*#AbVu41xqbL&LeH8^%5X{PPqwA?NF6itfoA$c+7Uqo`MwdP z)F)-Qq)$aAiJbbQMep0mq;yA0d48=x$?noIPX;&nFBq{Yp9(nZSODTswDc$W6it+`eds6C? zc11oKDfW?uk#~!^^fXeYla%2+1v)8f{gea7OxhLcKZ=z8kCa~(fAus{x+A4O*|ts>=_~5qTvDd57bxvXsZZJ!88~GW zDD_CGN6LJu0A;*MyCUBiMM`(144?Fs2+GOGNvTgtds4@~EAsrPP9rC!9x44Jr5QtFXXj}+}<^dTcBr9LV3Nne3JX;z1q;x;Z z$VnYD-O)@v(ymC;*a~A$N`FYHM@l`?uET`r zFDdm%srM8p^YehQC+&(X8AnS0NvTgt{X?Mihm`iDU6HNhNU29kz3h;-C+&(nJ&siJ z2b6j=Q*RWgXA~9lzJ6nKVv*8^+~BmO1-!-lhU5FE3#=kDeXJO{PcKI z+LO{fsbl|UXA~9lzIome#)t&^pBK!q|_s&-XUX8+7(HjN=iLa z>XA|}JFMlT^pCVFa@VP(jy?5988c~D)mA_xOZ8pp=t3a=N4W^T>k}4v3yLk(6>$>XWiON$KuG zP{#8RDEn(t_T!||k4LooQJ_*^K)WL46KR(8g3{ljUfQ3hdTaWjp_t%{Kcw`Zl>Xm( zl-3)Xqv@@NerRZ*ua*an*0j#hr;gR;4?+Klm0sXDO@|s9AFbm{+7(g7q>P`K4Q0HS zXItuw?B@fU4SmAU_Y57>OWV&ibh)9M4SmAU_Y56$gwZ$DR_6wPHpNzZ1w$e7+XcT4 zlRq~?+#>jx;KzaovM4_`oA_lf(K&+nf?!^6@*fMf_aXn9;MYfzKX4SWIY;Dz-wF;t znvydG_w^(HvEZ@CkgpN!6wE%BlEH$D1#c95Td?nOqAz%@-~qv}1cx6_+r5H2Payx5 z;FkX6UlY9XMDouFekquD5+&COel2)xE+rcUUlyE@N6BKrz5~d|1$PR*EcmTp`h8@k8P`{3I` z@DHkOxjS-q=3bZkqufVxzsx-*Z*bm-y!^and6(qv&ihf`&+>kqcQEg>JbOUz0aFGn z9MCl2g#qsm`163^z}^Fg3>-0V`oJ>=E*{uC@H+#a82Gz^UkwZlnmy>;LFW&;WzZLc zz8+)`&Ki8=;7Nn$4USHUPf1VNHs!i0d!{r`O-;RG>UC3ZntJQhA5T3n^~I@gO$|@W znKp3R_-V7JT|VvVY1d7AWZDzc!uiMN&(1$9|APEW@^8+6F#qTI&*#6B|4IIr`Msv+ zOh0b=sOd%1E2b}2$$KRNxt^f#x!KmF6`Urzt)^lzs3n{oV%+!+-! z&YICMqj|=L8CT7?W5)e6J_`S6#v3zw%^W$ibmqL7n`hoQ^TC2#Fw`P7Y^P8E| zX3d+mc-DqlSI)YA*8Q`dnf3XsuV$S%d&=zU*&Va@&VFU~C$s-FyU(04b0*BmpEGAp z`JCl*;&YO7HqW_s&MkB9nzMJ#zB!N2d2!AMa}LexcxU1M!az}O(U78%MdOMl7fmmkTU1&!uc*4{+@i*!ONuTp zdZFm6qEzt}#SaueSN!+lP{~mx14;@@E-$&h-bI@KoYQnlH-LT$0GR2N!1)y3A;YKL`= z`i}Kob*1$^b(M9Uy4Jd0{lL0e-C*rfH(Ix-o2*;aE!J)7R_hLRhjpjA)4EITvF=q5 zTR&7!SP!TtaX;#5MEDEr`@wGs{s_2az|Vjy1lt5J7Q9yQR>21Ze=hjE;5&k!3;vH_uYvSuPVdKo zxx!BpEE22|j17DmwyTA|Q1EKOI|TO$K0WZ4(0NVp6Vd;Z;J1QVgXlv)!GVIO3}U!b z2fYX^7hEdxxX3pO-emn6wwDV3`SHI6pEG0xX6-pc#sVK1LLZ(M95$5vsY4k`zTl&R z`vrgDlRqb#Q|yhH%?=N21->whZhtHIhd?JJUkJZ+`19b;9nKgo9npkYUaeq*;CjIe zN6_t#5lPryE0{B6J@}j6jy6zLrMCh#mIFHT~*-WFUs{2$rK z$w>_5PlDeH1}9T;gy6A)LnhPJ2*J^UlLcoBmI*GHta>R`BUmSrwSt$44_6CbFStkW zCxTB-W-c9=%(T29lGnu5JA$7~4#Uqk?cPA0lV46|9{x=%oHq}%N$rJIIn;@OA1(mv4Y`P5w!}xx!@G= z-zk^?yt!a1@WnE^>KRH;|KBWQSSOWG|D&?c;P&@r^xrBcj-JX8e{?!)&ZDO@MNbJn zFZlZDbi1Iu7uIAt=lwaRQ~i!;ettUr`KRa~aRy!K_VP`j0&aEd@Yj5QtsY^AFcIiA zpE1mt&p4bopFZfi6bc^!+kJt)z&*j^fn)4Epss;KMPK{>z)Y?gr}^CKI6tv~em*Do zs^Hs#p9=n6P}g4VPh}NjQe8z?uPz*g_2qJrtP*@+CiCj|Rm49Del41TYU+Oz*aUrl zE?r;EeAd0+ZKC7!kgO*khHCq{X_3BB$K<`>Zpi&T+lfnPSNLGmC8tX=^izC5XfU81x0ly0U5U7F=1N&Kz z0ME7`2mZ}^1~@<11-yJ2eY;WcKEcNXj|jXB+vkLTW!Z1RPm+}0bvEOybHiU(A3nQ# zTX^DZDSh8K^8DFjjz>>^4%_LU&S7rC&aVbU53Ku9LcM_;au3T(-xY zA=kstYtCgV{IzlSxh&;7&Se}XoYy@jIYYjJD_v^39QBy{<#|kEOJEahFb^x3a@ zkI3&Wq~>11y99qCIBYrPetkXiYCm;txvTI-OpUKt{uN@(MYvYHZ87De@sedf?*Y<(+)W750 ztURCN80$dq(T|+l75a@$#{+zoF?ecx2y0OI@G=%>G+(&m#--`Jk9XkOx%=@)X{@X5lqN7?Lnv z+4Acg5y;yh&BDuwEJz|M8@5@%9JK}mbrx1Ny&&m@-&L?7Jp%G|xI2=i)&gyq`@pt0 zFb6*&aU|p&K(0lOf^8pQ4sM&|K)wNJ;jb^`M*(y2&f;juHv;iO1}hdD(teO{#>yoN zYms9h>4zN*3*51gUx?LA7H+K^2gz~jc-S5b%u(Ca36O6E+A#Nr?FqmvwH+(79CZ=U zhWRA$mtYN)r7i|qFy}#jA}|N9IR-%93AA7y2sv(oLcRm*q%3tA5Wg^r^^y&Z!H`^H z4S{VZ(1zVm*bW9}sVTu>kWU5LSXm8+d?*liD1sv(&j(sCpA6e!z#R2bXe4Z31mgE; za4Kyb(8AwX@S}kiR%YYDkHxCY!U}By`0-esS?W}v4fAQ>Ckjr+YR!g} zcp6r1mYO0s4XZaB(ixCU$6C>bbQbuTSTov?5@)FbNM-}^g*#OQelF04xdeQnU@_K~ zHl$^cl&T8wC4yz@3`oj>Hq4daPY2pCF91JJaK1Vdl1jk^crC;onJP%m#46gxit;RA z71pX2Zs65`UyN5nmRcgX6t9PDXq*elGQ1wbP9D%w=cp+7vjxvpb ~v|$$mAH_=7 zR?C4Fq;c>o1naT#wV=@i$x5^&3mR-sakL)1YC~JG@P;N0zDe+Lv<6E(0Yu%l)&h^j zYSmUJV7-buh7~JH2kTV}E8Z>O#{n&@e7Ax>Rj?4NSA3~oumr1ETNMkIVkL_g&OjTh z;LE^Q0Bx*>F9%jy-+^Sl;6ki%ZM6VssYO`pBHw_>E9+YDRf3DL?nRCPkylv%B6U~+ z+p5935tzbC*v5K#H?R#WVOzCZw*%K;C2V6|y$86_x(9fHbsz8=>we(1SQFdoyVgVS z`FlVctLYzszftgJ>nD)hB)H4k2l;NGjTQFKz~5~>27V9FQukVqgTF`cKHTZW@8JnP zU_Ax-{Xk1SXgvddui!)0vyeP2_+#q;QJ`A+*&gx6>SwKr2 zVgCVqFTvjSSK#~De+C|D{}tHR{yTK~;YAF-11orp{ZH`6+TTKc9MD#y?0^#Vq0?{^YJZ@3T1kbStLvps@x%N=-=h?%7wf4!- zSq`*S)E)(Xgjc-^1(0+A@r#mn5%>*)8|@NEE&$qU zlU)XWGY}^mb_Mtgfq0*6p8-L zoJH6%@V@}s>RG!1{4WKcv*VB)5bUy>Ab(!)MY|dD7X)9jlaT)kXseg))xcldX-HnR z+rhsg_?o>IlHUMr^;^3G{HOK?@Sg}CvNuBVncx@pCdfY*{2zM@ zfuBHrzTgFceUNMv+!Xj3E)aqI6Tw4)Y{)+aVk`(A0X{6)H`oXKkl<0kp~1ee9R@_r4E6&*0*E+p}^CF!+~c6PX^8l zj)Lv{;3?oM1s4RzLb6bBQE)uuX9`vYCqP~;xFk3U^2I<)tqq5IGUK-i}NvGgt zp^cF50NU#E&?fL#2woZ50?Bs-cZRk?eiaaHJhUDBHG;cC7elfOXsKI6o#1Z~yd!iO zBzFq#30)5PU4r{U-vR$vXeVrcF8D;~YDgXjqJ4#~1^+9-Ux&U2{*};mz*j@p178c> z2>eaxX5eo_yMeEVZUr6;-41*sbSHdx6KJcqLVLi!E%!jKLvgY5G$teGvKEI(aOTl0*k{3fFaE(pH{$$Y_@@NdCK!v|rz0*Evz;eSKE2Z(t~*a}$c zAs~K*I*e{n{W2Va=hXdJSs92m=hTe{l0?z zA}2#~wBRw3QIH=C#5z543i#oICr8FYGD2`_)CGv|40G&9fIGD6oJ1!QUbguQU<&? zQURU&1n-ZW0m%;qpNLd~e>$=Nw$BJY8#xn_UkJV)sRDm6vKY2+0P#C;k+Zu$S}~U>@dp0qh5@0}jHRDxe0d8-PPFC&4dc zs9yp{sNVxes`G*Y?CG=u$Eb5c_y)Fm1UO#32b`d?!U62moCutZ-YJ0nngnnv-U$V; zJM%~2e6=PLz%I!62Sh*sldf*J+MZ73OrA}njOG?#}~lmct;XYE7S;Jy_yYN zsg?s9)TO{ibsI3QehF++p8}heeMA8JhBJYy)p@|Q+5v1=zXo=w*}VhUzgr00sBQyp zQXc`gsDA*rtI>S|*jbwiycDlA@I_433cOO?0^F${241UzM+WfilN{jp)HL9AY6b9m zbrtYNbsO+z^(1hY`WNt4Rd`fD-Kll~?^5po?@>qR1n^~(xxgQ)tAG!v8*(b}EtBu( zlmLH_QwIEQP7&~?oHKyCa?XU#Ejg9=w#glk?^Sm}bFaD^ntRo~aJyIi5Sn|{1JK;7 z9)xCl>IW%)i>E)zawN)UD)zt1ko!xJyDM=I;ITo22ag{7%G7z&7Eil7f9~|o8B=EF z&+K3DtAep5+e(fsJ-PJyvPl)n`nQ!m*upWCWgjmzV2=O=931=b-DUfF$UaaH;asY_9#65m%Ral1GX7(T zA8*GQOhEJfE>dbPTu?q6d;l@wesTzZVf;n#$Nl1L{Pn`$5%}YNa3B0}Z}({Y9g9|T zJlf1aw34A{7sF-Oa|CuYxtlo#yO!gzTR8zclc%X^$m{8-?F*3GXQHMrLESzJe@pSV z3^hE8I$ehv9YcLyg}R$S%}rtlybU#V4eIGy{H;e#JscLn@X|JJN;8cuQMJecbg)1WTfccGqE1dn%nwWrz^x%4B;wmJ#H3Z824ph_|jtMpN~bv9{>Scp_FFOSB>lH3jS9>3Q*Vo02#zjK))i z(R9o+d0bJlxiy-KrD}SLR8?EDu4!SLl1xRosyMbX+MZ~WJkl``tnO%ysf<)1p=)9i zfvQ*n(TyitRE5MvgdWTMQj4PX(UfP4)W;;cPPpP`UUWq)p~|AInkj0GrrMN*Q_&V{ zMycv_;D~r$QDHPC!m7GxB4#2=R#nDZlBu#(yk5;q)+MqiyCZH>0X(y=0`2UTtH zx+YaK(alKDE1j>xgX39R6f z`jS*Ci4b)rGYOJAk~}4BX)@IubsK_+s^e{m%vvsmQ5~;SjCM3tkVYKiEzyMb%xTOe z>*`{yQc_D95|x{j6E>(RB(;>8s}`ZD##-vh$Vp3PAf~COFxy3Q+0R(j>i`*Ih|&NZv|aN;YVJ z57VBdP9wsxZdDB})pSw|6YXiw=gCsu;!3wkRbEvn%^D2?{ex~JXv(OM>3AI?813+q zS=E7_p?Ta{@s|4J+VnU!iL_drj-{LtsX1MjOeNwg(DtdRqyXyBpA@6dY+zk4X=!g( zNHTg*qcqNoYkFK+tR_zP@ZSm&VxGJbc6Y=%B$E$FS zRIPA&PWT{wxKxpfVswQ*M@7*@Vg-w&ptUs-*IlRAGL0YppndUVPiXR2dMOt2qF6ea zXqRSPt822!l`8bY%806(5$YmFTFDq$*xuHbY)PwzSX(VUam?v%=5F>ARX2r+>Wo;2 z=MB2O_{w-JB|=9vL+E(u7ka*CN~w#^z#liCG1FH2;aN|Vq`~a>Qexj-X8iH%xt?VT zsmW2+A0fZlvG==@AgPogq^2Iu{eERiJe>@u%vG(JA@;OUtuO@juw-ZN56o|-W12G@ zF`ynoYWl`$>iCSb^}29cdM4UcqzllB3KH>XTJptj*7jA6$+gn%rTsWs-KCzcZyM-V z&Jb(uk`}h9a+xJDW)*3_#ILiYhfYrk+l+@@dtiZV=R{Fq322c zy5*?KCLc{&DDZ16>8>FHt-(z7O@@s}-KBor){aU{(lJzL=z7ws=$aVDs8}(2$Yg^= zq??HO(@mrUSeU|)c1A4LT9C#hsZIA(IyVn%<#WXe;6g7_qVCFkdNP(3V1)5`>&moV zL3>-$KN--P-KEskTBzN1Iy3xc?H4n$T#Gri(q45FdYVfPr|AIH`q zFAm*A#!)w^7-jhD=FlheyYz{D!=C51htN3gA(!e_oLt-Dx$P!0F1ty^C~lnn8&^T{ z$uo6#ujitRTRk=KekB;ibtNj4rz9r7ng~R3acj58I|9GDBXhm(=7!#Bk>8akEl##$ zDuErNI=!_dG3y~TZhOc%r}6^yOh(@mNc2RrAcZBgvx+yf`&zMPajB|Un=VYf-r|(} zKRbgZa<#i>kcwB`UOs=qljVVLWYPnuEF>UQ)#>i8EixuIb7SyNJxp9Vad_*wrrIv@}_XL>C8DCRAfTsPLv!$_qm!QcV}KY z2EH*68hT98lU~PQbwzsm?HctQ-ZP@8J?ApPb?w}oRJH37O2+V8yH0oD zGOJOw9X^xngVc8PnBc(QZZkL|@y*mA={{ksP0QR$&lc&T`+O}^re|f4F=G~F%mSRv z2rT)z$AwKg!&OFGFa=JjT5O|>l_SWUAB>a2#HW#u|$!DTw6#8^+%1?#tmsMXsvAn&*J zTWU?eEhIYInMGhxwOS0z9>TDuR#BxMeG!z9bL6nqmTJQ;zZgo;6BxDvTf*zK6s?r` z>}g|L(5alTq zhF3>*5^FGKSsGhw?s7&o_EdKzsH{m;_Oy6j)z-dprRnaq2FE|mNmDLOq37matm$#F zW2Rf4tEPBhC3%YNrG^;f?U-kHGOk5+1EIB-B-zZUm6jt5PEMaQ+fllQq&VpX0baZ| zbYUvCCLUYs>@afkP&RW>Au)hqBe10nC5%R0=h&oG1r9i5|1svOD_`g1%XDn8O0UDk zr!R9=buyW#j<+fqARL2l7GKqtf^D5QOo(6t*A#mDRB8w^m&2@=H$5EGF2FVv8;qB+ zMak9<>|hd{(>9u_8&k1py9IL!b=;B9aOL0S z!!o_tm5CNJ)u8e^y&lb!`QCgzcArKb>%m7ZTXCT~9BNMzg0G01$u#KEic@R{*s z?R2)hD)dCC`@F}9-D!_8yYnR^zT2jfZkD=B(!uyzsuuWKGz4BRsW*KxM0&CSk+Xf% zW3Q&B$23Ok${vkjxdY=$YaEt)E1qZ|cXw%9wjq9A<8l zu}|R?j~Qxt=H0R;*@QEef;932Et7}m!n@}wg*Ys3F=`lm4Y!8325M{Jp4%>K(Aztw z=mp)(g*cRHNO1olqzX%ZR_xi7%yCd9%vHSTX98Bf6fy(9B!ePO(rM{Rw9!yQCYCE8Vjs~dUVNhmKIN^83%J?!P(`iYm7BVaZ3eN#DBU~ z(XukhRL7#taAd|6Z}5=((%Vjz#$RnhY2jE6L8hfoc8tl^+6o-V#_P}#l&o%#Cerk) zJeqDS!YGNsOlL5hIo?&I9k*$T;zZn{VR|xxnN%!h9G6OHvb*LQs3}L2o}JI=`VT)` zCq8>~2B>g5Mc)_D(U>H4iusPqBTI0JrnZik8gvj0k)u>r7c?-VT@p{nc~%LQHMTWYU!9Z-AhIg0U9T@U$b8>qk-6N# zsf~NngOM@!ASgF1r6@lW0xdj~wmVFaTJ10vl3ltP9~CW_;kC3akVANdHTqiJ z0adi%3Ic0~-0Hwo$&E?H;gZT6@_Qt%us7R`yjdp~AZpj?L;BhdZR{|{Hr~-;T;zDy zVN6Bjp>BtsA|cpD-Z7|GqDU}GGI)6d6Gmy%x60}S*I#0K>7M5847~xYD@t>8-NH6~ z+*;|e^V^%Dr^VolaPp2VV;*<;#kh&0RWL-w(McpaFpEx0gRCgQ>9}@*qid#!ec@W% zh7tpsyzo`1(%6r~`UMfx%Vy3;^<)VmS+Qt2ObDC(2QS~4xH|DXOdKJS_<3=2W8j1? zb4|T%k}^{j+zm2WES9nl=l1UzHfD3}@RnN29uNJAJ3K6sZEUV4JejO~JmccldM?yl zC(OAb3?R5oB`9^9eW>=U0CHJ6)z+a&Hm!}LE2vHJLK81JiDOqzr|2w<7VYHb6BTLY z8j#WAt)uB$=x*m!Rl7D`-`401N^CCZm&X}8k^M%xv8t_uT|!!udPo*vOGPNwTIedg zV_WIzOy{L)8&c8wI5rS43MF&`*r`bjwIU;~eP^sSM;Sv!7!(@Tl_Eq}44(3{Bngyi zjxtQ3yr5~7Ay^n|VI?KeftpZ+z*p^0h7)YT>8(3e%S(-_b|rclxw6IJWHe_m!cgMg z=%Um$h;?lP)*li{P>CBTq9ZM(9vAO;`Hp)v*deUNkd4t`g}xVrykLcLoBPZ0n<7!?JPfTUOZCXSXJD z(+O$5EY_xP`GJurv3mOA#{GU=rd-_~Da;nWOYfT&N%2%QNPxNwUMkQACEp>1*pZH37!>ega{DS)e?I93JAOMwmxW!OtQ zBBL#g(b02^>ol`cZdy9R2E)*;Tr1W7YgVf=NKBMv#FrzA+L*bI$C##LyhO*q(aqqR z0F>7mj*T33o!x&N%S!OL4a)YzN|#{Q5KrTJTXn2uVR9{Zb-xhqRogcx!1q zmDcVY$vhks=%xWDRZuBPCDZ2OrSxiInO3!!Yd0YGgp^~3$iQ`<#2HH^b2cSh6d9S2 zV+~c?ZYYwpFwP9d6hvse1qD_eYnDP-=kN`vrA$5xGn&HAW4-iS-9%a|N>`a$8KPR8 zbH!3p`B*jQ$JXju2VJAOnA;{;H97^LE!??uMcVDRQ8D%9Sku@FS~~m!rkxFyl%8Pj zqRpV%?9?*Ra_W+{b1ItSgQ<^n=f;fATmx;`z*tpi;qY!qQs3N=#3XiZwG=zol;KWp zL1OWQ8kFItr(Ei~@kw~?k8T(`bj`c|vz>XaoLunSIyvF#WJu@_CU&^l>?-(-o~>kx zwlTTiU`j);=qjVSo$5GhUgGGqI+G~f@O11=D>TXSY;Z3PZGvLWQCqljPgpLokXFRz zLS@%LebhxZC8wd%zMDvEv2FUAuh#1(^fbL@?J0UPPtVudJw=~TYdU?8R&h+)&fn{3 zd%r~M9InICx`(r&8@4^2FOp+EV-@$_r76oo#O(yI+u-qR@#0+Z{G!yINug=tjw`xW z?({p?mi0nOB&>00SluL8rSQ1}oH&h5#!D$SSx(BM23N+=s*|mYuo!P+ZwI4UVCnI1 zA#MTazDUPnok=KCCSuf3eG-8NBgZciGIyC`^=w403`-zH7K^10?4DwQV^&zz=!erB zZ!mt#nBfVy98Y-0s_4oXdqPrlFf#PGnTY8Yx@(_N_nN*Qvw~|uPCKNh^m<8d-?}{{ zdqwt$lG`%YLSlM3w^MWSRg|R55+%*Ct03p=gk0N9ne+?@%uv(qYLgSFn0!5l=_;7% zPS*h6bsL9Qn$gW$%88A*TLZ-`CbMyBf!E%&+1IQ%m)661MaHm=*-WbE*zT+S%VMeI zJUV5b`sCHP?~e%@^EZvnD;jG22Fb7tYmpe8MI5+mFqrZWFM^Dz63dxJZ4}mLE{=BK ze{Bb*94E-yKrYjSfZ9<@#8=JtlvKrV8dR@!G;bujblj+UMjaPz_C-z$Ozd2PL`a+5 z_<2SX9WBU+pRV4ioHr< z7o&mWNdo4vCG`#1sKtXg9+_cL<1C?2sT8utCy0+A)T+@Y5Sq$$ zZ0Mz7#V2!RR1@@^^I|LURuM`LPp!fv-XAner>fi@%u#R!ht-nhYxguDf{2xg#?py+ z$%MUO9kkyQ%!>8aK6YW3LEw%(i_*}+GR>QZ!t2y=nheCvU}^eW0DRdrnWev zz0bYtyLRUq595WnG6zQzw-mNiTN-4OrFJDAa<~RM@;ldjZ5v<7VeukQ0I{co zApt|6JfuQlvjO7>3n#d;5#h8rN7BH@uQ>14H-T1QhF*-c;3+>LqNCYi5Kk*ODRWEFJM|`O59ML$*4rZiCRTQD(N~?Lf06h zOn+pOuGP8;s~S-`a2_hBMx4N4zaDE=X^NaFVF%lEMKtMrFt<&pEnb)6{0)19IvB@) zz3RoL& z!8()4VJ4!UV5+hblP_J$C3yeLU3AEC%+s9IR}4+Uv?uxzs3;`bn_IYl$QMzlWOdvU z*7d11mDH7o0f~sY?i!?A{PB=7@e6dx9X4*d{0?iSUGaUuUPQ>x=x0=GS}0V8j}HP4jI$AVJlW`@Zq z`xP?XmGDU)6J^Fht5Igc@T^@<2h>f+A6~bxJU?|>J+lczFXwO! z-Qc_=mSbS|O|H@ko_Rh`{i~8ZXh-Fi^OM4kr9QsK$1n5o5&>P7vZ~M%b}kxZHjWbK zs=&;u{gdY^9NOb1A)bVxyTnN-9>Pl7DvGD-(8QL?GD^u~VI(i*D#&g0avs;HC4$(8 zNZ|?sCe-i+-KR_|^#oGS@Njv7o1@OO&oyZsZG@({b4}W^G?9$9(O2`>Mtf)s5SQXa zwNV^u8_#TXw0{b>zhr+_x;giq4?9EGz%9(BxTC|XW17eH92%BpII78TwA62^$xu{F z#d&vOhK>oYTL>mxtcrNJD{J{h=)GM7>WT;*g;)b7KyBECFgnIOpSySooU-vrv)LI@ zdLO$3RV~#Pt9MLzIbR1av}%pirnFjWXgMA#HO5=&c}cF!@H!Pd=PET4*_$bGta+C< zt%@C&$XZq8*zuzlX5Lw7WM+scs5K}5YCihCT3xG!H4*g5u_lu^>-XHsF;N?A(-xFbYNyH$+Mix2fV~(J(aUwTdV4Q#}dY)zaJ9Az?$NopwfOs;-rTwKSQVrrX;kZD2v!&-32 z#q|cgas|&K#w54i707rD4XN-l9-~^v(%6rdNXob@I_%KRk(gX66Kiv;grVSIgwtX1 zI3u&%jPg<~`I4D}!^U0?!es!!VYS`|!TeVCJV4Es2RLuXqgMCvI$p9dq3cyzZo_#3 zF2eLVJEzpem=$B8QIF<<`|%EM0`>%^MtOn-D_0`tF@3-{Gq7yO_2oA0ahe@#y}JhY z_H0;Co{8mY_IJcWCy`Pfa${$QQp0MgNwbz4gSPSVKwEld+Uuy(Mst*P&m`J*mubI! z`J+Ae38>*2b)HeFyDU@Bi;t)0#mtkL9M1I62ywLqOEqqbOQy^E&pEe*&}_+o)3d+I zSTx<9axOy2fg-v>-rVx>?!jke6g6fY<`p<4Fg$$nIJz@MX9@_{y7V3#7ZA=4oK6!q zxZPTXjsDCE=9#s!XEy1qlG?@X7|k`tv8}}>$6a#{lv2+qD8X?q1+txv=wcz`>~?eg z!AdE6^Kv-BJqhe=W9Q#+GQx2cV?JpyFz}hS+CZ{KRtVWu1 zWs)veE+*PxTB+ktE7u@4PNo?(orA88WTI>3ny{zVGvny@f3$E13%yZ5FX@G|-Qw_7 zsFWtIg?OHV4Sh)5ZA?>Ni%>Tt-BnU|%5ab*WeWdP2^`6FWD)5R3u|54i#FyHu5))T zNl7)Xk0rE+H0tc)!2?Fx6gI5m>*SDu`_OEfh^1r|@8mV%)R|jk?(v$G?peuH6Q)s8 z>2&;f7s*K-4oeP`g-$kzf9wWKn{>_6He8eLo?N5!v98H9U)SulVl8qnD7i*`Kgl(k z*-F}(jB|`~uT3my#XI;omMV}+>U>&aHmXu*>)9$z^}t8QcsR!jfE`;i&)0U2N!u9@ zw3RU!S5DZ*nPb$p8L`nC86uxnMiBm3qi{?-y*Q#n)T!r2vq`+ESx&@_(dc>KB3e2R z3%m*H^!5ZOYJkJx{7c)(4OT}SaUvS;cxBq}I&(+tPDn-`RMY2*w$)GqzP1j&b7{#>0+k>k1 z6>ZMvI$(qqp5NgJfX@a|v>37ij>h)q+u`-del2vOhiVRDs^IFE4s``CEpCAK^bo281l((|!AODOeo(Haa z5uR|g@dCLl7_c0{=mC>v^zxOr@Ni3!hu9onYVm?i#y*@=M(Zo;ac0S#RWgiZQ51JI z;%yzK50ytXoHlYlOz(^>k{j39VQ5aSiOC5wZuYlg(T#$4T_dV|j)SEs!=%R&ICDhD zA}xD`Vq><+XlGbXtjs2bGgm??gk6Dk6Q)`E^1Sr_ddAvQq-SArBaIs~5)>+y&tHa^ zDPcq*F@YI-W1<~bZBo+0P-49E#e}YUN)M~+O{lu3M{LlxafBn!1bqIN=~;|3mqqLt z84m93qkYNq5+bqf=~EAh&a!xVK{MvUJc+~CJ309%&~qSDCmdTQ6vLI#Wdd<4#jmav zGDMz!H(ieOe05V_8gIbL!vvKf(%xl=*hR}`KL$s?>srjK@Y)%k>M4TuPGsVXa5~0D zL3?ftIIE~bzgq8mgze3#nbX(-zJ(*`o<#WSj zQl*QL2+>YoG8RtX{Uxg}91GV?=(^Jvs9mG%i%3?ZQo8A&518I$Bt_?TrcCQ)CP?dL z%KZL0IpO!uk!d|&erP41fEw(L+~tF7&~Etv2$g^a{Wg-Dr?Q{u+yY}=(0MPzho1ej zC}nG>3YW{!n_>&OzFsn%jQ-Fr80R~@U=Q=@PO7?JZ3T}UrO5esD0%Ee$=Q#e=fgW3 zjruQ~K~onc8C{aTGeF<@pK~h6>-=&Ki0o+MEpeX?p9)5iW%XLd_gk5z$OV2oZ_Sx1 z>DW7^!pU#E0e8JvL-hkwWU-gMCK9-b!}{)yTBbmpK};8=Yg^kQ-<3rf;3m8==wX5G z^ham4nGXGC&)(aO(e~Qx*=x@|_u9-Z+C8S@w~&3ubpCMserjI3FT`Ftc2@MqaB&|! z)3^@vrf6lXc?EBmBxAvAbzxO3E;)md`bDs$r@d9heyVqvH?SA55l)`f`vr$Kbh?Z=T^Za>itA zd{Wds{c_s(0>vj_9&wQ17mCe03ZFq1GI&v^XQ4}Ccp!!u6&7wdy}}x&IF6@Hj#)GkYMh7LQfNwwi-&$OT<68<#7drNo4Xi{ zF&?NE#i#%Gjx zcplRz8#dbGGFkBHZ$3M31GQtmyFv_@Zo`W2xYFXp(dP}WIAJTYHl^mW-A#jKLyNq)2l*R{AnK4mXUh}M(UP?K2bR56M+A+`ulg*5?8En1VT*!ql3PJ#4^khD9ckA=(^ z!qQqMGgv1elOvhQ?VcdyCY<66ru;ICPO)d!srAeld0pLFzI!)Q8-4nV9i29OT>@8_ z3Cpm3-VCn2d8t1!P{0{z3udHl`ZDZm6tgCp!sQd(FOs4+z9XiEu{Kxo>Fo7JW#Fh?h{i!awe(Fky5O`l@xfyGI!@l_|&7<8%ibfJ}c%FB4L zvd%lN;nG%man3|!)>+}i9xbUyM5J%x%0~8o-CbJ8WS*09Zy0vcV$1PcK830P_bwKd z6)HZoCdDVncQo738wJul3dqk2F zcOca1qP&pXYuN6V%?^6bcxWLrP;VU_J~GV6|7K73I@#3JoGuh6QIbN*3(93)i>0-6 zwXVUl!6qv`(l#)-Hcm#kwvK>{SRHOztj!5j=cFt{wcUS9Lq*zg=Ec>Gni~^goIuy% z$7trq+Ocnzz-L;p4~$QD&BH2^SIr9AaGtk9t_tZ-`Y(#DbVl`pbQ<%~M8^``GQb{N zPgU~;e(54`co$2p!G%05OWJV|UFkUu~TV%45o0a%r3|7=8FSS6Tw+mTTF7`0Pl`4##J(db?0;APu zM7|IMP&!kDEW#R3V(k44gA{nTxcqMvky+?yF^cjYE7nHbd*g92erAcy3fG%CR+H7A zwGtM2=>58Z?lRCiS{s@9`p8~?DoAe+$N0G#?suB6*YS!JW~vpumXVZXDRZ>UVB|xq z?u=UobZoWDEo;@x??B`8e;C0ry5ew7s6>h1!;oIVCvzGiy~P7?`A`np8OBZhIT@&? zsQm|OX z>#Rc_WBzKsEy5a57Q=Un{u78!*6%AH28(4E-`m89K@UP%O%1`*9nis5-Jc|76U+Hyx&1#dfh5XH_q(L()_vsW9xoD z6`csKw;CI&8&<+$Z)JW4iP7#ZqetCkCNTZmN#aO{t_2db{{65FMe)TSviXQ5G9BNU zG!~qwstWv+&ZLR@_jJ5kgjqR$eW9h^+{b4ya?z1Yb##SZxjs{~*ktY!ogO|Zccltb5#*|wx44i7LM_xVJm~U$q z;7qcOhn0|`3#h_tn@q{+=*Vz;*Zc+^6}b0c_RGp}jjO&6W7|sUA22h(C9W7noGj}b zq|8ZOq!?P|r|mG$kLi1|U`*v>uIMccXDU*$04)ohEW#*R*Jf7r)k%EA41@7Z)aW&c zFh2n&XRR_H(eEtMvp1AZoK`fsXi~x0g8YKQv6ClHEgD-maoUuzMW;*a^6e-hqhep3sZC zZV1M#(})U&FW$*F-k5IbR&+wOGPB3c5XmvKV5G%DbmgRFas9cY(_3R&P|f{3+7#l` z8uE$Wl}@HoDz^%_*2d{TTdX0O^3BEOC)?sHJFsVrLdOK872}f6))bK>%$FU9n|~(t zZ)WMdQK&jo7AE4fSzHC)T{ODpjW%=P#Z&PvIkkD;54P<4wbsM#M78 zEaKL}LRCS}# z>%rbkd$E{mPhr+K4!2dB`GrIN%9bPaWL^l49p_QFSx`{Vi311FFb>G{$5 zm>Ji^iFne)25Tg`(^zKQFT)Rurc^z9 zheqrV{;&4V2S(2F&hO96{+U_t%y?&PV-t4cmqLQc*^X^YY&ZkE_#e(0Z19>aiCt&x zUE7;F4M=c0PC>0zs1DUghnk!cm7IDl z^pa~LQ3;xUzQ5o5zB6O59g^!Qsk-~;eV_l&@A*Byf6w1HaM}+u7Nh2n-B|b_#=2+F z9NwvQ;q*FCz$s*t2-vy$xT3x8=j(NReI#bsvC-ysB0xw~^|Tg21PfO*JM%!|7{P{)L)-y5n=Rm9g$Ren`LhWv4Pnh} zac#mU6#bTOngKJR;P-rn(a9lAds&M6q&pysj~yL{?IBCw)*~rUrACC1$xqH;`M@QP zP6E-}V9IYvx|3rXpAPZup?*Tpik3-%__GUh-DFXK%rfLox=&P2+afBYu;G3@*~Hy? zsK65G3#uplRyf8$Avr8^3S@;-8f@4;#nW;jl3KPnjECk8Qs&Y}FhP*Cm4p%ABUo?8 zXYM&UG0~J+Hj3mDu29u2Uqg0ujmefUKFnkD7?Td@?&Ek+6O$)&Kg_gk?maPXeZ~+c z-oUInY(FWKG!;*>T2hgQ=J_?h9c-par5w6sbB-1|bkc9_n!s^2dT`1pk^wJWsO{#{ z8c?}oEV%_s0{kJnJn^Vs)?ktq{Epbh9iU9!c5^I#46ndT_P5@)au7KEec1X(;#E5n zG;MIwU8q|mESdn7t--s^6uTPL1(y^+yG(o-El_SOi>K2A-( zHtApsFlg%(l1oFY$!YcgF0jmE1M%+IK(R>%9SqPJOPfJ5k1ExW&OCO;-R|yi8cLDl z%RPLzYkt4R}gncGj-ki`|{g(JeLr^zdDQ=+$nS6;C$)&+Nwc?p(+6^n-KhB&11;=YC; zIFpxaU2LukB6k-9nubXar@?W|c59BPdP*uWjYyBc#pJntV)7t%QvCf`cKfgc!wp>; z=y!g`3d;N<2u~F67>y6l8KL<3IRpsi4>~3m$2+_@jQ$5X4m)Kgk%5+S8IxN!3_M)5 zOKoh#t9H?m$Q8MKVL&dk?@TxOK`KdM!?F$Sgn5(LMm~PfCkWHm;gEB;!(&*$*x|XrdNR3C78R+-2OpEjZQqfeUOrn(vk% zx@>fOiVhMjo)q65w7sh7EF)(+AZxo7`SAEDPL0|2DlBi*!LBLaa%$`BCZZV!`G+v= zx`?K_i1(b!j86Ad*nVdr#r78)?zDkiA+{ghqWt`{oW2@k(JlL!!)Z8?c}HF0G)nHH zOv8z%w3IUK*A&s06UmiDpHdCV0@Z}knb5u^I^7VRZikLHL;F_fQ3F&K1J~ijd!Z*7 z2c2R{V2t5~?Nd(FWwY&vkI~WG8#QLcA5OBn7fuh=c0j8PI zBvv@JHgMWu2+6Zf!!T3Gtc#zVLCL4L1u0sJ(d{EwnC^BR*yJH|tu=NsC+BWuS<+kD z1Ba18ck!ik6MTuAY7-~(Ga9;UI(&P&kwW=wy2PZV=1ojN$2(hnYpSArP9}BjJ?UUovganP2h4S={$$SXSrl=!5n?dh7fkIV-Mv3ZHTM6^pU zzkS{?2FY;!#ay(_Y8lg3dtmWHtm>FJ7jan6PH+n>>m_=P*zd8a*x z>}e@{jmx#KCV37?d)^cn^<#~h$1i1$6Pw14_u;`MsVm*5AXDw+RJ>jfzUn;82R*S0;oua#Z*{exUR#H=hrc)A^IP5;jvDGsVt;Xo{h5PhFk*hD4`n@EL zr`TJH4I%vca5&0xYZMNJ$#6W(F#R;iBveFbmF|h?pfP@r<6=Ze$O+=72^kF`6S`Iq z9=cZAGYU6`(6OEWBOwfZ+|q2PyefH)hShulM7gdMvXin8k)D;q5EemNz$5no@{NVV zAuQ!I3t^Xra}s6F@CVu5awu-o1p8d7xYtq+3#fC2UzcEEES#deyQvjJg|x!UIPD4&yjbu+fqb zC|mRVBt{Q489fNLv$m{hQ!?%&b0W>gx%pDx_0gyCjj(Fn!|l( zNIibpI*wReQiuD_gpUFogNw`5!r-W-7WbWDE0}7zTw6p+&9=Dj3}k}Aa+z8VT3iB| z`$A8G6}h~9ayi_gcXbdnf?S~vnL@oj$eK`h%N3Vdd-wlql&<}GNif{8)p%AMD`_!G z2a10=WltB6n;f|k35TM1NwQGn9y&(&f53RS8M=dZ9q8oQvH^ZB~ zo2k#`WXRo*3tn6zIib+0a6kDc;6#d91k}bhlvFxh9!^{s^>wNT;iunW#U3< zOtE!@kEum-tqQB~3m7$2O;6$^!jDmv%7};+WlN+U<6j~t3Oj-Wt6Ei?Vl!pIAyF*4 z6hvW>g#Z2cm~4nsh2q0u7mx|vQz6`_IF%(KBDlCBioBbFN@ADkYr@oq(4#cR!>2*z zZsJt>#|-sj61JN zO9kx^F>2wEL2W|1SEA9oDO`QT@GiyY*utnLj?V0vAw()7ln;|$rR=dbAEHf1s24HX zqkT-?UME0_^Mj;LF}6f7Bv=`umn4Wo?*#d!c1d4WiU$HiA!?J`Lq=Ldu2c9fQ#8y+ zl1lyI{UR*88j;#|B_l<|_bB4>c925pPsO-n8e9Fc;WxvYa4UFP6FwVOhTlZ-wffez z`EE*m;qD!mP*{_OqOcYCWME8@LzH(sR&#qvSKV|u)lG#OL zVQQDkOvB1*k6J96R^Nn$3D*o$B7L;bOHOf)Up1uuYc8#+HSXdX(>N=KuTdm@xkS{F zXwWpY?6tcobO;y&A*#P6#M+FIF}@Rx$dF(yRjGeXJ-!>ChpL4;$?r6iP=crk{9L27 zdoABE>5r3728MC$wTjzAe~V*9+bX}ctgyT_h3dE{zO97KR_^dJy{>V> zl=ERq5gp3vY2|3iP035jr({N4(W z91EG=raSa1wAP}VJMi?N@#gc8Y(E2Mz&rmCzl>J zjJdpT3BS5E^_?+vMw8dH=V*(&x;?I`P(x9@BGX#ZEc8mI`_RGZKj~3Xd(wxbGwR)K zxtcg_OHpo?6jRPMi6q%V9j-;yYEeumii=s*Gb|+9LxZ1PP~WGZTw#bF$;l{zx9Lx5 zzp-Y_)xw`brr=YLi|bvbyiTiwO5)` za!xI5F@R#p*AlWsIC6_Y`uT`+9HEk0lKCy6*GZoWQm1Y9To_EspXOY|tQfUQ&I+ey zC(u((IK1CZ#ZZN-Rh9<2YJ+kmwvzfwEp35yQL~+?)&f)Ni$q!r*G?ajydEdKk0a41 ziE9Z(Z>c4%A%c_n)n;p2WZ52a(}y2;5IEhO6t2}HqQi+G3KcCfV~BO8-r_6PJO$kq zQg&=ZitJ`sFC87aWHg5khW5AoVm84_hMQN)8Lqi}Y*-{(7rd>!E-4_qOhB=rsJ@bv zklpLn(GJqPL`Tz`ohQDLj$k2ze4=-0icL)Thtu#l6~p|8!3nX-utvjnhK6P+urP27oe zmS##Urr7c!MC}y!$003NmFy4ETdVd>!(!eQ-#cHp#h+M`d%)_E6lWJ{kA<>sgvD5z zVW%f@I4lx{sg?f_J5%OSy}T{7PGz)OtD#pg-U$@~TkaFUvftiE_3rru6k)CoFxNGTZLT!O;y6iQ zPdy!nNWXi?;W$K^o{-*8SkIfgn7GZiu<7%ZK7}+v`mpeWwA$(WRJaZQ9T)jO2=3J) zJr(~!_@>35J4vr=Y~oT^8b$V(dP`a*857dBg&K)8?etI_Y4u%ukiJq&#AmXZH1?GA zWUC1^t|q_i67kLs{;^dSsz&i~;y(tCn`I>9MEVoWA`XY&KXZ9$p>fLPMkb0|pN>=H zySdHxkv89k7N6{~)3idG1}rf$5VmC}`KVg0jj1}eS?{D(X&6(Attp#&xM^{OU;Ei< zQjYFP><^7Wef;lzwF?+U{o;6R3*!KBxZ6QPyqgrC@Q22^k|UfKB;^rWq%lS4bqyj) zaEaQ48Y<}3I4K^D7EiT~L>D#3m8sppShG2LO+%4Ys!L;Qk_0lD??{64^)1 z=v}4ZYu5?Z%?{RNu%xb9VR~h$o~lQJZ`wB%&tSJWFV12!kLS(;q%Hv9Z%kK2YRU$W1w@X(X^!xFVQv3iFL$|^lAYe05p+S4`* zvj<#j9A)<7HSF_BeJam2l8pk4#x$N1Rf;ElTq*mZDw`pmd2(uai1DDy#87;o71Pxw zriIOWtY*nmVeq73DKecWY{#I0_nvo1_)easE_6kTd>GyNVqhFKu`MS8Mz#3O4w(fBbG)=xUn zEbg#*EE@;OVw1Jjn!A+)fNz8NLZcpWn56gV7T)z)8I20uc5bon%1v&SBw-BL79y+1 zM^iqdn991VFNQshDL-U%asuw21QsMPW9395Y{4(+3Sa|rowJ-qM!tJi<;3%MngC7DWNrh7@{+Dv88y|r1q1P}UaH=#t$j9$Z9zBxnF6JNjpKt%!^5fsgWV`d7KmF-XKi9c{Ma9l2lyB*ZGMVx% za^7Z6o}p6vPB+247BtKVGxxFT~!xUM(X^|)4;YXz>m z&2=}fmF8O69c459m3sjwqk!^W4|t#_>MT|2!^M1-R+aab_x46z8QNGZ@6TozM4gG7 zbU25L#cV$6a92LcmiLpTD#w`ec6NiKYU83fp&TgiT-qTl+@8)7RH~gxfsL z9a$K4c^cdm67+fjpH*Br$!|WzQhbSgb>N)Mr^Ux!FTO-^3!)BBR^Ho}ln-L}st8)0 zrHFfrQKwgYuR?(zlc zm)#yZd#O%{E*4cxdA}95$@;GL&2sIJ1xk?$%lj*vC@b4fJ$W*eN~LT!jqD(2$zR1B zn1+H%@!L6WP(bm>ltf_41uFRgYx&9`G2P|*_3A`kJy0VB@UtMw#jcV;v{OX(tkSN) zRX=6221nA zSE*enKp*fbF8l@Mj-7p_zLHV%vplI??)oVKSkR+Wk3IqYxE_B_C+`!B?1STBI9SqZ z8WD0v05(gQIAmWZ-5$h81Z0hPpv%6Z{f2HO6QO#*f~c#AuaGa&BVb}wrHw9#!jeku zIRKrdV4gy?u&Zv%^F!mHqxuD_rS?P?rciC9jOzjW-v0W90+e9fQ9lE7<`=AJmJKmc zwp#asVY~JM?uNRj@r)j$JnHW_C4Gma*7DkSDx+TScOVB8Q@)FG2BFq$26BK*GTxRM zwPmKW05-DSLTT+qJzi2@*IrVEiRVT4ya-_ylpocAEP2LYYCL0zIRzAz+IjV7Z`2XH zDue1dO4Rd3T31jX#u;lhm7-^+vbzLl(^#Vix*95Il0=_1V7Ks4A1pvlrBZ=Hv)Qbg ziGmn(*4pO^TaFv_XT(({7od27k@64QVZcVVUWm<2zkHq@RM_-|WP z^qIBvsbV7sSpYf0|(JFL?Xi)90Q(QQQ6;HjTtyA#~c?geJY` zfdxfZiMBe$IFfce_j}FHFG?Gp^>$P1BFPzR6a7wa@uzl*faLU?-!_=P-5-_Wv~m7| zz-~MP(N_jr3TiwO7v#BLw%jk{snpM^r+Jz7lMg{7pP}Z@Eg??`Ya>Bmh-oo=1t|yt`pw3PiTqF#g-FoEArP#b&J?^a& zTdptGW0@ZPdPI7x)MGs&8`B9cSSjdv6=li*0e^)eLIukRXAvQTt>r{m->CN1MSht+ z#sa~VexHf!a#cnb?ca!%mnjmhMp$LF@2auix5j?gYO8%$W_+S|7Dqj)n<`NYCxDhFG4N z%1%=jk6Xaw*i)_%2T}s8@3g3$WUtg8&xa0VEeZr2k>qV*`UxI&88eNVTyH2$22TA? z^%zkP@RHjpw~lECr9Gi%!vsiu)arP{J4>30%%1O06X7hSqDsXyE)x+IQKwKV%I|B@ zD@2I8JVt6ouu~sT>Qg&L?fHg>N{g-4MXpkHlT3o;7?dlRqo^IWj|#V`SXuV-PWH4+k~QYl!TXgBKJgSjACVi ziY()>x|kGtM$(G)?{#C=^2(7{@1h#cJAtCJ#g{_yZ7b~CW>de8a_GjMBoCr3~g1ytRbB_h6VcwpXt)!QQ!>uB-zwqF{kejZmG_Xr58*$WL#vut?Q*ixR za4bZ4dUXhPi@1md%C zh^BLzG3fo0C$&htmh-8$oKJa98%jRrZfv|t;}P7NeX8%Ywa0T#RQVplAPvI7J_%Nn;Zr?{Wq5Pg^TYn=PvmQNI%U@ z+Ux-}CQ$>$z5ybAp5vTsK2Tulc@}eFwh!yB1FyS*TT!F#Lz7s-a|N<7{}HFHqA>dC zQF`80(U-&qbg7;*de+Y>a-*EJm+fgu=zEc`nX!$LL>(a&Y(9-{enUN45;B~a-Zc7n zQ?&CVIcq=i@E=?FkKz1ce<_a`=s`gMCY3QkT(#b9G~7)Vue3q41oyNT%}E~ zB6l0Jl>T34v5kL%TLBnFtaSSQxp9^s>HBeO}3`BP#E@C82Svm$29iD~e^j)A zFf|6p0%hV56cKtMK|S=83{04$>#}ysgu*6)pBkbnp7J)Bk$FioBMF?07U+G0()D@c zDTC@MvBhhmng1pFBbSd~0Lcoa6lYl*Sfzoa6Huzw{=CfEyQZsZ@4^YvfVG$C1$$c| z3K-C^#exP3K2YCE)rP0qE1vt6tZ@RvC>tXQ%-SnzTJ6V%w%U(H??hKkK=5Yu4Hg1) zVm-`0($(v-Y?C-9H|__w0W@@%oak$&cUp{7e7p_J48vf4lBmpZT|c_0S_Tul>;n zpZw{+{MP#IZ~c!`?`-{#8#CMA_g{0ppFj4)zxXdV{O$Q4|Fgfm^}*%KCszL9k~4qw zS0DXc@$H+=-+$L<|MIo}aL2QyzK`~P{Wl+d;ky5R%gpzu?ppi9cVFJTd}sb!M}Gf5 zef`=GKmX0?Kly&=pJ2RZ`g1wt;&&?fPFOH64`LH9k)JeMl>{cu z_XaXN;D6na3hQDtPHTwZ`5eY8)+XVVdbPAT*TafSx^#jZhNqQyFT?jr{g`!I{TNk~ zh&;TGb>^7!2D0*A>&^PsQdfWqOYh4q)T{@~Ta18bOO-(wt@6oI2~7&aLh}oA3y4Q7 z!GI+)$duIlSy)o8pUx}_d3%!t19DMVmdoK}foVUoLy94ZU6ku7nyo3tat@RTD>;oa zugVovxLcwK2*vOxgl6T0uzR96zy@QY@p@gz+FRbQwvEvbg~E-w zMO5EXq|Np_I9_%Fl=AEsFHk`J3o2-=T%{f5y~SLYg%QBIB*khzN>c}g!p0n;vAu%I zsH)2YDbp7;+OetTm#gJ!Qk$uv`j=VEqLaQVAA^st(!X)t)$$l92c?U0`SKXZ7AC<} z{R{jp$aPc_%#etXK0vI0LCwm05~DwS3rVUzNb03fx~U_FFgBuWhEwyozFfD;mF4KK zo?I7Q^aKrBN}nl$@c8BEE9QFQ6dEcDUt`cGs4^S>Gh{eqSe}88!lMK13%As{B!2oiF0_l&{1g2ucsB z{fj&jruDqtax7l^Gmg+5{3Hu2s(z@#!uu7l&NXy8| zZQ`-q$cR$w@4`Bsm)jnUM1vX4sNg`+O$seK@|%z^`+i zUadzNB*eihmx4Y55F?)@Y~CyPmi7c*}btP6YhBLMO+36e<#A=3WwvrB7`yc zcPEUrHNP~%kkNLT4rw8bD{yMsgv;>ALYgq-UhdmYih({{Bq3_p*iPN_aI})&LmO zRaY_dB{$z~q?$oBKT9Lsp5-_N*=S@)eFKEh)BW1jHo(Y|Mn4E?df#GJYfz*0yJS zPxXB`6F$_QEvZE4ZRtjOm5YPI-I;J#d-|4E#)4Z7<66VfiLq5i0*2cNVz&EC9qEql z-^he*?WLta|J-G5$b@xCS!2g_jg~=g*YUN>?f7c$NOzTourm|3C;3EmQI!b0ye~R9 zc`B+N#%?9-27Tz~hNv}fRJH2@3Da%Xe8E11ZJ96>U#vJyG@l>Bpq(^8bUiyaYQ;C* zn=vTjzCq#9(DV%kPGncoh1)aXmINj)vf~1D(kOMzY-TB=l4c^ism@srqZvMx7Fw4H ztCH;G=CUl~a<2cfLQYhVj9r}N*E8WGNfs9ex`PkSAB~V&@Y*QUpiAP!l*ZxB>RK^a z<+}@9xG0X9IE@OKu^OlCTOZNE>J$KT1z&;_5Xfz+E*2~zgw>gFV**GjT4;F-nKerb zVPz)VFjw-Fj#?8BX2P#0iM^4+=}5$PLZ%(CY3mnBLc4Z#H@AdY2hyZNM_ELkvw#QQ zdjU6R!kPqv7&3wIz&bCY%4N8qH{m=lIE2laa65Oz`Pk@K^|)}>Y*#DfDvYbe;w#Xi z$-|M#5M@ea|H5SmJQX78beTdJqv_)_kzMweQn%yw${GDDEx$m zCmPtxLk>ucq~)P|Q~8k;Mwu=VbYphh1I<#HN%;M*>E}6)ybfi;rnD^GH658;IXo2$ zK^1jn)gjrK(7u0>$?A41!PJWt5Yb2@<2J3&ee3pZj}WfQg!d=KxQdPqRXAs}3|Hv1 zHxs(;qI2BRF?&uPgkQ!nX-pWy?PCwhqHr_Eb;YD?8`=1(Jh{xSP2PuaPbTcRa#_dq zS&ebzb7}=0(f!Su;1G6*08i3wT-_HAX2SlK;<)C%4X*aGstL>U)djzwltFr8BDpj` zzD}#AL?KbdCo*~@~D6guLc22Yw` z*r#ta577dh6lE(8&|N3^)T6qoX*rq=7pq>1xHl7aUZIE+e#@(H#}SjjlMi7KTB zFxSaibTaG8qQ9nT+0-9zek?b3)E-}*seL}4tlKlk2V7PsfxFX>x}@KWSQ#CgJ~TNs zF@Es>!hGsAnc?hJ=?7|bS@kwk3)rYkMYCL?4Fs%EYYAo(kAV^s{*}hv<;K0NX7n>D zeYYj(SAs$9wMl91?DuX?VTIG-FF z!O<9J_I}omlDJa~UkY6mx->a|OwZb^tzgdP=yH~awdPl8{HZ;^ZT-*wvZUr7)&z9; za1pD4?9ZYY64z$8<|LIL2dSfzMLm`*pHSj<|Vn4cMoewwsKfxEfW`A zT<|U~Xhn;~!;x)(eAHY2UO6T%ZV7Y_;kKonE~%7_8QVP^|O{{*uxq#t-dsA?Q!F&$30Q)afAVkK|iwdhV~h*U)9=G z?KRe^)Xn9_7yb2Re|^qp_YC6*#Y(~En^ju;-MP7u7PSuWdCu)Q_6=d0o0yf zwx`w;%Z;z~vt--&V!83$*XAf%1Y->_ zNg+UpvApDhyA=m<$3^g3kkjESKNbL$;`Toav5gG-2|%1GxJr4nOWWYoju*A#KyPFS zyVu25949b=y)&QdggEG!r8!o(O+1QN5IB`jntX&NwC$QBR+5r$_vIFdo%umfzRlm_ zoD^T{4}pbT5`f3L?FUpuwa81=FO9BX&amY;!6|QnmGNu|(W>eTSvxo3O%yr+u%g`} z0t`Brl;e-oCEI;WD-pbg6Ei;g5rNt)Bf)8eTujJG(iE``X91zM#uR&#WTeFHfa6vY zlqm|%etL3An+l*n+aL6U@V1@m%W{i70NQ*jcG(MsnD6MxX}7tM`+-~qh4o2Os_ccM zT~A0|qd7SClfYQKI>%Phv@cqFquVI6D*0cJ@}(GCad#2PK~fY3ZR12dJHcIwUr40* z1@w^bU{RNI&g8r(Lm}FDAW3;%+h6b-eqYbZCt@lw1iWXj0rp%< zh@3B1O|ZZuc#!(a{(5$~^xrVOHmm>2MAFjay+HgwXv(Nld#!EDgNm^2sSb>(12(3r z;0kh;Vu6&s+{pO)_k7pT>vrapz1d^FX`)_kyo$~gW7XH@dId!CkJD>!8c`Sf$-%DI z*k-WT#VIcKU!CisBWLE=T0u-))Wvy*a#u?n@yassY5XZ7s37kva*!mgo8;+;+3&Xa(JlEK-a=Af{Q_ zZlibPuq$SvXzl5$d=kn|e`JFy2Y+nYm-HWo`TVEgfY)ZPgHCpfLipPqtIXI`Oia|> zZo$VSU7P(>CXBUh7~o^7V?O<)N#j*E|2t}P%*;+}u6q19UpZsQsp;6}NS&pmspwM- z=WQtZ;P|w@$=wk$XtYZt%Qj+z!Ycw&9rGr6I+yK{mmp0 zDbb(DmhJ~1{W`$G0OM}-PNnf(PRg-$dq5{y`t20q_YsuEewj0k*NN{(4!?zf!ASr- z+UZIi{s5#>u3Lm9_Ycs>xy9LRU_qY28djO@*3D;!a#5xJjti|g9x&LM+W4Y#o;MDm z^#p&|zeq)It=M0`-WhBW!}Q~6t>P_fb&xIHOL94OY$KoFYW;)cw5!_;;RZ(<7!&oI zzyMrH4$P`e>>ou$SyY>0k+P@72 z=%9|9`tG?FP1+chTLK=6uf2}R1IENZ8cMuG>nfvShIn)-l3Z93RV-ZcjGR%?vSW~DmYLZqV5Ha{djPpU^e9&<|Xal~%n0wW`Yz5a4 zM-5+^TN1ZlUDo``X#|9FsN_7%^aS|`)B%Bi-xIV!?sv+KAJL-&C~>6|3Qfc7Y6#fr zCzu5Nx!@bfuVMt}`&}Kuz+0{k^k1tX;-fOAWyEm~Q7z?yfa>0X;1=vZ!6gm#Ksiq!P?l*}1O@IK0c*|86?VO9G^AGJ){kI zl*13km0VaTU0r#09d;35h+&gChYA9k+=hGII{Q?6sgoDRUZzreSLm+2n@7cJFFL!$ zZ6hsiQM`=wXRrrUcD@&si(1c#Y?Ts;Z$rqHxy6;=?xOL(Ez>flKnk^A!u8dgZ(eus@J;JhZCF>mY1R7CbsJXQ@{x^)@Q!V$-m+%h`kM}Ohon+!J=7Oh z|LnUpE`sn1*;}Pred{bLHN0wwlUpkq-bNt{G8{wFT%Y=Lq%EeF_i8_98 z_@T@9YbPT!T_0oa{GQ3<>4&)ZS~;H<^v&6?|K6QDlu21x_77kAgU;}kf7HpQ^DTS& zf9v^Af30}qwmPy zl#A9lfY%oOSrkNOPEn37$n>j9b)B_-t`EvJY*+p1>PWwoF}}voQasZ+OQ9KHYFND{cJ+JvYZ=3 ztt$t0S+lO@^eZjxlFK>DQr?ZB(31Cl^6HZk&34_)HNb26*LxkQH*zVjdSO#2l22Dq z`bCxg*^+yJO3+^l_iDRfmBlfBCcq}^lcYOB`Uz{bzQ5ISndls0QFs!Cvd-{!6CZ1E z*9O8q3f7JSNpgj$E-+47u*Klq6_>3GA5~+5EBSslyj!eq^!W~5@OTp3&OmKR&P&@m zcOMDQ?PXky?aSeM(E4bIe$l0ux 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Equilibrate solutions with phases, exchangers, surfaces + */ + last_model.force_prep = true; + rate_sim_time_start = 0; + for (advection_step = 1; advection_step <= count_ad_shifts; + advection_step++) + { + log_msg(sformatf( + "\nBeginning of advection time step %d, cumulative pore volumes %f.\n", + advection_step, + (double) (((LDBLE) advection_step) / + ((LDBLE) count_ad_cells)))); + if (pr.use == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "Beginning of advection time step %d, cumulative pore volumes %f.\n", + advection_step, + (double) (((LDBLE) advection_step) / + ((LDBLE) count_ad_cells)))); + } +/* + * Advect + */ + for (i = count_ad_cells; i > 0; i--) + { + //solution_duplicate(i - 1, i); + Utilities::Rxn_copy(Rxn_solution_map, i -1, i); + } +/* + * Equilibrate and (or) mix + */ + for (int i = 1; i <= count_ad_cells; i++) + { + set_initial_moles(i); + cell_no = i; + set_advection(i, TRUE, TRUE, i); + run_reactions(i, kin_time, TRUE, 1.0); + if (advection_kin_time_defined == TRUE) + { + rate_sim_time = rate_sim_time_start + kin_time; + } + log_msg(sformatf( "\nCell %d.\n\n", i)); + if (pr.use == TRUE && pr.all == TRUE && + advection_step % print_ad_modulus == 0 && + advection_print[(size_t)i - 1] == TRUE) + { + output_msg(sformatf( "\nCell %d.\n\n", i)); + } + if (advection_step % punch_ad_modulus == 0 && + advection_punch[(size_t)i - 1] == TRUE) + { + punch_all(); + } + if (advection_step % print_ad_modulus == 0 && + advection_print[(size_t)i - 1] == TRUE) + { + print_all(); + } + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + //solution_duplicate(-2, i - 1); + saver(); + } + Utilities::Rxn_copy(Rxn_solution_map, -2, count_ad_cells); + //solution_duplicate(-2, count_ad_cells); + rate_sim_time_start += kin_time; + } + initial_total_time += rate_sim_time_start; + /* free_model_allocs(); */ + mass_water_switch = FALSE; + return (OK); +} diff --git a/phreeqcpp/basicsubs.cpp b/phreeqcpp/basicsubs.cpp new file mode 100644 index 00000000..758112f9 --- /dev/null +++ b/phreeqcpp/basicsubs.cpp @@ -0,0 +1,4080 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include "Utils.h" +#include "NameDouble.h" +#include "PBasic.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "Parser.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +activity(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE a; + + s_ptr = s_search(species_name); + if (s_ptr == s_h2o) + { + a = pow((LDBLE) 10., s_h2o->la); + } + else if (s_ptr == s_eminus) + { + a = pow((LDBLE) 10., s_eminus->la); + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + a = 1e-99; + } + else + { + a = pow((LDBLE) 10., s_ptr->lm + s_ptr->lg); + } + return (a); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +activity_coefficient(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE g, dum = 0.0; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF))) + { + if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk) + dum = log10(s_ptr->equiv / s_ptr->alk); + g = pow((LDBLE) 10., s_ptr->lg - dum); + } + else + { + g = 0; + } + return (g); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_activity_coefficient(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE g, dum = 0.0; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF))) + { + if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk) + dum = log10(s_ptr->equiv / s_ptr->alk); + g = s_ptr->lg - dum; + } + else + { + g = 0; + } + return (g); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +aqueous_vm(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS) + { + g = s_ptr->logk[vm_tc]; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +phase_vm(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + class phase *phase_ptr; + int l; + LDBLE g; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + g = 0.0; + } + else + { + g = phase_ptr->logk[vm0]; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sa_declercq(double sa_type, double Sa, double d, double m, double m0, double gfw) +/* ---------------------------------------------------------------------- */ +{ + if (sa_type == 0) + { + // surface-area-calculation-Fixed_Surface + return Sa; + } + else if (sa_type == 1) + // surface-area-calculation-Square + { + double mass0 = m0 * gfw; + double V0 = mass0 / d; + double St0 = mass0 * Sa; // total surface + double a0 = pow(V0, 1.0/3.0); // side length + double Sp0 = 6.0 * a0*a0; // surface particle + double np = St0 / Sp0; // number of particles + double RATS = Sa / St0; + double mass = m * gfw; + double V = mass / d; + double a = pow(V, 1.0/3.0); + double St = 6.0 * a*a*np; + return St * RATS; // total current surface + } + else if (sa_type == 2) + { + //double pi = 3.14159265359; + double mass0 = m0 * gfw; + double V0 = mass0 / d; // volume + double St0 = mass0 * Sa; // total surface + double a0 = pow(3.0 * V0/(4.0 * pi), 1.0/3.0); // ((3*V0)/(4 * 3.14159265359))^(1/3) + double Sp0 = (4.0 * pi) * a0 * a0; // surface particle + double np = St0 / Sp0; // number of particles + double RATS = Sa / St0; + + double mass = m * gfw; + double V = mass / d; + double a = pow(3.0 * V/(4.0 * pi), 1.0/3.0); //((3*V)/(4 * 3.14159265359))^(1/3) + double St = 4.0 * pi * a * a * np; + return St * RATS; // total current surface + } + error_string = sformatf( "Unknown surface area type in SA_DECLERCQ %d.", (int) sa_type); + error_msg(error_string, CONTINUE); + input_error++; + return (MISSING); + +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +diff_c(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL /*&& s_ptr->in != FALSE && s_ptr->type < EMINUS*/) + { + g = s_ptr->dw; + if (s_ptr->dw_t) + g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15); + g *= viscos_0_25 / viscos; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +setdiff_c(const char *species_name, double d) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL) + { + s_ptr->dw = d; + g = s_ptr->dw; + if (s_ptr->dw_t) + g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15); + g *= viscos_0_25 / viscos; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_SC(void) +/* ---------------------------------------------------------------------- */ +{ + //int i; + //LDBLE lm, a, l_z, Dw, SC, ff; + + //SC = 0; +# ifdef SKIP + for (i = 0; i < count_species_list; i++) + { + if (species_list[i].s->type == EX) + continue; + if (species_list[i].s->type == SURF) + continue; + if (i > 0 + && strcmp(species_list[i].s->name, + species_list[i - 1].s->name) == 0) + continue; + if (species_list[i].s == s_h2o) + continue; + if ((Dw = species_list[i].s->dw) == 0) + continue; + if ((l_z = fabs(species_list[i].s->z)) == 0) + continue; + + lm = species_list[i].s->lm; + if (lm > -9) + { + ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z); + + ff *= species_list[i].s->lg; + if (ff > 0) ff = 0; + a = under(lm + ff); + if (species_list[i].s->dw_t) + Dw *= exp(species_list[i].s->dw_t / tk_x - species_list[i].s->dw_t / 298.15); // the viscosity multiplier is done in SC + SC += a * l_z * l_z * Dw; + } + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos; + + return (SC); +//# endif + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((Dw = s_x[i]->dw) == 0) + continue; + if ((l_z = fabs(s_x[i]->z)) == 0) + continue; + + lm = s_x[i]->lm; + if (lm > -9) + { + ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z); + + ff *= s_x[i]->lg; + if (ff > 0) ff = 0; + a = under(lm + ff); + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC + SC += a * l_z * l_z * Dw; + } + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos; + + return (SC); +# endif + int i; + LDBLE ka, l_z, Dw, ff, sqrt_mu; + sqrt_mu = sqrt(mu_x); + + SC = 0; + //LDBLE ta1, ta2, ta3, ta4; + for (i = 0; i < (int)this->s_x.size(); i++) + { + // ** for optimizing, get numbers from -analyt for H+ = H+... + //if (!strcmp(s_x[i]->name, "H+")) + //{ + // ta1 = s_x[i]->logk[2]; + // ta2 = s_x[i]->logk[3]; + // ta3 = s_x[i]->logk[4]; + // ta4 = s_x[i]->logk[5]; + // break; + //} + // + } + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((Dw = s_x[i]->dw) == 0) + { + if (correct_Dw) + Dw = default_Dw; + else + continue; + } + if ((l_z = fabs(s_x[i]->z)) == 0) + l_z = 1; // only a 1st approximation for correct_Dw in electrical field + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC + if (s_x[i]->dw_a2) + ka = DH_B * s_x[i]->dw_a2 * sqrt_mu / (1 + pow(mu_x, 0.75)); + else + { + ka = DH_B * 4.73 * sqrt_mu / (1 + pow(mu_x , 0.75)); + //ka = DH_B * ta1 * sqrt_mu / (1 + pow(mu_x, ta2)); + //ka = DH_B * ta1 * sqrt_mu / (1 + mu_x / ta2); + } + if (s_x[i]->dw_a) + { + ff = exp(-s_x[i]->dw_a * DH_A * l_z * sqrt_mu / (1 + ka)); + if (print_viscosity) + { + ff *= pow((viscos_0 / viscos), s_x[i]->dw_a_visc); + } + } + else + { + ff = exp(-1.6 * DH_A * l_z * sqrt_mu / (1 + ka)); + //ff = exp(-ta3 * DH_A * l_z * sqrt_mu / (1 + ka)); + } + Dw *= ff; + s_x[i]->dw_corr = Dw; + + if (s_x[i]->z == 0) + continue; + s_x[i]->dw_t_SC = s_x[i]->moles / mass_water_aq_x * l_z * l_z * Dw; + SC += s_x[i]->dw_t_SC; + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos_0; + return (SC); +} +#ifdef SKIP +/*Debye-Onsager according to Robinson and Stokes, 1954, JACS 75, 1991, + but with sqrt charge multiplier for B2 and mu^ff dependent ka */ + LDBLE q, B1, B2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, Sum_m_dw, z_plus, z_min, t1, t2, Dw_SC; + + m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = Sum_m_dw = z_plus = z_min = 0; + SC = 0; + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((l_z = s_x[i]->z) == 0) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if ((Dw = s_x[i]->dw) == 0) + Dw = 1e-9; + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier cancels in q... + if (l_z > 0) + { + m_plus += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_plus += t1; + eq_dw_plus += t1 * Dw; + Sum_m_dw += s_x[i]->moles * Dw; + } + else + { + m_min += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_min -= t1; + eq_dw_min -= t1 * Dw; + Sum_m_dw += s_x[i]->moles * Dw; + } + } + // Falkenhagen, q = (Sum(z1 * m1*Dw1) + Sum(z2 *m2*Dw2)) / ((Sum(m1*Dw1) + Sum(m2*Dw2))(av_z1 + av_z2)) + z_plus = eq_plus / m_plus; // |av_z1| + z_min = eq_min / m_min; // |av_z2| + q = (eq_dw_plus + eq_dw_min) / (Sum_m_dw * (z_min + z_plus)); + t1 = 1.60218e-19 * 1.60218e-19 / (6 * pi); + B1 = t1 / (2 * 8.8542e-12 * eps_r * 1.38066e-23 * tk_x) * + q / (1 + sqrt(q)) * DH_B * 1e10 * z_plus * z_min; // DH_B is per Angstrom (*1e10) + t2 = viscos_0; // (1 - 0.5) * viscos_0 + 0.5 * viscos; + B2 = t1 * AVOGADRO / t2 * DH_B * 1e17; // DH_B per Angstrom (*1e10), viscos in mPa.s (*1e3), B2 in cm2 (*1e4) + + Dw_SC = viscos_0_25 / t2 * 1e4 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((l_z = fabs(s_x[i]->z)) == 0) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if ((Dw = s_x[i]->dw) == 0) + Dw = 1e-9; + + Dw *= Dw_SC; + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity factor is in Dw_SC + a = (s_x[i]->dw_a ? s_x[i]->dw_a : 3.5); + ka = DH_B * a; + if (s_x[i]->dw_a2) + ka *= pow((double) mu_x, s_x[i]->dw_a2); + else + ka *= sqrt_mu; + + // Falkenhagen... + //SC += under(lm) * l_z * l_z * (Dw - B2 * l_z * sqrt_mu / (1 + ka)) * (1 - B1 * sqrt_mu / + // ((1 + ka) * (1 + ka * sqrt(q) + ka * ka / 6))); + + t1 = (Dw - (B1 * Dw + B2) * sqrt_mu / (1 + ka)); + //t1 = (Dw - (B1 * Dw + B2 * sqrt(l_z)) * sqrt_mu / (1 + ka)); + //t1 = (Dw - (B1 * Dw + B2 * l_z * l_z) * sqrt_mu / (1 + ka)); + if (t1 > 0) + SC += under(lm) * l_z * l_z * t1; + } + return (SC * 1e3); +#endif + +/* VP: Density Start */ +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_dens(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates density based on the formulas and parameters from Millero, + * 2000. + * + * Millero, F.J. (2000) The equation of state of lakes. Aquatic geochemistry + * volume 6, pages 1-17 + */ + int i; + LDBLE M_T, /*rho_new,*/ gfw; + /* 2 options: original VP, assign the volumes of species with zero molar volume to their master species, + but this doubles counts of complexes with -Vm defined. And, cation-OH and H-anion + complexes are counted once. Also, must add H+ and OH-... */ + //class species *s_ptr; + + //V_solutes = M_T = 0.0; + //for (i = 0; i < count_species_list; i++) + //{ + // if (species_list[i].s->type != AQ && species_list[i].s->type != HPLUS) + // continue; + + // //if (species_list[i].master_s->secondary != NULL) + // // gfw = species_list[i].master_s->secondary->gfw; + // //else + // // gfw = species_list[i].master_s->primary->gfw; + + // /* OH-... */ + // if (!strcmp(species_list[i].s->name, "OH-")) + // s_ptr = s_search("OH-"); + // else if (species_list[i].s->logk[vm_tc] == 0) + // { + // s_ptr = species_list[i].master_s; + // if (s_ptr->secondary) + // gfw = s_ptr->secondary->gfw; + // else + // gfw = s_ptr->primary->gfw; + // } + // else + // { + // s_ptr = species_list[i].s; + // compute_gfw(species_list[i].s->name, &gfw); + // } + + // /* Special case: CO3-2 species */ + // if (strcmp(s_ptr->name, "CO3-2") == 0) + // { + // if (strstr(species_list[i].s->name, "HCO3") != NULL) + // { + // s_ptr = s_search("HCO3-"); + // } else if (strstr(species_list[i].s->name, "CO3") != NULL) + // { + // compute_gfw("CO3-2", &gfw); + // } + // } + // if (!gfw || !strcmp(species_list[i].s->name, "CO2")) /* CO2, H+ and OH- */ + // compute_gfw(species_list[i].s->name, &gfw); + + // M_T += (species_list[i].s->moles * gfw); + // V_solutes += species_list[i].s->moles * s_ptr->logk[vm_tc]; + //} + + /* 2nd option, use species_x, vm = 0 for complexes with undefined volume... */ + V_solutes = M_T = 0.0; + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + + //compute_gfw(s_x[i]->name, &gfw); + gfw = s_x[i]->gfw; + M_T += s_x[i]->moles * gfw; + V_solutes += s_x[i]->moles * s_x[i]->logk[vm_tc]; + } + /* If pure water then return rho_0 */ + if (M_T == 0) + return rho_0; + else + return rho_0 * (1e3 + M_T / mass_water_aq_x) / (rho_0 * V_solutes / mass_water_aq_x + 1e3); + + //M_T /= 1e3; + //solution_mass = mass_water_aq_x + M_T; + //V_solutes = M_T - rho_0 * V_solutes / 1e3; + + //rho_new = halve(f_rho, 0.5, 2.0, 1e-7); + //if (!PHR_ISFINITE(rho_new) || rho_new > 1.99999) rho_new = 1.99999; + + //return rho_new; +} +/* VP: Density End */ +/* DP: Function for interval halving */ + +LDBLE Phreeqc:: +f_rho(LDBLE rho_old, void *cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE rho = 1.0; + Phreeqc * pThis; + + pThis = (Phreeqc *) cookie; + + pThis->solution_volume = pThis->solution_mass / rho_old; + if (pThis->solution_volume != 0) + { + rho = pThis->V_solutes / pThis->solution_volume; + } + rho = rho + pThis->rho_0; + return (rho - rho_old); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_solution_volume(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates solution volume based on sum of mass of element plus density + */ + LDBLE total_mass = 0; + LDBLE gfw; + //compute_gfw("H", &gfw); + gfw = s_hplus->primary->gfw; + total_mass = total_h_x * gfw; + //compute_gfw("O", &gfw); + gfw = s_h2o->primary->gfw; + total_mass += total_o_x * gfw; + + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->s->type != AQ) continue; + class master *master_ptr = master[i]; + if (master_ptr->primary == TRUE && strcmp(master_ptr->elt->name, "Alkalinity")) + { + total_mass += master_ptr->total_primary * master_ptr->elt->gfw; + } + } + LDBLE rho = calc_dens(); + LDBLE vol = 1e-3 * total_mass / rho; + return (vol); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_n(const char *name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + int i; + LDBLE lk; + class logk *logk_ptr; + LDBLE l_logk[MAX_LOG_K_INDICES]; + class name_coef add_logk; + std::vector add_logk_v; + + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + strcpy(token, name); + logk_ptr = logk_search(token); + if (logk_ptr != NULL) + { + add_logk.name = token; + add_logk.coef = 1.0; + add_logk_v.push_back(add_logk); + add_other_logk(l_logk, add_logk_v); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + return (lk); + } + return (-999.99); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_p(const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + char token[MAX_LENGTH]; + class phase *phase_ptr; + LDBLE lk=-999.9; + LDBLE l_logk[MAX_LOG_K_INDICES]; + + strcpy(token, name); + phase_ptr = phase_bsearch(token, &j, FALSE); + + if (phase_ptr != NULL) + { + CReaction* reaction_ptr; + if (phase_ptr->replaced) + reaction_ptr = &phase_ptr->rxn_s; + else + reaction_ptr = &phase_ptr->rxn; + /* + * Print saturation index + */ + reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) - + phase_ptr->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + //lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM); + select_log_k_expression(reaction_ptr->logk, l_logk); + add_other_logk(l_logk, phase_ptr->add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + } + return (lk); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_s(const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + char token[MAX_LENGTH]; + class species *s_ptr; + LDBLE lk, l_logk[MAX_LOG_K_INDICES]; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + //if (s_ptr->logk[vm_tc]) + /* calculate delta_v for the reaction... */ + s_ptr->logk[delta_v] = calc_delta_v(*&s_ptr->rxn, false); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + select_log_k_expression(s_ptr->logk, l_logk); + mu_terms_in_logk = true; + add_other_logk(l_logk, s_ptr->add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + return (lk); + } + return (-999.99); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +dh_a0(const char* name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + class species* s_ptr; + double a = -999.99; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + a = s_ptr->dha; + } + return (a); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +dh_bdot(const char* name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + class species* s_ptr; + double b = -999.99; + if (llnl_temp.size() > 0) + { + b = bdot_llnl; + } + else + { + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + b = s_ptr->dhb; + } + } + return (b); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_deltah_p(const char* name) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + char token[MAX_LENGTH]; + class phase* phase_ptr; + LDBLE lkm, lkp; + LDBLE l_logk[MAX_LOG_K_INDICES]; + double dh = -999.99; + strcpy(token, name); + phase_ptr = phase_bsearch(token, &j, FALSE); + + if (phase_ptr != NULL) + { + CReaction* reaction_ptr; + if (phase_ptr->replaced) + reaction_ptr = &phase_ptr->rxn_s; + else + reaction_ptr = &phase_ptr->rxn; + /* + * Print saturation index + */ + reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) - + phase_ptr->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + //lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM); + select_log_k_expression(reaction_ptr->logk, l_logk); + add_other_logk(l_logk, phase_ptr->add_logk); + lkm = k_calc(l_logk, tk_x - 1.0, patm_x * PASCAL_PER_ATM); + lkp = k_calc(l_logk, tk_x + 1.0, patm_x * PASCAL_PER_ATM); + dh = (lkp - lkm) / 2.0 * LOG_10 * R_KJ_DEG_MOL * pow(tk_x, 2.0); + } + return (dh); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_deltah_s(const char* name) +/* ---------------------------------------------------------------------- */ +{ + int i; + char token[MAX_LENGTH]; + class species* s_ptr; + LDBLE lkm, lkp, l_logk[MAX_LOG_K_INDICES]; + double dh = -999.99; + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + /* calculate delta_v for the reaction... */ + s_ptr->logk[delta_v] = calc_delta_v(*&s_ptr->rxn, false); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + select_log_k_expression(s_ptr->logk, l_logk); + mu_terms_in_logk = true; + add_other_logk(l_logk, s_ptr->add_logk); + lkm = k_calc(l_logk, tk_x-1.0, patm_x * PASCAL_PER_ATM); + lkp = k_calc(l_logk, tk_x + 1.0, patm_x * PASCAL_PER_ATM); + dh = (lkp - lkm) / 2.0 * LOG_10 * R_KJ_DEG_MOL * pow(tk_x,2.0); + return (dh); + } + return (0.0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_surface_charge(const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH], token1[MAX_LENGTH]; + const char* cptr; + int i, j, k; + LDBLE charge; + class rxn_token_temp *token_ptr; + class master *master_ptr; + /* + * Go through species, sum charge + */ + charge = 0; + for (k = 0; k < (int)this->s_x.size(); k++) + { + if (s_x[k]->type != SURF) + continue; + /* + * Match surface_name + */ + count_trxn = 0; + trxn_add(s_x[k]->rxn_s, 1.0, false); /* rxn_s is set in tidy_model */ + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s->type != SURF) + continue; + master_ptr = trxn.token[i].s->primary; + strcpy(token, master_ptr->elt->name); + replace("_", " ", token); + cptr = token; + copy_token(token1, &cptr, &j); + if (strcmp(surface_name, token1) == 0) + { + charge += s_x[k]->moles * s_x[k]->z; + } + } + } + return (charge); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +diff_layer_total(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in DDL layer + */ + cxxSurfaceCharge *surface_charge_ptr1; + std::string name, token, surface_name_local; + class master *master_ptr; + + LDBLE mass_water_surface; + LDBLE molality, moles_excess, moles_surface, charge; + + if (use.Get_surface_ptr() == NULL || (dl_type_x == cxxSurface::NO_DL && + strcmp_nocase("psi", total_name) != 0 && + strcmp_nocase("psi1", total_name) != 0 && + strcmp_nocase("psi2", total_name) != 0 && + strcmp_nocase("charge", total_name) != 0 + && strcmp_nocase("charge1", + total_name) != 0 + && strcmp_nocase("charge2", + total_name) != 0 + && strcmp_nocase("sigma", + total_name) != 0 + && strcmp_nocase("sigma1", + total_name) != 0 + && strcmp_nocase("sigma2", + total_name) != 0)) + return (0); + +/* + * Find surface... + */ + int j; + for (j = 0; j < count_unknowns; j++) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else + { + if (x[j]->type != SURFACE) + continue; + token = x[j]->master[0]->elt->name; + Utilities::replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + CParser::copy_token(name, b, e); + } + if (surface_name != NULL) + { + if (strcmp(name.c_str(), surface_name) == 0) + break; + } + else + { + break; + } + } + if (j >= count_unknowns) + return (0); + surface_name_local = name; + /* + * Psi, charge, sigma + */ + if (strcmp_nocase("psi", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + return ((LDBLE) (x[j]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ)); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else + { + return (0); + } + } + else if (strcmp_nocase("psi1", total_name) == 0) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI1); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else if (strcmp_nocase("psi2", total_name) == 0) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI2); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else if (strcmp_nocase("charge", total_name) == 0) + { + if ((use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) && dl_type_x == cxxSurface::NO_DL) + { + return ((LDBLE) (x[j]->f)); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma0() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (calc_surface_charge(surface_name_local.c_str())); + } + } + else if (strcmp_nocase("charge1", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma1() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (0); + } + } + else if (strcmp_nocase("charge2", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma2() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (dl_type_x != cxxSurface::NO_DL) + { + charge = calc_surface_charge(surface_name_local.c_str()); + } + else + { + charge = x[j]->f; + } + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { + return ((charge * F_C_MOL / + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()))); + } + else + { + return (0); + } + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma0())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma1", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma1())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma2", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma2())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("water", total_name) == 0) + { + if (dl_type_x != cxxSurface::NO_DL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return (charge_ptr->Get_mass_water()); + } + else + { + return (0); + } + } +/* + * find total moles of each element in diffuse layer... + */ + surface_charge_ptr1 = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (surface_charge_ptr1) + { + mass_water_surface = surface_charge_ptr1->Get_mass_water(); + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + LDBLE g = surface_charge_ptr1->Get_g_map()[s_x[j]->z].Get_g(); + + moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl + + mass_water_surface / + mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + moles_surface = mass_water_surface * molality + moles_excess; +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + elt_list_combine(); +/* + * Return totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + return ((LDBLE) elt_list[j].coef); + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_t_sc(const char *name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + class species *s_ptr; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + if (!s_ptr->z) + return (0); + calc_SC(); + if (!SC) + return (0); + LDBLE t = s_ptr->dw_t_SC * 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0) * viscos_0_25 / viscos_0; + return (t / SC); + } + return (-999.99); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equi_phase(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + int j; + + if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL) + return (0); + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0) + { + break; + } + } +/* + * Print pure phase assemblage data + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (j == count_unknowns) + { + /* if not an unknown */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (strcmp_nocase + (it->second.Get_name().c_str(), phase_name) == 0) + { + return (it->second.Get_moles()); + } + } + } + else + { + /* if an unknown */ + if (x[j]->moles < 0.0) + x[j]->moles = 0.0; + return (x[j]->moles); + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equi_phase_delta(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + int j; + + if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL) + return (0); + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0) + { + break; + } + } +/* + * Print pure phase assemblage data + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (j == count_unknowns) + { + /* if not an unknown */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (strcmp_nocase + (it->second.Get_name().c_str(), phase_name) == 0) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + if (state != TRANSPORT && state != PHAST) + { + //LDBLE moles = it->second.Get_moles(); + //LDBLE delta_moles = moles - comp_ptr->Get_moles() - + // comp_ptr->Get_delta(); + return(0); + } + else + { + LDBLE moles = it->second.Get_moles(); + LDBLE delta_moles = moles - comp_ptr->Get_initial_moles(); + return(delta_moles); + } + } + } + } + else + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (state != TRANSPORT && state != PHAST) + { + LDBLE delta_moles = + x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta(); + return(delta_moles); + } + else + { + LDBLE delta_moles = + x[j]->moles - comp_ptr->Get_initial_moles(); + return(delta_moles); + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equivalent_fraction(const char *name, LDBLE *eq, std::string &elt_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr = s_search(name); + *eq = 0; + elt_name.clear(); + LDBLE f = 0; + if (s_ptr != NULL && (s_ptr->type == EX || s_ptr->type == SURF)) + { + *eq = s_ptr->equiv; + const class elt_list *next_elt; + LDBLE tot=0.0; + for (next_elt = &s_ptr->next_elt[0]; next_elt->elt != NULL; next_elt++) + { + if (next_elt->elt->master->s->type == SURF || + next_elt->elt->master->s->type == EX) + { + tot = total_mole(next_elt->elt->name); + elt_name = next_elt->elt->name; + } + } + if (s_ptr->in == TRUE && tot > 0.0) + { + f = s_ptr->moles * s_ptr->equiv / tot; + } + } + return f; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_comp(const char *gas_comp_name) +/* ---------------------------------------------------------------------- */ +{ + int i; + + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + if (strcmp_nocase(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), gas_comp_name) == 0) + { + class phase *phase_ptr = phase_bsearch(gas_comp_name, &i, false); + if (phase_ptr) + { + return (phase_ptr->moles_x); + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_p(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (0); + if (gas_unknown->moles < 1e-12) + return (0); + } + return (gas_phase_ptr->Get_total_p()); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_vm(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (0); + if (gas_unknown->moles < 1e-12) + return (0); + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (gas_phase_ptr->Get_v_m() >= 0.01) + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } + return gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles(); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_misc1(const char *ss_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0.0); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0) + { + if (ss_ptr->Get_miscibility()) + { + return (ss_ptr->Get_xb1()); + } + else + { + return (1.0); + } + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_misc2(const char *ss_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0.0); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0) + { + if (ss_ptr->Get_miscibility()) + { + return (ss_ptr->Get_xb2()); + } + else + { + return (1.0); + } + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_ss_comp(const char *ss_comp_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (strcmp_nocase(comp_ptr->Get_name().c_str(), ss_comp_name) == 0) + { + if (ss_ptr->Get_ss_in()) + { + return (comp_ptr->Get_moles()); + } + else + { + return (0.0); + } + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +get_calculate_value(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Gets value from a calclate_value structure + * arguments: name + * return: LDBLE of value + */ +{ + class calculate_value *calculate_value_ptr; + calculate_value_ptr = calculate_value_search(name); + if (calculate_value_ptr == NULL) + { + error_string = sformatf( "CALC_VALUE Basic function, %s not found.", + name); + //error_msg(error_string, CONTINUE); + //input_error++; + warning_msg(error_string); + return (MISSING); + } + if (name == NULL) + { + error_string = sformatf( + "Definition for calculated value not found, %s", name); + input_error++; + error_msg(error_string, CONTINUE); + return (MISSING); + } + + char l_command[] = "run"; + PBasic interp(this, this->phrq_io); + if (calculate_value_ptr->new_def == TRUE) + { + if (interp.basic_compile + (calculate_value_ptr->commands.c_str(), + &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + + if (interp.basic_run(l_command, + calculate_value_ptr->linebase, + calculate_value_ptr->varbase, + calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + return (calculate_value_ptr->value); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +kinetics_moles(const char *kinetics_name) +/* ---------------------------------------------------------------------- */ +{ + + if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL) + return (0); + for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0) + { + return (kinetics_comp_ptr->Get_m()); + } + } + + error_string = sformatf( "No data for rate %s in KINETICS keyword.", + kinetics_name); + //if (count_warnings >= 0) // appt debug cvode + // warning_msg(error_string); + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +kinetics_moles_delta(const char *kinetics_name) +/* ---------------------------------------------------------------------- */ +{ + + if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL) + return (0); + for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0) + { + //return (kinetics_comp_ptr->Get_m()); + + if (state != TRANSPORT && state != PHAST) + { + //LDBLE moles = kinetics_comp_ptr->Get_m(); + LDBLE delta_moles = - kinetics_comp_ptr->Get_moles(); + return delta_moles; + } + else + { + //moles = kinetics_comp_ptr->Get_m(); + LDBLE delta_moles = + kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles(); + return delta_moles; + } + } + } + + //error_string = sformatf( "No data for rate %s in KINETICS keyword.", + // kinetics_name); + //warning_msg(error_string); + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_activity(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE la; + + s_ptr = s_search(species_name); + + if (s_ptr == s_eminus) + { + la = s_eminus->la; + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + la = -99.99; + } + else if (s_ptr == s_h2o) + { + la = s_h2o->la; + } + else + { + la = s_ptr->lm + s_ptr->lg; + } + return (la); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_molality(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE lm; + + s_ptr = s_search(species_name); + + if (s_ptr == s_eminus) + { + lm = -99.99; + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + lm = -99.99; + } + else if (s_ptr == s_h2o) + { + lm = log10(s_ptr->moles / mass_water_aq_x); + } + else + { + lm = s_ptr->lm; + } + return (lm); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +molality(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + LDBLE m; + + s_ptr = s_search(species_name); + if (s_ptr == NULL || s_ptr == s_eminus || s_ptr->in == FALSE) + { + m = 1e-99; + } + else + { + /* m = pow(10., s_ptr->lm); */ + m = s_ptr->moles / mass_water_aq_x; + } + return (m); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pr_pressure(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + class phase *phase_ptr; + int l; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Gas %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE && phase_ptr->pr_in) + { + return phase_ptr->pr_p; + } + return (0.0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pressure(void) +/* ---------------------------------------------------------------------- */ +{ + return (patm_x); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pr_phi(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + class phase *phase_ptr; + int l; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Gas %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE && phase_ptr->pr_in) + { + return phase_ptr->pr_phi; + } + return (1.0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +saturation_ratio(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + class rxn_token *rxn_ptr; + class phase *phase_ptr; + int l; + LDBLE si, iap; + + iap = 0.0; + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Mineral %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE) + { + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = iap - phase_ptr->lk; + return (pow((LDBLE) 10.0, si)); + } + return (0.0); + +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +saturation_index(const char *phase_name, LDBLE * iap, LDBLE * si) +/* ---------------------------------------------------------------------- */ +{ + class rxn_token *rxn_ptr; + class phase *phase_ptr; + int l; + + *si = -99.99; + *iap = 0.0; + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Mineral %s, not found.", phase_name); + warning_msg(error_string); + *si = -99; + } + else if (phase_ptr->in != FALSE) + { + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + *iap += rxn_ptr->s->la * rxn_ptr->coef; + } + *si = *iap - phase_ptr->lk; + } + else + { + return (ERROR); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_gases(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE tot; + const class elt_list *next_elt; + + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + tot = 0; + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + class phase * phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), + &i, FALSE); + if (match_elts_in_species(phase_ptr->formula, mytemplate) == TRUE) + { + if (name == NULL) + { + tot += phase_ptr->moles_x; + } + else + { + for (next_elt = &phase_ptr->next_elt[0]; + next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * phase_ptr->moles_x; + break; + } + } + } + } + } + return (tot); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_species(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE tot; + const class elt_list *next_elt; + + count_elts = 0; + paren_count = 0; + tot = 0; + if (sum_species_map.find(mytemplate) == sum_species_map.end()) + { + std::vector species_list; + for (i = 0; i < (int)this->s_x.size(); i++) + { + class species *s_ptr = s_x[i]; + if (match_elts_in_species(s_ptr->name, mytemplate) == TRUE) + { + species_list.push_back(s_ptr->name); + } + } + sum_species_map[mytemplate] = species_list; + } + std::vector &species_list = (sum_species_map.find(mytemplate))->second; + for (size_t i=0; i < species_list.size(); i++) + { + class species *s_ptr = s_search(species_list[i].c_str()); + if (s_ptr->in == FALSE) continue; + if (name == NULL) + { + tot += s_ptr->moles; + } + else + { + for (next_elt = &s_ptr->next_elt[0]; next_elt->elt != NULL; + next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * s_ptr->moles; + break; + } + } + } + } + return (tot); +} + + + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_ss(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + LDBLE tot; + const class elt_list *next_elt; + + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + tot = 0; + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), mytemplate) == 0) + { + if (!ss_ptr->Get_ss_in()) + { + tot = 0; + break; + } + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (name == NULL) + { + tot += comp_ptr->Get_moles(); + } + else + { + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + for (next_elt = &phase_ptr->next_elt[0]; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * comp_ptr->Get_moles(); + break; + } + } + } + } + break; + } + } + return (tot); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +list_ss(std::string ss_name, cxxNameDouble &composition) +/* ---------------------------------------------------------------------- */ +{ + LDBLE tot = 0; + composition.clear(); + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name.c_str()) == 0) + { + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + composition.add(comp_ptr->Get_name().c_str(), comp_ptr->Get_moles()); + tot += comp_ptr->Get_moles(); + } + break; + } + } + return (tot); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +match_elts_in_species(const char *name, const char *mytemplate) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int i, i1, l, case_no, match; + char c, c1; + const char* cptr, *ptr1; + LDBLE d; + char token[MAX_LENGTH], equal_list[MAX_LENGTH]; + char token1[MAX_LENGTH], template1[MAX_LENGTH], equal_list1[MAX_LENGTH]; + char str[2]; + + strcpy(token, name); + squeeze_white(token); + replace("(+", "(", token); + if (strstr("token", "++") != NULL) + { + replace("++++++", "+6", token); + replace("+++++", "+5", token); + replace("++++", "+4", token); + replace("+++", "+3", token); + replace("++", "+2", token); + } + if (strstr("token", "--") != NULL) + { + replace("------", "-6", token); + replace("-----", "-5", token); + replace("----", "-4", token); + replace("---", "-3", token); + replace("--", "-2", token); + } + + cptr = token; + std::vector< std::pair > match_vector; + while ((c = *cptr) != '\0') + { + c1 = *(cptr + 1); + str[0] = c; + str[1] = '\0'; +/* + * New element + */ + if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '[')) + { + /* + * Get new element and subscript + */ + std::string element; + if (get_elt(&cptr, element, &l) == ERROR) + { + return (ERROR); + } + if (get_num(&cptr, &d) == ERROR) + { + return (ERROR); + } + std::pair pr(element, d); + match_vector.push_back(pr); + } + else + { + std::pair pr(str, 1.0); + match_vector.push_back(pr); + cptr += 1; + } + } + /* + * Replace elements with first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + cptr = template1; + while (extract_bracket(&cptr, equal_list) == TRUE) + { + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list from template + */ + std::string elt_name; + if (copy_token(elt_name, &ptr1) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + std::string replace_name = elt_name; + /* + * Replace in species all equivalent names from template + */ + while (copy_token(elt_name, &ptr1) != EMPTY) + { + for (i = 0; i < (int) match_vector.size(); i++) + { + if (elt_name == match_vector[i].first) + { + match_vector[i].first = replace_name; + } + } + } + } + /* + * Combine contiguous elements + */ + i1 = 0; + for (i = 1; i < (int) match_vector.size(); i++) + { + if ((isupper((int) (match_vector[i].first[0])) != FALSE) + && (match_vector[i].first == match_vector[i1].first)) + { + match_vector[i1].second += match_vector[i].second; + } + else + { + i1++; + match_vector[i1].first = match_vector[i].first; + match_vector[i1].second = match_vector[i].second; + } + } + int count_match_tokens = i1 + 1; + /* + * write out string + */ + token[0] = '\0'; + for (i = 0; i < count_match_tokens; i++) + { + strcat(token, match_vector[i].first.c_str()); + if (match_vector[i].second != 1.0) + { + sprintf(token1, "%g", (double) match_vector[i].second); + strcat(token, token1); + } + } + /* + * Write a template name using first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + cptr = template1; + while (extract_bracket(&cptr, equal_list) == TRUE) + { + strcpy(equal_list1, equal_list); + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list + */ + std::string elt_name; + if (copy_token(elt_name, &ptr1) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + replace(equal_list1, elt_name.c_str(), template1); + squeeze_white(template1); + cptr = template1; + } + /* + * Compare string + */ + /* Cases: 0 exact match + * 1 leading wild card + * 2 trailing wild card + * 3 leading and trailing wild card + */ + case_no = 0; + if (template1[0] == '*') + case_no = 1; + l = (int) strlen(template1); + if (template1[l - 1] == '*') + { + if (case_no != 1) + { + case_no = 2; + } + else + { + case_no = 3; + } + } + while (replace("*", "", template1)); + match = FALSE; + switch (case_no) + { + case 0: + /* exact match */ + if (strcmp(token, template1) == 0) + match = TRUE; + break; + case 1: + /* leading wild card */ + if ((cptr = strstr(token, template1)) == NULL) + { + match = FALSE; + } + else + { + if (strcmp(cptr, template1) == 0) + match = TRUE; + } + break; + case 2: + /* trailing wild card */ + if (strstr(token, template1) == token) + match = TRUE; + break; + case 3: + /* trailing wild card */ + if (strstr(token, template1) != NULL) + match = TRUE; + break; + } + return (match); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +extract_bracket(const char **string, char *bracket_string) +/* ---------------------------------------------------------------------- */ +{ + const char* cptr; + char *ptr1; + + if ((cptr = strstr(*string, "{")) == NULL) + return (FALSE); + strcpy(bracket_string, cptr); + if ((ptr1 = strstr(bracket_string, "}")) == NULL) + { + error_string = sformatf( + "No matching bracket (}) in isotope template string %s", + *string); + error_msg(error_string, CONTINUE); + input_error++; + return (FALSE); + } + ptr1[1] = '\0'; + *string = strstr(*string, "}"); + *string += 1; + return (TRUE); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +surf_total(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in LDBLE layer + */ + int j; + + if (use.Get_surface_ptr() == NULL || surface_name == NULL || total_name == NULL) + return (0); + + bool redox = false; + if (strstr(total_name, "(") != NULL) + { + redox = true; + } + if (!redox) + { + if (strcmp(total_name, "H") == 0 || strcmp(total_name, "O") == 0) + { + return surf_total_no_redox(total_name, surface_name); + } + } +/* + * Find surface... + */ + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + + std::string token; + token = x[j]->master[0]->elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + break; + } + if (j >= count_unknowns) + return (0); +/* + * find total moles for redox state + */ + LDBLE t = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type != SURF) + continue; + + std::string token; + bool match = false; + + // find if surface matches + for (int i = 0; s_x[j]->next_elt[i].elt != NULL; i++) + { + if (s_x[j]->next_elt[i].elt->master->type != SURF) continue; + + //strcpy(token, s_x[j]->next_elt[i].elt->name); + //replace("_", " ", token); + //cptr = token; + //copy_token(name, &cptr, &k); + token = s_x[j]->next_elt[i].elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + { + match = true; + break; + } + } + if (!match) continue; + + // surface matches, now match element or redox state + class rxn_token *rxn_ptr; + if (s_x[j]->mole_balance == NULL) + { + for (rxn_ptr = &s_x[j]->rxn_s.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->name; + } + else if (!redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->primary->elt->name; + } + else if (!redox && rxn_ptr->s->primary) + { + token = rxn_ptr->s->primary->elt->name; + } + else + { + continue; + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + else + // sum all sites in case total_name is a surface name without underscore surf ("Hfo_w", "Hfo") + { + if (rxn_ptr->s->type == SURF) + { + if (token.find("_") != std::string::npos) + { + token = token.substr(0, token.find("_")); + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + } + } + } + } + else + { + for (int i = 0; s_x[j]->next_secondary[i].elt != NULL; i++) + { + token = s_x[j]->next_secondary[i].elt->name; + if (strcmp(token.c_str(), total_name) == 0) + { + t += s_x[j]->next_secondary[i].coef * s_x[j]->moles; + break; + } + } + } + } + return t; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +surf_total_no_redox(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in LDBLE layer + */ + int i, j, k; + char name[MAX_LENGTH], token[MAX_LENGTH]; + char surface_name_local[MAX_LENGTH]; + const char* cptr; + + if (use.Get_surface_ptr() == NULL) + return (0); + +/* + * Find surface... + */ + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + strcpy(token, x[j]->master[0]->elt->name); + replace("_", " ", token); + cptr = token; + copy_token(name, &cptr, &k); + if (surface_name != NULL) + { + if (strcmp(name, surface_name) == 0) + break; + } + else + { + break; + } + } + if (j >= count_unknowns) + return (0); + strcpy(surface_name_local, name); +/* + * find total moles of each element in diffuse layer... + */ + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type != SURF) + continue; + for (i = 0; s_x[j]->next_elt[i].elt != NULL; i++) + { + if (s_x[j]->next_elt[i].elt->master->type != SURF) continue; + + strcpy(token, s_x[j]->next_elt[i].elt->name); + replace("_", " ", token); + cptr = token; + copy_token(name, &cptr, &k); + if (strcmp(name, surface_name_local) == 0) + { +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, s_x[j]->moles); + //fprintf(stderr, "%15s\t%e\t%s\t%s\n", s_x[j]->name, s_x[j]->moles, name, surface_name_local ); + break; + } + } + } + elt_list_combine(); +/* + * Return totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + return ((LDBLE) elt_list[j].coef); + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +total(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ + class master *master_ptr; + LDBLE t; + + if (strcmp(total_name, "H") == 0) + { + return (total_h_x / mass_water_aq_x); + } + if (strcmp(total_name, "O") == 0) + { + return (total_o_x / mass_water_aq_x); + } + std::string noplus = total_name; + replace(noplus, "+", ""); + master_ptr = master_bsearch(noplus.c_str()); + t = 0.0; + if (master_ptr == NULL) + { + if (strcmp_nocase(total_name, "water") == 0) + { + return (mass_water_aq_x); + } + else if (strcmp_nocase(total_name, "charge") == 0) + { + return (cb_x / mass_water_aq_x); + } +/* + sprintf (error_string, "Cannot find definition for master species, %s.", + total_name); + warning_msg (error_string); +*/ + } +/* + * Primary master species + */ + else if (master_ptr->primary == TRUE) + { + /* + * Not a redox element + */ + if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total / mass_water_aq_x; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (size_t i = master_ptr->number + 1; + (i < (int)master.size() && master[i]->elt->primary == master_ptr); + i++) + { + t += master[i]->total / mass_water_aq_x; + } + } + } +/* + * Secondary master species + */ + else + { + t = master_ptr->total / mass_water_aq_x; + } + return (t); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +total_mole(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ + class master *master_ptr; + LDBLE t; + + if (strcmp(total_name, "H") == 0) + { + return (total_h_x); + } + if (strcmp(total_name, "O") == 0) + { + return (total_o_x); + } + std::string noplus = total_name; + replace(noplus, "+", ""); + master_ptr = master_bsearch(noplus.c_str()); + t = 0.0; + if (master_ptr == NULL) + { + if (strcmp_nocase(total_name, "water") == 0) + { + return (mass_water_aq_x / gfw_water); + } + else if (strcmp_nocase(total_name, "charge") == 0) + { + return (cb_x); + } +/* + sprintf (error_string, "Cannot find definition for master species, %s.", + total_name); + warning_msg (error_string); +*/ + } +/* + * Primary master species + */ + else if (master_ptr->primary == TRUE) + { + /* + * Not a redox element + */ + if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (size_t i = master_ptr->number + 1; + (i < master.size() && master[i]->elt->primary == master_ptr); + i++) + { + t += master[i]->total; + } + } + } +/* + * Secondary master species + */ + else + { + t = master_ptr->total; + } + return (t); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_edl_species(cxxSurfaceCharge & charge_ref) +/* ---------------------------------------------------------------------- */ +{ + + double mass_water_surface = charge_ref.Get_mass_water(); + sys.clear(); + for (int j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type == H2O) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[j]->name); + sys[count_sys].moles = mass_water_surface / gfw_water; + sys_tot += sys[count_sys].moles; + count_sys++; + } + else if (s_x[j]->type < H2O) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + double molality = under(s_x[j]->lm); + double moles_excess = mass_water_aq_x * molality * charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + double moles_surface = mass_water_surface * molality + moles_excess; + sys[count_sys].name = string_duplicate(s_x[j]->name); + sys[count_sys].moles = moles_surface; + sys_tot += sys[count_sys].moles; + count_sys++; + } + else + { + continue; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +edl_species(const char *surf_name, LDBLE * count, char ***names, LDBLE ** moles, LDBLE * area, LDBLE * thickness) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + sys_tot = 0; + sys.clear(); + if (!(dl_type_x == cxxSurface::NO_DL)) + { + cxxSurface *surface_ptr = use.Get_surface_ptr(); + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge & charge_ref = surface_ptr->Get_surface_charges()[i]; + if (strcmp(charge_ref.Get_name().c_str(), surf_name) == 0) + { + get_edl_species(charge_ref); + *area = charge_ref.Get_specific_area() * charge_ref.Get_grams(); + *thickness = surface_ptr->Get_thickness(); + break; + } + } + } + /* + * Sort system species + */ + if (sys.size() > 1) + { + qsort(&sys[0], sys.size(), + sizeof(class system_species), system_species_compare); + } + /* + * malloc space + */ + *names = (char **) PHRQ_malloc((sys.size() + 1) * sizeof(char *)); + if (names == NULL) + malloc_error(); + *moles = (LDBLE *) PHRQ_malloc((sys.size() + 1) * sizeof(LDBLE)); + if (moles == NULL) + malloc_error(); + + (*names)[0] = NULL; + (*moles)[0] = 0; + for (i = 0; i < (int)sys.size(); i++) + { + (*names)[i + 1] = sys[i].name; + (*moles)[i + 1] = sys[i].moles; + } + *count = (LDBLE)sys.size(); + + //PHRQ_free(sys); + sys.clear(); + return (sys_tot); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +system_total(const char *total_name, LDBLE * count, char ***names, + char ***types, LDBLE ** moles, int isort) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + + sys_tot = 0; + sys.clear(); + if (strcmp_nocase(total_name, "elements") == 0) + { + system_total_elements(); + } + else if (strcmp_nocase(total_name, "phases") == 0) + { + system_total_si(); + } + else if (strcmp_nocase(total_name, "aq") == 0) + { + system_total_aq(); + } + else if (strcmp_nocase(total_name, "ex") == 0) + { + system_total_ex(); + } + else if (strcmp_nocase(total_name, "surf") == 0) + { + system_total_surf(); + } + else if (strcmp_nocase(total_name, "s_s") == 0) + { + system_total_ss(); + } + else if (strcmp_nocase(total_name, "gas") == 0) + { + system_total_gas(); + } + else if (strcmp_nocase(total_name, "equi") == 0) + { + system_total_equi(); + } + else if (strcmp_nocase(total_name, "kin") == 0) + { + system_total_kin(); + } + else + { + if (strstr(total_name, "(") == NULL) + { + system_total_elt(total_name); + } + else + { + system_total_elt_secondary(total_name); + } + } + /* + * Sort system species + */ + if (sys.size() > 1 && isort == 0) + { + qsort(&sys[0], sys.size(), + sizeof(class system_species), system_species_compare); + } + else if (sys.size() > 1) + { + qsort(&sys[0], sys.size(), + sizeof(class system_species), system_species_compare_name); + } + /* + * malloc space + */ + size_t count_sys = sys.size(); + *names = (char **) PHRQ_malloc((count_sys + 1) * sizeof(char *)); + if (names == NULL) + malloc_error(); + *types = (char **) PHRQ_malloc((count_sys + 1) * sizeof(char *)); + if (types == NULL) + malloc_error(); + *moles = (LDBLE *) PHRQ_malloc((count_sys + 1) * sizeof(LDBLE)); + if (moles == NULL) + malloc_error(); + + (*names)[0] = NULL; + (*types)[0] = NULL; + (*moles)[0] = 0; + for (i = 0; i < (int)count_sys; i++) + { + (*names)[i + 1] = sys[i].name; + (*types)[i + 1] = sys[i].type; + (*moles)[i + 1] = sys[i].moles; + } + *count = (LDBLE) count_sys; + if (strcmp_nocase(total_name, "elements") == 0) + { + sys_tot = 0;; + for (i = 0; i < (int)count_sys; i++) + { + if (strcmp(sys[i].type, "dis") == 0 && + strstr(sys[i].name, "(") == NULL && + strcmp(sys[i].name, "H") != 0 + && strcmp(sys[i].name, "O") != 0) + { + sys_tot += sys[i].moles; + } + } + } + //PHRQ_free(sys); + sys.clear(); + return (sys_tot); +} + +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +kinetics_formula(std::string kin_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of kinetic reactant + * Also returns arrays of elements and stoichiometry in stoichiometry + */ + stoichiometry.clear(); + std::string formula; + + if (use.Get_kinetics_ptr() == NULL) + return (formula); + std::vector comps = use.Get_kinetics_ptr()->Get_kinetics_comps(); + count_elts = 0; + paren_count = 0; + for (size_t i=0 ; i < comps.size(); i++) + { + cxxKineticsComp *comp_ptr = &comps[i]; + if (kin_name == comp_ptr->Get_rate_name().c_str()) + { + cxxNameDouble nd = comp_ptr->Get_namecoef(); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + // Try Phases + int l; + class phase *phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, it->second); + } + else + { + // add formula + std::string name = it->first; + LDBLE coef = it->second; + const char* cptr = &name[0]; + get_elts_in_species(&cptr, coef); + } + } + formula.append(kin_name); + //elt_list[count_elts].elt = NULL; + elt_list_combine(); + stoichiometry = elt_list_NameDouble(); + break; + } + } + return (formula); +} +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +phase_formula(std::string phase_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of mineral + * Also returns arrays of elements and stoichiometry in elts_arg and coef_arg + */ + stoichiometry.clear(); + std::string formula; + + int j; + class phase *phase_ptr = phase_bsearch(phase_name.c_str(), &j, FALSE); + if (phase_ptr != NULL) + { + formula.append(phase_ptr->formula); + cxxNameDouble nd(phase_ptr->next_elt); + stoichiometry = nd; + } + + return (formula); +} +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +species_formula(std::string phase_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of mineral + * Also returns arrays of elements and stoichiometry in elts_arg and coef_arg + */ + stoichiometry.clear(); + std::string formula; + formula = "none"; + class species *s_ptr = s_search(phase_name.c_str()); + if (s_ptr != NULL) + { + cxxNameDouble nd(s_ptr->next_elt); + stoichiometry = nd; + stoichiometry["charge"] = s_ptr->z; + if (s_ptr->type == EX) + { + formula = "ex"; + } + else if (s_ptr->type == SURF) + { + formula = "surf"; + } + else + { + formula = "aq"; + } + } + return (formula); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elements(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE t; + char name[MAX_LENGTH]; + class master *master_ptr; + + /* + * Include H and O + */ + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate("H"); + sys[count_sys].moles = total_h_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate("O"); + sys[count_sys].moles = total_o_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++;; + /* + * Include H(1) and O(-2) + */ + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate("H(1)"); + sys[count_sys].moles = solution_sum_secondary("H(1)"); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate("O(-2)"); + sys[count_sys].moles = solution_sum_secondary("O(-2)"); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + for (i = 0; i < (int)master.size(); i++) + { + master_ptr = master[i]; + if (master_ptr->primary == TRUE && master_ptr->total_primary <= 0) + continue; + if (master_ptr->in == FALSE + && (master_ptr->primary == FALSE + || master_ptr->total_primary == 0)) + continue; + /* + * H and O + */ + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + if (master_ptr->primary == TRUE) + { + if (master_ptr->total_primary > 0) + { + t = master_ptr->total_primary; + /* + * Not a redox element + */ + } + else if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (size_t j = master_ptr->number + 1; + master[j]->elt->primary == master_ptr; j++) + { + t += master[j]->total; + } + } + /* + * Secondary master species + */ + } + else + { + t = master_ptr->total; + } + strcpy(name, master[i]->elt->name); + count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = t; + sys_tot += sys[count_sys].moles; + if (master[i]->s->type <= SOLID) + { + sys[count_sys].type = string_duplicate("dis"); + } + else if (master[i]->s->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + } + else if (master[i]->s->type == SURF || master[i]->s->type == SURF_PSI) + { + sys[count_sys].type = string_duplicate("surf"); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_si(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE si, iap; + class rxn_token *rxn_ptr; + char name[MAX_LENGTH]; + + sys_tot = -999.9; + for (i = 0; i < (int)phases.size(); i++) + { + if (phases[i]->in == FALSE || phases[i]->type != SOLID) + continue; +/* + * Print saturation index + */ + iap = 0.0; + for (rxn_ptr = &phases[i]->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -phases[i]->lk + iap; + strcpy(name, phases[i]->name); + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = si; + if (si > sys_tot) + sys_tot = si; + sys[count_sys].type = string_duplicate("phase"); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_aq(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + //if (s_x[i]->type != AQ) + if (s_x[i]->type > HPLUS) + continue; + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("aq"); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_ex(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != EX) + continue; + if (s_x[i]->primary != NULL) + continue; + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("ex"); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_surf(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != SURF) + continue; + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("surf"); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), + &i, FALSE); + assert(phase_ptr); + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = phase_ptr->moles_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_equi(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Equilibrium phases + */ + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + std::map comps = use.Get_pp_assemblage_ptr()->Get_pp_assemblage_comps(); + std::map ::iterator it = comps.begin(); + for ( ; it != comps.end(); it++) + { + cxxPPassemblageComp *comp_ptr = &(it->second); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = equi_phase(sys[count_sys].name); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_kin(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Equilibrium phases + */ + if (use.Get_kinetics_ptr() == NULL) + return (OK); + std::vector comps = use.Get_kinetics_ptr()->Get_kinetics_comps(); + for (size_t i=0 ; i < comps.size(); i++) + { + cxxKineticsComp *comp_ptr = &comps[i]; + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(comp_ptr->Get_rate_name().c_str()); + sys[count_sys].moles = comp_ptr->Get_m(); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("kin"); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_ss(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t k = 0; k < ss_ptrs.size(); k++) + { + cxxSS *ss_ptr = ss_ptrs[k]; + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = comp_ptr->Get_moles(); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elt(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j, k; + LDBLE molality, moles_excess, moles_surface, mass_water_surface; + char name[MAX_LENGTH]; + +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + count_elts = 0; + paren_count = 0; + add_elt_list(s_x[i]->next_elt, s_x[i]->moles); + + elt_list_combine(); + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + if (s_x[i]->type == AQ) + { + sys[count_sys].type = string_duplicate("aq"); + } + else if (s_x[i]->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + /* subtract again the dummy moles of primary exchange species... */ + if (s_x[i]->primary != NULL) + { + sys_tot -= elt_list[j].coef; + } + } + else if (s_x[i]->type == SURF) + { + sys[count_sys].type = string_duplicate("surf"); + } + else if (s_x[i]->type == HPLUS) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_h_x; */ + } + else if (s_x[i]->type == H2O) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_o_x; */ + } + else + { + error_msg("System_total", STOP); + } + break; + } + } + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + /* + * Find position of component in surface charge data + */ + i = -1; + for (k = 0; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge); + i++; + /* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + mass_water_surface = charge_ptr->Get_mass_water(); + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + moles_excess = + mass_water_aq_x * molality * + (charge_ptr->Get_g_map()[s_x[j]->z].Get_g() * s_x[j]->erm_ddl + + mass_water_surface / mass_water_aq_x * (s_x[j]->erm_ddl - + 1)); + moles_surface = mass_water_surface * molality + moles_excess; + /* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + elt_list_combine(); + /* + * Print totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + strcpy(name, x[k]->master[0]->elt->name); + replace("_psi", "", name); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("diff"); + break; + } + } + } + } +/* + * find total moles in mineral phases + */ + if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP) + continue; + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_add_formula().size() > 0) + if (comp_ptr->Get_add_formula().size() > 0) + continue; + count_elts = 0; + paren_count = 0; + int j; + //class phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE); + class phase * phase_ptr = x[i]->phase; + add_elt_list(phase_ptr->next_elt, x[i]->moles); + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + break; + } + } + } + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t k = 0; k < ss_ptrs.size(); k++) + { + cxxSS *ss_ptr = ss_ptrs[k]; + if (ss_ptr->Get_ss_in()) + { + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_elt, + comp_ptr->Get_moles()); + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + break; + } + } + } + } + } + } +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + class phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[i].Get_phase_name().c_str(), &k, FALSE); + assert(phase_ptr); + if (phase_ptr->in == TRUE) + { + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_elt, phase_ptr->moles_x); + elt_list_combine(); + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + break; + } + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elt_secondary(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j, k, l; + LDBLE molality, moles_excess, moles_surface, mass_water_surface, sum, + coef; + char name[MAX_LENGTH]; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary.size() != 0) + { + add_elt_list(s_x[i]->next_secondary, s_x[i]->moles); + } + else + { + add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles); + } + elt_list_combine(); + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + if (s_x[i]->type == AQ) + { + sys[count_sys].type = string_duplicate("aq"); + } + else if (s_x[i]->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + } + else if (s_x[i]->type == SURF) + { + sys[count_sys].type = string_duplicate("surf"); + } + else if (s_x[i]->type == HPLUS) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_h_x; */ + } + else if (s_x[i]->type == H2O) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_o_x; */ + } + else + { + error_msg("System_total", STOP); + } + break; + } + } + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + /* + * Find position of component in surface charge data + */ + i = -1; + for (k = 0; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge); + i++; + /* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + mass_water_surface = charge_ptr->Get_mass_water(); + sum = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary.size() != 0) + { + add_elt_list(s_x[i]->next_secondary, 1); + } + else + { + add_elt_list(s_x[i]->next_sys_total, 1); + } + for (l = 0; l < count_elts; l++) + { + if (strcmp(elt_list[l].elt->name, total_name) == 0) + { + coef = elt_list[l].coef; + if (s_x[j]->type > H2O) + continue; + molality = under(s_x[j]->lm); + moles_excess = + mass_water_aq_x * molality * + charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + moles_surface = + mass_water_surface * molality + moles_excess; + sum += moles_surface * coef; + break; + } + } + if (l >= count_elts) + continue; + strcpy(name, x[k]->master[0]->elt->name); + replace("_psi", "", name); + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = sum; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("diff"); + break; + } + } + } +/* + * find total moles in mineral phases + */ + if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP) + continue; + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_add_formula().size() > 0) + if (comp_ptr->Get_add_formula().size() > 0) + continue; + count_elts = 0; + paren_count = 0; + int j; + //class phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE); + class phase * phase_ptr = x[i]->phase; + add_elt_list(phase_ptr->next_sys_total, x[i]->moles); + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + break; + } + } + } + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + if (ss_ptr->Get_ss_in()) + { + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_sys_total, + comp_ptr->Get_moles()); + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + break; + } + } + } + } + } + } +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + class phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE); + assert(phase_ptr); + if (phase_ptr->in == TRUE) + { + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_sys_total, + phase_ptr->moles_x); + + elt_list_combine(); + /* + * Look for element + */ + for (size_t j1 = 0; j1 < (size_t) count_elts; j1++) + { + if (strcmp(elt_list[j1].elt->name, total_name) == 0) + { + size_t count_sys = sys.size(); + sys.resize(count_sys + 1); + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j1].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solution_number(void) +/* ---------------------------------------------------------------------- */ +{ + Phreeqc * PhreeqcPtr = this; + int soln_no = -999; + if (PhreeqcPtr->state == TRANSPORT) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == PHAST) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == ADVECTION) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state < REACTION) + { + soln_no = PhreeqcPtr->use.Get_solution_ptr()->Get_n_user(); + } + else + { + if (PhreeqcPtr->use.Get_mix_in()) + { + soln_no = PhreeqcPtr->use.Get_n_mix_user(); + } + else + { + soln_no = PhreeqcPtr->use.Get_n_solution_user(); + } + } + return soln_no; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +solution_sum_secondary(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j; + LDBLE sum; +/* + * find total moles in aq, surface, and exchange + */ + sum = 0; + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > H2O) + continue; + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary.size() != 0) + { + add_elt_list(s_x[i]->next_secondary, s_x[i]->moles); + } + else + { + add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles); + } + elt_list_combine(); + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sum += elt_list[j].coef; + break; + } + } + } + return (sum); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_species_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class system_species *a, *b; + + a = (const class system_species *) ptr1; + b = (const class system_species *) ptr2; + if (a->moles < b->moles) + return (1); + if (a->moles > b->moles) + return (-1); + return (0); +} +int Phreeqc:: +system_species_compare_name(const void* ptr1, const void* ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class system_species* a, * b; + + a = (const class system_species*)ptr1; + b = (const class system_species*)ptr2; + return (strncmp(a->name, b->name, MAX_LENGTH)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_solids(cxxExchange *exchange_ptr, + cxxPPassemblage *pp_assemblage_ptr, + cxxGasPhase *gas_phase_ptr, + cxxSSassemblage *ss_assemblage_ptr, + cxxSurface *surface_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in solid phases + */ + count_elts = 0; + paren_count = 0; +/* + * find total moles in exchanger + */ + if (exchange_ptr != NULL) + { + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + add_elt_list(exchange_ptr->Get_exchange_comps()[i].Get_totals(), 1.0); + } + } + if (surface_ptr != NULL) + { + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + add_elt_list(surface_ptr->Get_surface_comps()[i].Get_totals(), 1.0); + } + } + if (ss_assemblage_ptr != NULL) + { + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + add_elt_list(phase_ptr->next_elt, + comp_ptr->Get_moles()); + } + } + } + if (gas_phase_ptr != NULL) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + int i; + class phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE); + add_elt_list(phase_ptr->next_elt, gas_phase_ptr->Get_gas_comps()[j].Get_moles()); + } + } + if (pp_assemblage_ptr != NULL) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + add_elt_list(phase_ptr->next_elt, + it->second.Get_moles()); + } + } + elt_list_combine(); + return (OK); +} + +LDBLE Phreeqc:: +iso_value(const char *total_name) +{ + int j; + char token[MAX_LENGTH]; + char my_total_name[MAX_LENGTH]; + strcpy(token, ""); + strcpy(my_total_name, total_name); + while (replace(" ","_",my_total_name)); + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(my_total_name, isotope_ratio[j]->name) != 0) + continue; + return (isotope_ratio[j]->converted_ratio); + } + strcpy(my_total_name, total_name); + while (replace("[","",my_total_name)); + while (replace("]","",my_total_name)); + strcat(token,"R("); + strcat(token,my_total_name); + strcat(token,")"); + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(token, isotope_ratio[j]->name) != 0) + continue; + return (isotope_ratio[j]->converted_ratio); + } + return -1000.; +} + +char * Phreeqc:: +iso_unit(const char *total_name) +{ + int j; + char token[MAX_LENGTH], unit[MAX_LENGTH]; + class master_isotope *master_isotope_ptr; + char my_total_name[MAX_LENGTH]; + strcpy(token, ""); + strcpy(my_total_name, total_name); + while (replace(" ","_",my_total_name)); + strcpy(unit, "unknown"); + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(my_total_name, isotope_ratio[j]->name) != 0) + continue; + master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name); + if (master_isotope_ptr != NULL) + { + strcpy(unit, master_isotope_ptr->units); + } + return string_duplicate(unit); + } + strcpy(my_total_name, total_name); + while (replace("[","",my_total_name)); + while (replace("]","",my_total_name)); + strcat(token,"R("); + strcat(token,my_total_name); + strcat(token,")"); + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(token, isotope_ratio[j]->name) != 0) + continue; + master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name); + if (master_isotope_ptr != NULL) + { + strcpy(unit, master_isotope_ptr->units); + } + return string_duplicate(unit); + } + return string_duplicate(unit); +} + +int Phreeqc:: +basic_compile(const char *commands, void **lnbase, void **vbase, void **lpbase) +{ + return this->basic_interpreter->basic_compile(commands, lnbase, vbase, lpbase); +} + +int Phreeqc:: +basic_run(char *commands, void *lnbase, void *vbase, void *lpbase) +{ + return this->basic_interpreter->basic_run(commands, lnbase, vbase, lpbase); +} + +void Phreeqc:: +basic_free(void) +{ + delete this->basic_interpreter; + this->basic_interpreter = NULL; +} + +#if defined(SWIG) || defined(SWIG_IPHREEQC) + +#include "BasicCallback.h" + +double Phreeqc:: +basic_callback(double x1, double x2, const char * str) +{ + if (this->basicCallback) + { + return this->basicCallback->Callback(x1, x2, str); + } + return 0.0; +} + +#else /* defined(SWIG) || defined(SWIG_IPHREEQC) */ + +#ifdef IPHREEQC_NO_FORTRAN_MODULE +double Phreeqc:: +basic_callback(double x1, double x2, char * str) +#else +double Phreeqc:: +basic_callback(double x1, double x2, const char * str) +#endif +{ + double local_x1 = x1; + double local_x2 = x2; + + if (basic_callback_ptr != NULL) + { + return (*basic_callback_ptr) (x1, x2, (const char *) str, basic_callback_cookie); + } + if (basic_fortran_callback_ptr != NULL) + { +#ifdef IPHREEQC_NO_FORTRAN_MODULE + return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str)); +#else + return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str)); +#endif + } + return 0; +} + +void +Phreeqc::register_basic_callback(double (*fcn)(double x1, double x2, const char *str, void *cookie), void *cookie1) +{ + this->basic_callback_ptr = fcn; + this->basic_callback_cookie = cookie1; +} +#ifdef IPHREEQC_NO_FORTRAN_MODULE +void +Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, char *str, size_t l)) +{ + this->basic_fortran_callback_ptr = fcn; +} +#else + +void +Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, const char *str, int l)) +{ + this->basic_fortran_callback_ptr = fcn; +} +#endif + +#endif /* defined(SWIG) || defined(SWIG_IPHREEQC) */ diff --git a/phreeqcpp/chart_icon.gif b/phreeqcpp/chart_icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..53bfb63abdc7c3d1d990ec5043b314b34fd1d5f8 GIT binary patch literal 2141 zcmX|=c~sJA7snrg)CvuZ6qJmLG;v9Z)U-@+#fY%nGA%1yB61tTGpFi$%&$-|Gy`TG>^90cS-Pi9q2v&j5 z03cupO(g>X<2guGh#(oeUxgv@FeKT~pbEoO$bt~Qt~^Mq6ql-?RvzA!C(}X@3=!1~ zA^1x1HhoDmR4m+3I;%+KF<=Z~5Ep8eA#H?Qj|NJj3hyCdJL$W`Lt01{$7V}a4*ig9 z9KwVtd94u9OMpyOsik7YephS+4DS_?5MHTX!Sq7}lVs_OFq@{sJyq@$JQ#Fl~o(4b#0OEKK=lApMfM6;NKDWY7M^Y;;=dZbl|BcfVTtx zB#_2ujiwuHZ;#7O0;c?%+MYc+Q?8Dqm#`Av8%$QmVH5Q}Uv{~OEI%DMXbbp#0Hz-f z;^qQi0bpS8ZyuHjz$EyI1^`*|e;yV^FZroy1F+yIFg*pZSiMIh@;U776xceq3m^Cb z@Lk`m$LH1E22=m?qq_aTT_s2g*7FnVw38+}ENhM*KJ^}8s{w0{?H9`72q4M@G}AV~ zllOp0Q-J0C^wh)Y@3}*s3?|*3rX^w1_#nrCbshj?09NZ~fh7QZ0>Bo4zAwcrkq#XA z`8)$o`M<=At@a1Pp<=Ka0cSpA!c5a@{vE6r8b~O9goY{Lh{Pwnaqf2JtSJ`Vj0~hA} zJz`rm+MK)a#+82ajFokT1L3cqo%K6GF3E6D*?Tdxo>n+m;)Z-y)V|=p@~cDqqiXtv z1grmkh#o?P{hs*u=Z{Qy+7iRX!Ac)rc?vU^Ocy1I7rbRm^G&n1u=AT->tYM;3{>$!Xp z0krQcWXlGuB6hpn+;PH;AEr*Iez83A)B)9FnZz+_dWuu%AL%XHnOa4i$2y#xgLqPN zbKIQx3T0$&q%6l)X)Y&sS~2TKaxa~t>kNI0%&5z|O%8=f0*rkX5eVx$FWYIz_&^NY ztpDIJZN93sF>?NTN+wL9Dn1!oW3C>^5n0qb$Cby&?P-&ui{8R-Idv4RSj;qNNenMZ z3EgMC%mLMP`9p18PD&_)nuojd($dE$g=jgk--lwq^qM=xvjZgw;#KGS<$kU2qTlI_ z#?<*w*oD3tYZEqw+iB<%yvk2+5Ds8Q3XZ?k8p8B4Oc8)@?E$97-j9t+C z(&0lt+WCMVg6O=NV&oriSaQ#SrbBm2*GNfAE;93_HkHYQD@$UXhLU}(1BQ?M?}6J? zOe*?E<(AirInN+9>f!|98)rT8u{Xj5S=4#`(tG(tzN2v3v2G6*l+vO^aflGW~F_*x;u) z|DA_l_)T~foP5tOiI|R##ig*9hQELdTkqkc#DkT*W;cL483W*`>@VH%lJl5u&rv!u zgquTX*!ar$U4caoUS1Bx+IVFLF{30o8!gLN9yYS9`Qh$x!{Rlb6ZVYlaS2v$MEAXS zlmKIoM-G+ONWjvt&e!?y+C@_)Z#-X zg;!+1$oQ`auRwD}n=R#G)_4kHxuigYllQ%Pa zu}1VB>ct5M=noXrytl`Hg@y=3a-uE0Hz@L5ahXbPDP^atB^X-?HL zYDnSqppd3_C5_@(I>BypF@pfCm%|N+`F)Yc2TNKZ;{{-#uYi~Z5@Xs-@^+|V@(^YH z%0S$TCye|u0-SR)5Lb+Q7(dulv6yPGXcr-qlYyA0MqCJ}s_(aII44_Ko(Urj^cOa6 zKl7Sm8<02ck(n{_zCx>t56q*_`&GN0P(`Wp-b8+woh?6wx~|#SeruqRky&=0k+6E* Vx1xOhwd%pH1pffb;!FTM{{fu%o3H=? literal 0 HcmV?d00001 diff --git a/phreeqcpp/cl1.cpp b/phreeqcpp/cl1.cpp new file mode 100644 index 00000000..107a8ad9 --- /dev/null +++ b/phreeqcpp/cl1.cpp @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include "Phreeqc.h" +#include "phqalloc.h" + +/* debug +#define DEBUG_CL1 +#define CHECK_ERRORS + */ + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +int Phreeqc:: +cl1(int k, int l, int m, int n, + int l_nklmd, int l_n2d, + LDBLE * q, + int *l_kode, LDBLE l_toler, + int *l_iter, LDBLE * l_x, LDBLE * l_res, LDBLE * l_error, + LDBLE * l_cu, int *l_iu, int *l_s, int check) +{ + /* System generated locals */ + union double_or_int + { + int ival; + LDBLE dval; + } *q2; + + /* Local variables */ + int nklm; + LDBLE xmin, xmax; + int iout = 0; + // static i runs faster on windows + int i, j; + LDBLE l_z; + int maxit, n1, n2; + LDBLE pivot; + int ia, ii, kk, nk, js; + int in = 0; + LDBLE sn; + int iphase, kforce; + LDBLE zu, zv; + LDBLE tpivot; + int klm, jmn, nkl, jpn; + LDBLE cuv; + long double sum; + int klm1; + int q_dim, cu_dim; + int kode_arg; + LDBLE check_toler; +#ifdef CHECK_ERRORS + char **col_name, **row_name; + int *row_back, *col_back; +#endif +/* THIS SUBROUTINE USES A MODIFICATION OF THE SIMPLEX */ +/* METHOD OF LINEAR PROGRAMMING TO CALCULATE AN L1 SOLUTION */ +/* TO A K BY N SYSTEM OF LINEAR EQUATIONS */ +/* AX=B */ +/* SUBJECT TO L LINEAR EQUALITY CONSTRAINTS */ +/* CX=D */ +/* AND M LINEAR INEQUALITY CONSTRAINTS */ +/* EX.LE.F. */ +/* DESCRIPTION OF PARAMETERS */ +/* K NUMBER OF ROWS OF THE MATRIX A (K.GE.1). */ +/* L NUMBER OF ROWS OF THE MATRIX C (L.GE.0). */ +/* M NUMBER OF ROWS OF THE MATRIX E (M.GE.0). */ +/* N NUMBER OF COLUMNS OF THE MATRICES A,C,E (N.GE.1). */ +/* KLMD SET TO AT LEAST K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* KLM2D SET TO AT LEAST K+L+M+2 FOR ADJUSTABLE DIMENSIONS. */ +/* NKLMD SET TO AT LEAST N+K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* N2D SET TO AT LEAST N+2 FOR ADJUSTABLE DIMENSIONS */ +/* Q TWO DIMENSIONAL REAL ARRAY WITH KLM2D ROWS AND */ +/* AT LEAST N2D COLUMNS. */ +/* ON ENTRY THE MATRICES A,C AND E, AND THE VECTORS */ +/* B,D AND F MUST BE STORED IN THE FIRST K+L+M ROWS */ +/* AND N+1 COLUMNS OF Q AS FOLLOWS */ +/* A B */ +/* Q = C D */ +/* E F */ +/* THESE VALUES ARE DESTROYED BY THE SUBROUTINE. */ +/* KODE A CODE USED ON ENTRY TO, AND EXIT */ +/* FROM, THE SUBROUTINE. */ +/* ON ENTRY, THIS SHOULD NORMALLY BE SET TO 0. */ +/* HOWEVER, IF CERTAIN NONNEGATIVITY CONSTRAINTS */ +/* ARE TO BE INCLUDED IMPLICITLY, RATHER THAN */ +/* EXPLICITLY IN THE CONSTRAINTS EX.LE.F, THEN KODE */ +/* SHOULD BE SET TO 1, AND THE NONNEGATIVITY */ +/* CONSTRAINTS INCLUDED IN THE ARRAYS X AND */ +/* RES (SEE BELOW). */ +/* ON EXIT, KODE HAS ONE OF THE */ +/* FOLLOWING VALUES */ +/* 0- OPTIMAL SOLUTION FOUND, */ +/* 1- NO FEASIBLE SOLUTION TO THE */ +/* CONSTRAINTS, */ +/* 2- CALCULATIONS TERMINATED */ +/* PREMATURELY DUE TO ROUNDING ERRORS, */ +/* 3- MAXIMUM NUMBER OF ITERATIONS REACHED. */ +/* TOLER A SMALL POSITIVE TOLERANCE. EMPIRICAL */ +/* EVIDENCE SUGGESTS TOLER = 10**(-D*2/3), */ +/* WHERE D REPRESENTS THE NUMBER OF DECIMAL */ +/* DIGITS OF ACCURACY AVAILABLE. ESSENTIALLY, */ +/* THE SUBROUTINE CANNOT DISTINGUISH BETWEEN ZERO */ +/* AND ANY QUANTITY WHOSE MAGNITUDE DOES NOT EXCEED */ +/* TOLER. IN PARTICULAR, IT WILL NOT PIVOT ON ANY */ +/* NUMBER WHOSE MAGNITUDE DOES NOT EXCEED TOLER. */ +/* ITER ON ENTRY ITER MUST CONTAIN AN UPPER BOUND ON */ +/* THE MAXIMUM NUMBER OF ITERATIONS ALLOWED. */ +/* A SUGGESTED VALUE IS 10*(K+L+M). ON EXIT ITER */ +/* GIVES THE NUMBER OF SIMPLEX ITERATIONS. */ +/* X ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST N2D. */ +/* ON EXIT THIS ARRAY CONTAINS A */ +/* SOLUTION TO THE L1 PROBLEM. IF KODE=1 */ +/* ON ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE */ +/* SIMPLE NONNEGATIVITY CONSTRAINTS ON THE */ +/* VARIABLES. THE VALUES -1, 0, OR 1 */ +/* FOR X(J) INDICATE THAT THE J-TH VARIABLE */ +/* IS RESTRICTED TO BE .LE.0, UNRESTRICTED, */ +/* OR .GE.0 RESPECTIVELY. */ +/* RES ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST KLMD. */ +/* ON EXIT THIS CONTAINS THE RESIDUALS B-AX */ +/* IN THE FIRST K COMPONENTS, D-CX IN THE */ +/* NEXT L COMPONENTS (THESE WILL BE =0),AND */ +/* F-EX IN THE NEXT M COMPONENTS. IF KODE=1 ON */ +/* ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE SIMPLE */ +/* NONNEGATIVITY CONSTRAINTS ON THE RESIDUALS */ +/* B-AX. THE VALUES -1, 0, OR 1 FOR RES(I) */ +/* INDICATE THAT THE I-TH RESIDUAL (1.LE.I.LE.K) IS */ +/* RESTRICTED TO BE .LE.0, UNRESTRICTED, OR .GE.0 */ +/* RESPECTIVELY. */ +/* ERROR ON EXIT, THIS GIVES THE MINIMUM SUM OF */ +/* ABSOLUTE VALUES OF THE RESIDUALS. */ +/* CU A TWO DIMENSIONAL REAL ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* IU A TWO DIMENSIONAL INTEGER ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* S INTEGER ARRAY OF SIZE AT LEAST KLMD, USED FOR */ +/* WORKSPACE. */ +/* DOUBLE PRECISION DBLE */ +/* REAL */ + +/* INITIALIZATION. */ + + zv = 0; + kode_arg = *l_kode; + cl1_space(check, l_n2d, k + l + m, l_nklmd); + +/* Parameter adjustments */ + q_dim = l_n2d; + q2 = (union double_or_int *) q; + cu_dim = l_nklmd; + +/* Function Body */ + maxit = *l_iter; + n1 = n + 1; + n2 = n + 2; + nk = n + k; + nkl = nk + l; + klm = k + l + m; + klm1 = klm + 1; + nklm = n + klm; + kforce = 1; + *l_iter = 0; + js = 0; + ia = -1; + +/* SET UP LABELS IN Q. */ + for (j = 0; j < n; ++j) + { + q2[klm1 * q_dim + j].ival = j + 1; + } +/* L10: */ + for (i = 0; i < klm; ++i) + { + q2[i * q_dim + n1].ival = n + i + 1; + if (q2[i * q_dim + n].dval < 0.) + { + for (j = 0; j < n1; ++j) + { + q2[i * q_dim + j].dval = -q2[i * q_dim + j].dval; + } + q2[i * q_dim + n1].ival = -q2[i * q_dim + n1].ival; +/* L20: */ + } + } +/* L30: */ +/* SET UP PHASE 1 COSTS. */ + iphase = 2; +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 1 costs\n")); +#endif +/* Zero first row of cu and iu */ + memset(&l_cu[0], 0, (size_t)nklm * sizeof(LDBLE)); + memset(&l_iu[0], 0, (size_t)nklm * sizeof(int)); +/* L40: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L40\n")); +#endif + if (l != 0) + { + for (j = nk; j < nkl; ++j) + { + l_cu[j] = 1.; + l_iu[j] = 1; + } +/* L50: */ + iphase = 1; + } + +/* Copy first row of cu and iu to second row */ + memcpy((void *) &(l_cu[cu_dim]), (void *) &(l_cu[0]), + (size_t) nklm * sizeof(LDBLE)); + memcpy((void *) &(l_iu[cu_dim]), (void *) &(l_iu[0]), + (size_t) nklm * sizeof(int)); + +/* L60: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L60\n")); +#endif + if (m != 0) + { + for (j = nkl; j < nklm; ++j) + { + l_cu[cu_dim + j] = 1.; + l_iu[cu_dim + j] = 1; + jmn = j - n; + if (q2[jmn * q_dim + n1].ival < 0) + { + iphase = 1; + } + } +/* L70: */ + } +/* L80: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L80\n")); +#endif + if (*l_kode != 0) + { + for (j = 0; j < n; ++j) + { + if (l_x[j] < 0.) + { +/* L90: */ + l_cu[j] = 1.; + l_iu[j] = 1; + } + else if (l_x[j] > 0.) + { + l_cu[cu_dim + j] = 1.; + l_iu[cu_dim + j] = 1; + } + } +/* L110: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L110\n")); +#endif + for (j = 0; j < k; ++j) + { + jpn = j + n; + if (l_res[j] < 0.) + { +/* L120: */ + l_cu[jpn] = 1.; + l_iu[jpn] = 1; + if (q2[j * q_dim + n1].ival > 0) + { + iphase = 1; + } + } + else if (l_res[j] > 0.) + { +/* L130: */ + l_cu[cu_dim + jpn] = 1.; + l_iu[cu_dim + jpn] = 1; + if (q2[j * q_dim + n1].ival < 0) + { + iphase = 1; + } + } + } +/* L140: */ + } +/* L150: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L150\n")); +#endif + if (iphase == 2) + { + goto L500; + } +/* COMPUTE THE MARGINAL COSTS. */ + L160: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L160\n")); +#endif + for (j = js; j < n1; ++j) + { + sum = 0.; + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii < 0) + { + l_z = l_cu[cu_dim - ii - 1]; + } + else + { + l_z = l_cu[ii - 1]; + } + sum += (long double) q2[i * q_dim + j].dval * (long double) l_z; + } + q2[klm * q_dim + j].dval = (double)sum; + } + for (j = js; j < n; ++j) + { + ii = q2[klm1 * q_dim + j].ival; + if (ii < 0) + { + l_z = l_cu[cu_dim - ii - 1]; + } + else + { + l_z = l_cu[ii - 1]; + } + q2[klm * q_dim + j].dval -= l_z; + } +/* DETERMINE THE VECTOR TO ENTER THE BASIS. */ + L240: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L240, xmax %e\n", xmax)); +#endif + xmax = 0.; + if (js >= n) + { + goto L490; /* test for optimality */ + } + for (j = js; j < n; ++j) + { + zu = q2[klm * q_dim + j].dval; + ii = q2[klm1 * q_dim + j].ival; + if (ii > 0) + { + zv = -zu - l_cu[ii - 1] - l_cu[cu_dim + ii - 1]; + } + else + { + ii = -ii; + zv = zu; + zu = -zu - l_cu[ii - 1] - l_cu[cu_dim + ii - 1]; + } +/* L260 */ + if (kforce == 1 && ii > n) + { + continue; + } + if (l_iu[ii - 1] != 1 && zu > xmax) + { + xmax = zu; + in = j; + } +/* L270 */ + if (l_iu[cu_dim + ii - 1] != 1 && zv > xmax) + { + xmax = zv; + in = j; + } + } +/* L280 */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L280 xmax %e, toler %e\n", xmax, l_toler)); +#endif + if (xmax <= l_toler) + { +#ifdef DEBUG_CL1 + output_msg(sformatf( "xmax before optimality test %e\n", xmax)); +#endif + goto L490; /* test for optimality */ + } + if (q2[klm * q_dim + in].dval != xmax) + { + for (i = 0; i < klm1; ++i) + { + q2[i * q_dim + in].dval = -q2[i * q_dim + in].dval; + } + q2[klm1 * q_dim + in].ival = -q2[klm1 * q_dim + in].ival; +/* L290: */ + q2[klm * q_dim + in].dval = xmax; + } +/* DETERMINE THE VECTOR TO LEAVE THE BASIS. */ + if (iphase != 1 && ia != -1) + { + xmax = 0.; +/* find maximum absolute value in column "in" */ + for (i = 0; i <= ia; ++i) + { + l_z = fabs(q2[i * q_dim + in].dval); + if (l_z > xmax) + { + xmax = l_z; + iout = i; + } + } +/* L310: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L310, xmax %e\n", xmax)); +#endif +/* switch row ia with row iout, use memcpy */ + if (xmax > l_toler) + { + if (ia != iout) + { + memcpy((void *) &(scratch[0]), (void *) &(q2[ia * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[ia * q_dim]), (void *) &(q2[iout * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[iout * q_dim]), (void *) &(scratch[0]), + (size_t) n2 * sizeof(LDBLE)); + } +/* L320: */ +/* set pivot to row ia, column in */ + iout = ia; + --ia; + pivot = q2[iout * q_dim + in].dval; + goto L420; /* Gauss Jordan */ + } + } +/* L330: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L330, xmax %e\n", xmax)); +#endif + kk = -1; +/* divide column n1 by positive value in column "in" greater than toler */ + for (i = 0; i < klm; ++i) + { + l_z = q2[i * q_dim + in].dval; + if (l_z > l_toler) + { + ++kk; + l_res[kk] = q2[i * q_dim + n].dval / l_z; + l_s[kk] = i; + } + } +/* L340: */ +#ifdef DEBUG_CL1 + if (kk < 0) + { + output_msg(sformatf( "kode = 2 in loop 340.\n")); + } +#endif + L350: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L350, xmax %e\n", xmax)); +#endif + if (kk < 0) + { +/* no positive value found in L340 or bypass intermediate verticies */ + *l_kode = 2; + goto L590; + } +/* L360: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L360, xmax %e\n", xmax)); +#endif +/* find minimum residual */ + xmin = l_res[0]; + iout = l_s[0]; + j = 0; + if (kk != 0) + { + for (i = 1; i <= kk; ++i) + { + if (l_res[i] < xmin) + { + j = i; + xmin = l_res[i]; + iout = l_s[i]; + } + } +/* L370: */ +/* put kk in position j */ + l_res[j] = l_res[kk]; + l_s[j] = l_s[kk]; + } +/* L380: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L380 iout %d, xmin %e, xmax %e\n", iout, + xmin, xmax)); +#endif + --kk; + pivot = q2[iout * q_dim + in].dval; + ii = q2[iout * q_dim + n1].ival; + if (iphase != 1) + { + if (ii < 0) + { +/* L390: */ + if (l_iu[-ii - 1] == 1) + { + goto L420; + } + } + else + { + if (l_iu[cu_dim + ii - 1] == 1) + { + goto L420; + } + } + } +/* L400: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L400\n")); +#endif + ii = abs(ii); + cuv = l_cu[ii - 1] + l_cu[cu_dim + ii - 1]; + if (q2[klm * q_dim + in].dval - pivot * cuv > l_toler) + { + +/* BYPASS INTERMEDIATE VERTICES. */ + for (j = js; j < n1; ++j) + { + l_z = q2[iout * q_dim + j].dval; + q2[klm * q_dim + j].dval -= l_z * cuv; + q2[iout * q_dim + j].dval = -l_z; + } +/* L410: */ + q2[iout * q_dim + n1].ival = -q2[iout * q_dim + n1].ival; + goto L350; + } +/* GAUSS-JORDAN ELIMINATION. */ + L420: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Gauss Jordon %d\n", *l_iter)); +#endif + if (*l_iter >= maxit) + { + *l_kode = 3; + goto L590; + } +/* L430: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L430\n")); +#endif + ++(*l_iter); + for (j = js; j < n1; ++j) + { + if (j != in) + { + q2[iout * q_dim + j].dval /= pivot; + } + } +/* L440: */ + for (j = js; j < n1; ++j) + { + if (j != in) + { + l_z = -q2[iout * q_dim + j].dval; + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + q2[i * q_dim + j].dval += l_z * q2[i * q_dim + in].dval; + } + } +/* L450: */ + } + } +/* L460: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L460\n")); +#endif + tpivot = -pivot; + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + q2[i * q_dim + in].dval /= tpivot; + } + } +/* L470: */ + q2[iout * q_dim + in].dval = 1. / pivot; + ii = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = ii; + ii = abs(ii); + if (l_iu[ii - 1] == 0 || l_iu[cu_dim + ii - 1] == 0) + { + goto L240; + } +/* switch column */ + for (i = 0; i < klm1; ++i) + { + l_z = q2[i * q_dim + in].dval; + q2[i * q_dim + in].dval = q2[i * q_dim + js].dval; + q2[i * q_dim + js].dval = l_z; + } + i = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = q2[klm1 * q_dim + js].ival; + q2[klm1 * q_dim + js].ival = i; +/* L480: */ + ++js; + goto L240; +/* TEST FOR OPTIMALITY. */ + L490: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L490\n")); +#endif + if (kforce == 0) + { + if (iphase == 1) + { + if (q2[klm * q_dim + n].dval <= l_toler) + { + goto L500; + } +#ifdef DEBUG_CL1 + output_msg(sformatf( "q2[klm1-1, n1-1] > *toler. %e\n", + q2[(klm1 - 1) * q_dim + n1 - 1].dval)); +#endif + *l_kode = 1; + goto L590; + } + *l_kode = 0; + goto L590; + } + if (iphase != 1 || q2[klm * q_dim + n].dval > l_toler) + { + kforce = 0; + goto L240; + } +/* SET UP PHASE 2 COSTS. */ + L500: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 2 costs %d\n", *l_iter)); +#endif + iphase = 2; + for (j = 0; j < nklm; ++j) + { + l_cu[j] = 0.; + } +/* L510: */ + for (j = n; j < nk; ++j) + { + l_cu[j] = 1.; + } + memcpy((void *) &(l_cu[cu_dim]), (void *) &(l_cu[0]), + (size_t) nklm * sizeof(LDBLE)); +/* L520: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii <= 0) + { + if (l_iu[cu_dim - ii - 1] == 0) + { + continue; + } + l_cu[cu_dim - ii - 1] = 0.; + } + else + { +/* L530: */ + if (l_iu[ii - 1] == 0) + { + continue; + } + l_cu[ii - 1] = 0.; + } +/* L540: */ + ++ia; +/* switch row */ + if (ia != i) + { + memcpy((void *) &(scratch[0]), (void *) &(q2[ia * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[ia * q_dim]), (void *) &(q2[i * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[i * q_dim]), (void *) &(scratch[0]), + (size_t) n2 * sizeof(LDBLE)); + } +/* L550: */ + } +/* L560: */ + goto L160; + + +/* PREPARE OUTPUT. */ + L590: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L590\n")); +#endif + sum = 0.; + for (j = 0; j < n; ++j) + { + l_x[j] = 0.; + } +/* L600: */ + for (i = 0; i < klm; ++i) + { + l_res[i] = 0.; + } +/* L610: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + sn = 1.; + if (ii < 0) + { + ii = -ii; + sn = -1.; + } + if (ii <= n) + { +/* L620: */ + l_x[ii - 1] = sn * q2[i * q_dim + n].dval; + } + else + { +/* L630: */ + l_res[ii - n - 1] = sn * q2[i * q_dim + n].dval; + if (ii >= n1 && ii <= nk) + { +/* * DBLE(Q(I,N1)) */ + sum += (long double) q2[i * q_dim + n].dval; + } + } + } +/* L640: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L640\n")); +#endif + *l_error = (double)sum; + /* + * Check calculation + */ + if ((check == 1) && (*l_kode == 0)) + { + check_toler = 10. * l_toler; + /* + * Check optimization constraints + */ + if (kode_arg == 1) + { + for (i = 0; i < k; ++i) + { + if (res_arg[i] < 0.0) + { + if (l_res[i] > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: optimization constraint not satisfied row %d, res %s, constraint %f.\n", + row_name[row_back[i]], l_res[i], res_arg[i])); +#endif + *l_kode = 1; + } + } + else if (res_arg[i] > 0.0) + { + if (l_res[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: optimization constraint not satisfied row %s, res %e, constraint %f.\n", + row_name[row_back[i]], l_res[i], res_arg[i])); +#endif + *l_kode = 1; + } + } + } + } + /* + * Check equalities + */ + for (i = k; i < k + l; ++i) + { + if (fabs(l_res[i]) > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: equality constraint not satisfied row %s, res %e, tolerance %e.\n", + row_name[row_back[i]], l_res[i], check_toler)); +#endif + *l_kode = 1; + } + } + /* + * Check inequalities + */ + for (i = k + l; i < k + l + m; ++i) + { + if (l_res[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: inequality constraint not satisfied row %s, res %e, tolerance %e.\n", + row_name[row_back[i]], l_res[i], check_toler)); +#endif + *l_kode = 1; + } + } + /* + * Check dissolution/precipitation constraints + */ + if (kode_arg == 1) + { + for (i = 0; i < n; ++i) + { + if (x_arg[i] < 0.0) + { + if (l_x[i] > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: dis/pre constraint not satisfied column %s, x %e, constraint %f.\n", + col_name[col_back[i]], l_x[i], x_arg[i])); +#endif + *l_kode = 1; + } + } + else if (x_arg[i] > 0.0) + { + if (l_x[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: dis/pre constraint not satisfied column %s, x %e, constraint %f.\n", + col_name[col_back[i]], l_x[i], x_arg[i])); +#endif + *l_kode = 1; + } + } + } + } + if (*l_kode == 1) + { + output_msg(sformatf( + "\n\tCL1: Roundoff errors in optimization.\n\t Try using -multiple_precision in INVERSE_MODELING\n")); + } + } + return 0; +} + +void Phreeqc:: +cl1_space(int check, int l_n2d, int klm, int l_nklmd) +{ + if (check == 1) + { + if ((size_t)l_n2d > x_arg.size()) + { + x_arg.resize((size_t)l_n2d); + } + memset(&x_arg[0], 0, sizeof(double) * (size_t)l_n2d); + + if ((size_t)klm > res_arg.size()) + { + res_arg.resize((size_t)klm); + } + memset(&res_arg[0], 0, sizeof(double) * (size_t)klm); + } + if (l_nklmd > 0) + { + if ((size_t)l_nklmd > scratch.size()) + { + scratch.resize(l_nklmd); + } + memset(&scratch[0], 0, sizeof(double) * (size_t)l_nklmd); + } + else if (scratch.size() == 0) + { + scratch.resize(1); + memset(&scratch[0], 0, sizeof(double)); + } +} diff --git a/phreeqcpp/cl1mp.cpp b/phreeqcpp/cl1mp.cpp new file mode 100644 index 00000000..79767406 --- /dev/null +++ b/phreeqcpp/cl1mp.cpp @@ -0,0 +1,1141 @@ +#ifdef INVERSE_CL1MP +#include +#include +#include +#include +#include + +#include "Phreeqc.h" +#include "phqalloc.h" +#include "phrqtype.h" + +/* debug +#define DEBUG_CL1 +#define CHECK_ERRORS + */ + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +int Phreeqc:: +cl1mp(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE * q_arg, + int *kode_arg, LDBLE toler_arg, + int *iter, LDBLE * x_arg, LDBLE * res_arg, LDBLE * error_arg, + LDBLE * cu_arg, int *iu, int *s, int check, LDBLE censor_arg) +{ + mpf_set_default_prec(256); + /* System generated locals */ + union double_or_int + { + int ival; + mpf_t dval; + } *q2; + + /* Local variables */ + int nklm; + int iout = 0; + // static i runs faster + int i, j; + int maxit, n1; //, n2; + int ia, ii, kk, nk, js; + int in = 0; + int iphase, kforce; + int klm, jmn, nkl, jpn; + int klm1; + int *kode; + int q_dim, cu_dim; + int iswitch; + mpf_t *q; + mpf_t *x; + mpf_t *res; + mpf_t error; + mpf_t *cu; + mpf_t dummy, dummy1, sum, z, zu, zv, xmax, minus_one, toler, check_toler; + /*mpf_t *scratch; */ + mpf_t pivot, xmin, cuv, tpivot, sn; + mpf_t zero; + int censor; + mpf_t censor_tol; +/* THIS SUBROUTINE USES A MODIFICATION OF THE SIMPLEX */ +/* METHOD OF LINEAR PROGRAMMING TO CALCULATE AN L1 SOLUTION */ +/* TO A K BY N SYSTEM OF LINEAR EQUATIONS */ +/* AX=B */ +/* SUBJECT TO L LINEAR EQUALITY CONSTRAINTS */ +/* CX=D */ +/* AND M LINEAR INEQUALITY CONSTRAINTS */ +/* EX.LE.F. */ +/* DESCRIPTION OF PARAMETERS */ +/* K NUMBER OF ROWS OF THE MATRIX A (K.GE.1). */ +/* L NUMBER OF ROWS OF THE MATRIX C (L.GE.0). */ +/* M NUMBER OF ROWS OF THE MATRIX E (M.GE.0). */ +/* N NUMBER OF COLUMNS OF THE MATRICES A,C,E (N.GE.1). */ +/* KLMD SET TO AT LEAST K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* KLM2D SET TO AT LEAST K+L+M+2 FOR ADJUSTABLE DIMENSIONS. */ +/* NKLMD SET TO AT LEAST N+K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* N2D SET TO AT LEAST N+2 FOR ADJUSTABLE DIMENSIONS */ +/* Q TWO DIMENSIONAL REAL ARRAY WITH KLM2D ROWS AND */ +/* AT LEAST N2D COLUMNS. */ +/* ON ENTRY THE MATRICES A,C AND E, AND THE VECTORS */ +/* B,D AND F MUST BE STORED IN THE FIRST K+L+M ROWS */ +/* AND N+1 COLUMNS OF Q AS FOLLOWS */ +/* A B */ +/* Q = C D */ +/* E F */ +/* THESE VALUES ARE DESTROYED BY THE SUBROUTINE. */ +/* KODE A CODE USED ON ENTRY TO, AND EXIT */ +/* FROM, THE SUBROUTINE. */ +/* ON ENTRY, THIS SHOULD NORMALLY BE SET TO 0. */ +/* HOWEVER, IF CERTAIN NONNEGATIVITY CONSTRAINTS */ +/* ARE TO BE INCLUDED IMPLICITLY, RATHER THAN */ +/* EXPLICITLY IN THE CONSTRAINTS EX.LE.F, THEN KODE */ +/* SHOULD BE SET TO 1, AND THE NONNEGATIVITY */ +/* CONSTRAINTS INCLUDED IN THE ARRAYS X AND */ +/* RES (SEE BELOW). */ +/* ON EXIT, KODE HAS ONE OF THE */ +/* FOLLOWING VALUES */ +/* 0- OPTIMAL SOLUTION FOUND, */ +/* 1- NO FEASIBLE SOLUTION TO THE */ +/* CONSTRAINTS, */ +/* 2- CALCULATIONS TERMINATED */ +/* PREMATURELY DUE TO ROUNDING ERRORS, */ +/* 3- MAXIMUM NUMBER OF ITERATIONS REACHED. */ +/* TOLER A SMALL POSITIVE TOLERANCE. EMPIRICAL */ +/* EVIDENCE SUGGESTS TOLER = 10**(-D*2/3), */ +/* WHERE D REPRESENTS THE NUMBER OF DECIMAL */ +/* DIGITS OF ACCURACY AVAILABLE. ESSENTIALLY, */ +/* THE SUBROUTINE CANNOT DISTINGUISH BETWEEN ZERO */ +/* AND ANY QUANTITY WHOSE MAGNITUDE DOES NOT EXCEED */ +/* TOLER. IN PARTICULAR, IT WILL NOT PIVOT ON ANY */ +/* NUMBER WHOSE MAGNITUDE DOES NOT EXCEED TOLER. */ +/* ITER ON ENTRY ITER MUST CONTAIN AN UPPER BOUND ON */ +/* THE MAXIMUM NUMBER OF ITERATIONS ALLOWED. */ +/* A SUGGESTED VALUE IS 10*(K+L+M). ON EXIT ITER */ +/* GIVES THE NUMBER OF SIMPLEX ITERATIONS. */ +/* X ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST N2D. */ +/* ON EXIT THIS ARRAY CONTAINS A */ +/* SOLUTION TO THE L1 PROBLEM. IF KODE=1 */ +/* ON ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE */ +/* SIMPLE NONNEGATIVITY CONSTRAINTS ON THE */ +/* VARIABLES. THE VALUES -1, 0, OR 1 */ +/* FOR X(J) INDICATE THAT THE J-TH VARIABLE */ +/* IS RESTRICTED TO BE .LE.0, UNRESTRICTED, */ +/* OR .GE.0 RESPECTIVELY. */ +/* RES ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST KLMD. */ +/* ON EXIT THIS CONTAINS THE RESIDUALS B-AX */ +/* IN THE FIRST K COMPONENTS, D-CX IN THE */ +/* NEXT L COMPONENTS (THESE WILL BE =0),AND */ +/* F-EX IN THE NEXT M COMPONENTS. IF KODE=1 ON */ +/* ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE SIMPLE */ +/* NONNEGATIVITY CONSTRAINTS ON THE RESIDUALS */ +/* B-AX. THE VALUES -1, 0, OR 1 FOR RES(I) */ +/* INDICATE THAT THE I-TH RESIDUAL (1.LE.I.LE.K) IS */ +/* RESTRICTED TO BE .LE.0, UNRESTRICTED, OR .GE.0 */ +/* RESPECTIVELY. */ +/* ERROR ON EXIT, THIS GIVES THE MINIMUM SUM OF */ +/* ABSOLUTE VALUES OF THE RESIDUALS. */ +/* CU A TWO DIMENSIONAL REAL ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* IU A TWO DIMENSIONAL INTEGER ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* S INTEGER ARRAY OF SIZE AT LEAST KLMD, USED FOR */ +/* WORKSPACE. */ +/* DOUBLE PRECISION DBLE */ +/* REAL */ + +/* INITIALIZATION. */ + /* + * mp variables + */ + censor = 1; + if (censor_arg == 0.0) + censor = 0; + mpf_set_default_prec(96); + mpf_init(zero); + mpf_init(dummy); + mpf_init(dummy1); + mpf_init_set_d(censor_tol, censor_arg); + q = (mpf_t *) + PHRQ_malloc((size_t) + (max_row_count * max_column_count * sizeof(mpf_t))); + if (q == NULL) + malloc_error(); + for (i = 0; i < max_row_count * max_column_count; ++i) + { + mpf_init_set_d(q[i], q_arg[i]); + if (censor == 1) + { + if (mpf_cmp(q[i], zero) != 0) + { + mpf_abs(dummy1, q[i]); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q[i], 0); + } + } + } + } + x = (mpf_t *) PHRQ_malloc((size_t) (n2d * sizeof(mpf_t))); + if (x == NULL) + malloc_error(); + for (i = 0; i < n2d; ++i) + { + mpf_init_set_d(x[i], x_arg[i]); + } + res = (mpf_t *) PHRQ_malloc((size_t) ((k + l + m) * sizeof(mpf_t))); + if (res == NULL) + malloc_error(); + for (i = 0; i < k + l + m; ++i) + { + mpf_init_set_d(res[i], res_arg[i]); + } + cu = (mpf_t *) PHRQ_malloc((size_t) (2 * nklmd * sizeof(mpf_t))); + if (cu == NULL) + malloc_error(); + for (i = 0; i < 2 * nklmd; ++i) + { + mpf_init_set_d(cu[i], cu_arg[i]); + } + kode = (int *) PHRQ_malloc(sizeof(int)); + if (kode == NULL) + malloc_error(); + *kode = *kode_arg; + mpf_init(sum); + mpf_init(error); + mpf_init(z); + mpf_init(zu); + mpf_init(zv); + mpf_init(xmax); + mpf_init_set_si(minus_one, -1); + mpf_init_set_d(toler, toler_arg); + mpf_init_set_d(check_toler, toler_arg); + mpf_init(pivot); + mpf_init(xmin); + mpf_init(cuv); + mpf_init(tpivot); + mpf_init(sn); +/* Parameter adjustments */ + q_dim = n2d; + q2 = (union double_or_int *) q; + cu_dim = nklmd; + +/* Function Body */ + maxit = *iter; + n1 = n + 1; + // n2 = n + 2; + nk = n + k; + nkl = nk + l; + klm = k + l + m; + klm1 = klm + 1; + nklm = n + klm; + kforce = 1; + *iter = 0; + js = 0; + ia = -1; +/* Make scratch space */ +/* + scratch = (LDBLE *) PHRQ_malloc( (size_t) nklmd * sizeof(LDBLE)); + if (scratch == NULL) malloc_error(); + for (i=0; i < nklmd; i++) { + scratch[i] = 0.0; + } +*/ +/* + scratch = (mpf_t *) PHRQ_malloc( (size_t) nklmd * sizeof(mpf_t)); + if (scratch == NULL) malloc_error(); + for (i=0; i < nklmd; i++) { + mpf_init(scratch[i]); + } +*/ +/* SET UP LABELS IN Q. */ + for (j = 0; j < n; ++j) + { + q2[klm1 * q_dim + j].ival = j + 1; + } +/* L10: */ + for (i = 0; i < klm; ++i) + { + q2[i * q_dim + n1].ival = n + i + 1; + if (mpf_cmp_d(q2[i * q_dim + n].dval, 0.0) < 0) + { + for (j = 0; j < n1; ++j) + { + /* q2[ i * q_dim + j ].dval = -q2[ i * q_dim + j ].dval; */ + mpf_neg(q2[i * q_dim + j].dval, q2[i * q_dim + j].dval); + } + q2[i * q_dim + n1].ival = -q2[i * q_dim + n1].ival; +/* L20: */ + } + } +/* L30: */ +/* SET UP PHASE 1 COSTS. */ + iphase = 2; +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 1 costs\n")); +#endif +/* Zero first row of cu and iu */ + /*memcpy( (void *) &(cu[0]), (void *) &(scratch[0]), (size_t) nklm * sizeof(mpf_t) ); */ + for (j = 0; j < nklm; ++j) + { + mpf_set_si(cu[j], 0); + iu[j] = 0; + } +/* L40: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L40\n")); +#endif + if (l != 0) + { + for (j = nk; j < nkl; ++j) + { + mpf_set_si(cu[j], 1); + /*cu[ j ] = 1.; */ + iu[j] = 1; + } +/* L50: */ + iphase = 1; + } + +/* Copy first row of cu and iu to second row */ + /*memcpy( (void *) &(cu[cu_dim]), (void *) &(cu[0]), (size_t) nklm * sizeof(mpf_t) ); */ + for (i = 0; i < nklm; ++i) + { + mpf_set(cu[cu_dim + i], cu[i]); + } + memcpy((void *) &(iu[cu_dim]), (void *) &(iu[0]), + (size_t) nklm * sizeof(int)); +/* L60: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L60\n")); +#endif + if (m != 0) + { + for (j = nkl; j < nklm; ++j) + { + /* cu[ cu_dim + j ] = 1.; */ + mpf_set_si(cu[cu_dim + j], 1); + iu[cu_dim + j] = 1; + jmn = j - n; + if (q2[jmn * q_dim + n1].ival < 0) + { + iphase = 1; + } + } +/* L70: */ + } +/* L80: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L80\n")); +#endif + if (*kode != 0) + { + for (j = 0; j < n; ++j) + { + /* if ( x[j] < 0.) { */ + if (mpf_cmp_si(x[j], 0) < 0) + { +/* L90: */ + /* cu[ j ] = 1.; */ + mpf_set_si(cu[j], 1); + iu[j] = 1; + /* } else if (x[j] > 0.) { */ + } + else if (mpf_cmp_si(x[j], 0) > 0) + { + /* cu[ cu_dim + j ] = 1.; */ + mpf_set_si(cu[cu_dim + j], 1); + iu[cu_dim + j] = 1; + } + } +/* L110: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L110\n")); +#endif + for (j = 0; j < k; ++j) + { + jpn = j + n; + /* if (res[j] < 0.) { */ + if (mpf_cmp_si(res[j], 0) < 0) + { +/* L120: */ + /* cu[ jpn ] = 1.; */ + mpf_set_si(cu[jpn], 1); + iu[jpn] = 1; + if (q2[j * q_dim + n1].ival > 0) + { + iphase = 1; + } + /* } else if (res[j] > 0.) { */ + } + else if (mpf_cmp_si(res[j], 0) > 0) + { +/* L130: */ + /* cu[ cu_dim + jpn ] = 1.; */ + mpf_set_si(cu[cu_dim + jpn], 1); + iu[cu_dim + jpn] = 1; + if (q2[j * q_dim + n1].ival < 0) + { + iphase = 1; + } + } + } +/* L140: */ + } +/* L150: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L150\n")); +#endif + if (iphase == 2) + { + goto L500; + } +/* COMPUTE THE MARGINAL COSTS. */ + L160: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L160\n")); +#endif + for (j = js; j < n1; ++j) + { + mpf_set_si(sum, 0); + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii < 0) + { + /* z = cu[ cu_dim - ii - 1 ]; */ + mpf_set(z, cu[cu_dim - ii - 1]); + } + else + { + /*z = cu[ ii - 1 ]; */ + mpf_set(z, cu[ii - 1]); + } + /*sum += q2[ i * q_dim + j ].dval * z; */ + mpf_mul(dummy, q2[i * q_dim + j].dval, z); + mpf_add(sum, sum, dummy); + } + /*q2[ klm * q_dim + j ].dval = sum; */ + mpf_set(q2[klm * q_dim + j].dval, sum); + } + for (j = js; j < n; ++j) + { + ii = q2[klm1 * q_dim + j].ival; + if (ii < 0) + { + /*z = cu[ cu_dim - ii - 1 ]; */ + mpf_set(z, cu[cu_dim - ii - 1]); + } + else + { + /*z = cu[ ii - 1 ]; */ + mpf_set(z, cu[ii - 1]); + } + /*q2[ klm * q_dim + j ].dval -= z; */ + mpf_sub(q2[klm * q_dim + j].dval, q2[klm * q_dim + j].dval, z); + } +/* DETERMINE THE VECTOR TO ENTER THE BASIS. */ + L240: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L240, xmax %e\n", mpf_get_d(xmax))); +#endif + /*xmax = 0.; */ + mpf_set_si(xmax, 0); + if (js >= n) + { + goto L490; /* test for optimality */ + } + for (j = js; j < n; ++j) + { + /*zu = q2[ klm * q_dim + j ].dval; */ + mpf_set(zu, q2[klm * q_dim + j].dval); + ii = q2[klm1 * q_dim + j].ival; + if (ii > 0) + { + /*zv = -zu - cu[ ii - 1 ] - cu[ cu_dim + ii - 1 ]; */ + mpf_mul(dummy, cu[cu_dim + ii - 1], minus_one); + mpf_sub(dummy, dummy, cu[ii - 1]); + mpf_sub(zv, dummy, zu); + } + else + { + ii = -ii; + /* zv = zu; */ + mpf_set(zv, zu); + /* zu = -zu - cu[ ii - 1 ] - cu[ cu_dim + ii - 1 ]; */ + mpf_mul(dummy, cu[cu_dim + ii - 1], minus_one); + mpf_sub(dummy, dummy, cu[ii - 1]); + mpf_sub(zu, dummy, zu); + } +/* L260 */ + if (kforce == 1 && ii > n) + { + continue; + } + /*if (iu[ ii - 1 ] != 1 && zu > xmax){ */ + if ((iu[ii - 1] != 1) && (mpf_cmp(zu, xmax) > 0)) + { + /*xmax = zu; */ + mpf_set(xmax, zu); + in = j; + } +/* L270 */ + /*if (iu[ cu_dim + ii - 1 ] != 1 && zv > xmax ) { */ + if ((iu[cu_dim + ii - 1] != 1) && (mpf_cmp(zv, xmax) > 0)) + { + /*xmax = zv; */ + mpf_set(xmax, zv); + in = j; + } + } +/* L280 */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L280 xmax %e, toler %e\n", mpf_get_d(xmax), + mpf_get_d(toler))); +#endif + /*if (xmax <= toler) { */ + if (mpf_cmp(xmax, toler) <= 0) + { +#ifdef DEBUG_CL1 + output_msg(sformatf( "xmax before optimality test %e\n", + mpf_get_d(xmax))); +#endif + goto L490; /* test for optimality */ + } + /*if (q2[ klm * q_dim + in ].dval != xmax) { */ + if (mpf_cmp(q2[klm * q_dim + in].dval, xmax) != 0) + { + for (i = 0; i < klm1; ++i) + { + /*q2[ i * q_dim + in ].dval = -q2[ i * q_dim + in ].dval; */ + mpf_neg(q2[i * q_dim + in].dval, q2[i * q_dim + in].dval); + } + q2[klm1 * q_dim + in].ival = -q2[klm1 * q_dim + in].ival; +/* L290: */ + /*q2[ klm * q_dim + in ].dval = xmax; */ + mpf_set(q2[klm * q_dim + in].dval, xmax); + } +/* DETERMINE THE VECTOR TO LEAVE THE BASIS. */ + if (iphase != 1 && ia != -1) + { + /*xmax = 0.; */ + mpf_set_si(xmax, 0); +/* find maximum absolute value in column "in" */ + for (i = 0; i <= ia; ++i) + { + /*z = fabs(q2[ i * q_dim + in ].dval); */ + mpf_abs(z, q2[i * q_dim + in].dval); + /*if (z > xmax) { */ + if (mpf_cmp(z, xmax) > 0) + { + /*xmax = z; */ + mpf_set(xmax, z); + iout = i; + } + } +/* L310: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L310, xmax %e\n", mpf_get_d(xmax))); +#endif +/* switch row ia with row iout, use memcpy */ + /*if (xmax > toler) { */ + if (mpf_cmp(xmax, toler) > 0) + { + /* + memcpy( (void *) &(scratch[0]), (void *) &(q2[ ia * q_dim]), + (size_t) n2 * sizeof(mpf_t) ); + memcpy( (void *) &(q2[ ia * q_dim ]), (void *) &(q2[ iout * q_dim]), + (size_t) n2 * sizeof(mpf_t) ); + memcpy( (void *) &(q2[ iout * q_dim ]), (void *) &(scratch[ 0 ]), + (size_t) n2 * sizeof(mpf_t) ); + */ + for (i = 0; i < n1; ++i) + { + mpf_set(dummy, q2[ia * q_dim + i].dval); + mpf_set(q2[ia * q_dim + i].dval, q2[iout * q_dim + i].dval); + mpf_set(q2[iout * q_dim + i].dval, dummy); + } + j = q2[ia * q_dim + n1].ival; + q2[ia * q_dim + n1].ival = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = j; + +/* L320: */ +/* set pivot to row ia, column in */ + iout = ia; + --ia; + /*pivot = q2[ iout * q_dim + in ].dval; */ + mpf_set(pivot, q2[iout * q_dim + in].dval); + goto L420; /* Gauss Jordan */ + } + } +/* L330: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L330, xmax %e\n", mpf_get_d(xmax))); +#endif + kk = -1; +/* divide column n1 by positive value in column "in" greater than toler */ + for (i = 0; i < klm; ++i) + { + /*z = q2[ i * q_dim + in ].dval; */ + mpf_set(z, q2[i * q_dim + in].dval); + /*if (z > toler) { */ + if (mpf_cmp(z, toler) > 0) + { + ++kk; + /*res[kk] = q2[ i * q_dim + n ].dval / z; */ + mpf_div(res[kk], q2[i * q_dim + n].dval, z); + s[kk] = i; + } + } +/* L340: */ + if (kk < 0) + { + output_msg(sformatf( "kode = 2 in loop 340.\n")); + } + L350: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L350, xmax %e\n", mpf_get_d(xmax))); +#endif + if (kk < 0) + { +/* no positive value found in L340 or bypass intermediate verticies */ + *kode = 2; + goto L590; + } +/* L360: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L360, xmax %e\n", mpf_get_d(xmax))); +#endif +/* find minimum residual */ + /*xmin = res[ 0 ]; */ + mpf_set(xmin, res[0]); + iout = s[0]; + j = 0; + if (kk != 0) + { + for (i = 1; i <= kk; ++i) + { + /*if (res[i] < xmin) { */ + if (mpf_cmp(res[i], xmin) < 0) + { + j = i; + /*xmin = res[i]; */ + mpf_set(xmin, res[i]); + iout = s[i]; + } + } +/* L370: */ +/* put kk in position j */ + /*res[j] = res[kk]; */ + mpf_set(res[j], res[kk]); + s[j] = s[kk]; + } +/* L380: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L380 iout %d, xmin %e, xmax %e\n", iout, + mpf_get_d(xmin), mpf_get_d(xmax))); +#endif + --kk; + /*pivot = q2[ iout * q_dim + in ].dval; */ + mpf_set(pivot, q2[iout * q_dim + in].dval); + ii = q2[iout * q_dim + n1].ival; + if (iphase != 1) + { + if (ii < 0) + { +/* L390: */ + if (iu[-ii - 1] == 1) + { + goto L420; + } + } + else + { + if (iu[cu_dim + ii - 1] == 1) + { + goto L420; + } + } + } +/* L400: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L400\n")); +#endif + ii = abs(ii); + /*cuv = cu[ ii - 1 ] + cu[ cu_dim + ii - 1]; */ + mpf_add(cuv, cu[ii - 1], cu[cu_dim + ii - 1]); + /*if (q2[ klm * q_dim + in ].dval - pivot * cuv > toler) { */ + mpf_mul(dummy, pivot, cuv); + mpf_sub(dummy, q2[klm * q_dim + in].dval, dummy); + if (mpf_cmp(dummy, toler) > 0) + { +/* BYPASS INTERMEDIATE VERTICES. */ + for (j = js; j < n1; ++j) + { + /*z = q2[ iout * q_dim + j ].dval; */ + mpf_set(z, q2[iout * q_dim + j].dval); + /*q2[ klm * q_dim + j ].dval -= z * cuv; */ + mpf_mul(dummy1, z, cuv); + mpf_sub(q2[klm * q_dim + j].dval, q2[klm * q_dim + j].dval, + dummy1); + + if (censor == 1) + { + if (mpf_cmp(q2[klm * q_dim + j].dval, zero) != 0) + { + mpf_abs(dummy1, q2[klm * q_dim + j].dval); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q2[klm * q_dim + j].dval, 0); + } + } + } + + /*q2[ iout * q_dim + j ].dval = -z; */ + mpf_neg(q2[iout * q_dim + j].dval, z); + } +/* L410: */ + q2[iout * q_dim + n1].ival = -q2[iout * q_dim + n1].ival; + goto L350; + } +/* GAUSS-JORDAN ELIMINATION. */ + L420: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Gauss Jordon %d\n", *iter)); +#endif + if (*iter >= maxit) + { + *kode = 3; + goto L590; + } +/* L430: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L430\n")); +#endif + ++(*iter); + for (j = js; j < n1; ++j) + { + if (j != in) + { + /*q2[ iout * q_dim + j ].dval /= pivot; */ + mpf_div(q2[iout * q_dim + j].dval, q2[iout * q_dim + j].dval, + pivot); + } + } +/* L440: */ + for (j = js; j < n1; ++j) + { + if (j != in) + { + /*z = -q2[ iout * q_dim + j ].dval; */ + mpf_neg(z, q2[iout * q_dim + j].dval); + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + /*q2[ i * q_dim + j ].dval += z * q2[ i * q_dim + in ].dval; */ + mpf_mul(dummy, z, q2[i * q_dim + in].dval); + mpf_add(q2[i * q_dim + j].dval, q2[i * q_dim + j].dval, + dummy); + + if (censor == 1) + { + if (mpf_cmp(q2[i * q_dim + j].dval, zero) != 0) + { + mpf_abs(dummy1, q2[i * q_dim + j].dval); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q2[i * q_dim + j].dval, 0); + } + } + } + } + } +/* L450: */ + } + } +/* L460: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L460\n")); +#endif + /*tpivot = -pivot; */ + mpf_neg(tpivot, pivot); + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + /*q2[ i * q_dim + in ].dval /= tpivot; */ + mpf_div(q2[i * q_dim + in].dval, q2[i * q_dim + in].dval, tpivot); + } + } +/* L470: */ + /*q2[ iout * q_dim + in ].dval = 1. / pivot; */ + mpf_set_si(dummy, 1); + mpf_div(q2[iout * q_dim + in].dval, dummy, pivot); + ii = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = ii; + ii = abs(ii); + if (iu[ii - 1] == 0 || iu[cu_dim + ii - 1] == 0) + { + goto L240; + } +/* switch column */ + for (i = 0; i < klm1; ++i) + { + /*z = q2[ i * q_dim + in ].dval; */ + mpf_set(z, q2[i * q_dim + in].dval); + /*q2[ i * q_dim + in ].dval = q2[ i * q_dim + js ].dval; */ + mpf_set(q2[i * q_dim + in].dval, q2[i * q_dim + js].dval); + /*q2[ i * q_dim + js ].dval = z; */ + mpf_set(q2[i * q_dim + js].dval, z); + } + i = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = q2[klm1 * q_dim + js].ival; + q2[klm1 * q_dim + js].ival = i; +/* L480: */ + ++js; + goto L240; +/* TEST FOR OPTIMALITY. */ + L490: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L490\n")); +#endif + if (kforce == 0) + { + if (iphase == 1) + { + /*if (q2[ klm * q_dim + n ].dval <= toler) { */ + if (mpf_cmp(q2[klm * q_dim + n].dval, toler) <= 0) + { + goto L500; + } +#ifdef DEBUG_CL1 + output_msg(sformatf( "q2[klm1-1, n1-1] > *toler. %e\n", + mpf_get_d(q2[(klm1 - 1) * q_dim + n1 - 1].dval))); +#endif + *kode = 1; + goto L590; + } + *kode = 0; + goto L590; + } + /*if (iphase != 1 || q2[ klm * q_dim + n ].dval > toler) { */ + if ((iphase != 1) || (mpf_cmp(q2[klm * q_dim + n].dval, toler) > 0)) + { + kforce = 0; + goto L240; + } +/* SET UP PHASE 2 COSTS. */ + L500: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 2 costs %d\n", *iter)); +#endif + iphase = 2; + for (j = 0; j < nklm; ++j) + { + /*cu[ j ] = 0.; */ + mpf_set_si(cu[j], 0); + } +/* L510: */ + for (j = n; j < nk; ++j) + { + /*cu[ j ] = 1.; */ + mpf_set_si(cu[j], 1); + } + /* + memcpy( (void *) &(cu[cu_dim]), (void *) &(cu[0]), (size_t) nklm * sizeof(LDBLE) ); + */ + for (i = 0; i < nklm; ++i) + { + mpf_set(cu[cu_dim + i], cu[i]); + } + +/* L520: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii <= 0) + { + if (iu[cu_dim - ii - 1] == 0) + { + continue; + } + /*cu[ cu_dim - ii - 1 ] = 0.; */ + mpf_set_si(cu[cu_dim - ii - 1], 0); + } + else + { +/* L530: */ + if (iu[ii - 1] == 0) + { + continue; + } + /*cu[ ii - 1 ] = 0.; */ + mpf_set_si(cu[ii - 1], 0); + } +/* L540: */ + ++ia; +/* switch row */ + /* + memcpy( (void *) &(scratch[0]), (void *) &(q2[ ia * q_dim]), + (size_t) n2 * sizeof(LDBLE) ); + memcpy( (void *) &(q2[ ia * q_dim ]), (void *) &(q2[ i * q_dim]), + (size_t) n2 * sizeof(LDBLE) ); + memcpy( (void *) &(q2[ i * q_dim ]), (void *) &(scratch[ 0 ]), + (size_t) n2 * sizeof(LDBLE) ); + */ + for (iswitch = 0; iswitch < n1; ++iswitch) + { + mpf_set(dummy, q2[ia * q_dim + iswitch].dval); + mpf_set(q2[ia * q_dim + iswitch].dval, + q2[i * q_dim + iswitch].dval); + mpf_set(q2[i * q_dim + iswitch].dval, dummy); + } + iswitch = q2[ia * q_dim + n1].ival; + q2[ia * q_dim + n1].ival = q2[i * q_dim + n1].ival; + q2[i * q_dim + n1].ival = iswitch; +/* L550: */ + } +/* L560: */ + goto L160; + + +/* PREPARE OUTPUT. */ + L590: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L590\n")); +#endif + /*sum = 0.; */ + mpf_set_si(sum, 0); + for (j = 0; j < n; ++j) + { + /*x[j] = 0.; */ + mpf_set_si(x[j], 0); + } +/* L600: */ + for (i = 0; i < klm; ++i) + { + /*res[i] = 0.; */ + mpf_set_si(res[i], 0); + } +/* L610: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + /*sn = 1.; */ + mpf_set_si(sn, 1); + if (ii < 0) + { + ii = -ii; + /*sn = -1.; */ + mpf_set_si(sn, -1); + } + if (ii <= n) + { +/* L620: */ + /*x[ii - 1] = sn * q2[ i * q_dim + n ].dval; */ + mpf_mul(x[ii - 1], sn, q2[i * q_dim + n].dval); + } + else + { +/* L630: */ + /*res[ii - n - 1] = sn * q2[ i * q_dim + n ].dval; */ + mpf_mul(res[ii - n - 1], sn, q2[i * q_dim + n].dval); + if (ii >= n1 && ii <= nk) + { +/* * DBLE(Q(I,N1)) */ + /*sum += q2[ i * q_dim + n ].dval; */ + mpf_add(sum, sum, q2[i * q_dim + n].dval); + } + } + } +/* L640: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L640\n")); +#endif + /* + * Check calculation + */ + mpf_set_si(dummy, 100); + mpf_mul(check_toler, toler, dummy); + if (check && *kode == 0) + { + /* + * Check optimization constraints + */ + if (*kode_arg == 1) + { + for (i = 0; i < k; ++i) + { + if (res_arg[i] < 0.0) + { + mpf_sub(dummy, res[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: optimization constraint not satisfied row %d, res %e, constraint %f.\n", + i, mpf_get_d(res[i]), res_arg[i])); +#endif + *kode = 1; + } + } + else if (res_arg[i] > 0.0) + { + mpf_add(dummy, res[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: optimization constraint not satisfied row %d, res %e, constraint %f.\n", + i, mpf_get_d(res[i]), res_arg[i])); +#endif + *kode = 1; + } + } + } + } + /* + * Check equalities + */ + for (i = k; i < k + l; ++i) + { + mpf_abs(dummy, res[i]); + if (mpf_cmp(dummy, check_toler) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: equality constraint not satisfied row %d, res %e, tolerance %e.\n", + i, mpf_get_d(res[i]), mpf_get_d(check_toler))); +#endif + + *kode = 1; + } + } + /* + * Check inequalities + */ + for (i = k + l; i < k + l + m; ++i) + { + mpf_neg(dummy, check_toler); + if (mpf_cmp(res[i], dummy) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: inequality constraint not satisfied row %d, res %e, tolerance %e.\n", + i, mpf_get_d(res[i]), mpf_get_d(check_toler))); +#endif + *kode = 1; + } + } + /* + * Check dissolution/precipitation constraints + */ + if (*kode_arg == 1) + { + for (i = 0; i < n; ++i) + { + if (x_arg[i] < 0.0) + { + mpf_sub(dummy, x[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: dis/pre constraint not satisfied column %d, x %e, constraint %f.\n", + i, mpf_get_d(x[i]), x_arg[i])); +#endif + *kode = 1; + } + } + else if (x_arg[i] > 0.0) + { + mpf_add(dummy, x[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: dis/pre constraint not satisfied column %d, x %e, constraint %f.\n", + i, mpf_get_d(x[i]), x_arg[i])); +#endif + *kode = 1; + } + } + } + } + if (*kode == 1) + { + output_msg(sformatf( + "\n\tCL1MP: Roundoff errors in optimization.\n\t Deleting model.\n")); + } + } + /* + * set return variables + */ + /**error = sum;*/ + mpf_set(error, sum); + *error_arg = mpf_get_d(error); + *kode_arg = *kode; + for (i = 0; i < n2d; ++i) + { + x_arg[i] = mpf_get_d(x[i]); + } + for (i = 0; i < k + l + m; ++i) + { + res_arg[i] = mpf_get_d(res[i]); + } + + /*scratch = free_check_null (scratch); */ + + for (i = 0; i < max_row_count * max_column_count; ++i) + { + mpf_clear(q[i]); + } + q = (mpf_t *) free_check_null(q); + for (i = 0; i < n2d; ++i) + { + mpf_clear(x[i]); + } + x = (mpf_t *) free_check_null(x); + for (i = 0; i < k + l + m; ++i) + { + mpf_clear(res[i]); + } + res = (mpf_t *) free_check_null(res); + for (i = 0; i < 2 * nklmd; ++i) + { + mpf_clear(cu[i]); + } + cu = (mpf_t *) free_check_null(cu); + mpf_clear(zero); + mpf_clear(dummy); + mpf_clear(dummy1); + mpf_clear(sum); + mpf_clear(error); + mpf_clear(z); + mpf_clear(zu); + mpf_clear(zv); + mpf_clear(xmax); + mpf_clear(minus_one); + mpf_clear(toler); + mpf_clear(check_toler); + mpf_clear(pivot); + mpf_clear(xmin); + mpf_clear(cuv); + mpf_clear(tpivot); + mpf_clear(sn); + mpf_clear(censor_tol); + kode = (int *) free_check_null(kode); + return 0; +} +#endif // INVERSE_CL1MP diff --git a/phreeqcpp/class_main.cpp b/phreeqcpp/class_main.cpp new file mode 100644 index 00000000..74721e86 --- /dev/null +++ b/phreeqcpp/class_main.cpp @@ -0,0 +1,643 @@ +#include "Phreeqc.h" + +#include "NameDouble.h" +#include "Solution.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Exchange.h" +#include "Surface.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +//#include +//#include + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- + * MAIN + * ---------------------------------------------------------------------- */ +int +main(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + // check for floating point exceptions on Linux + // feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); +//#define WIN32_MEMORY_DEBUG +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ +#ifdef SKIP + // Send messages (leaks) to stderr + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); +#endif + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 31195; +#endif +#ifdef SKIP +//Set the x86 floating-point control word according to what +//exceptions you want to trap. +_clearfp(); //Always call _clearfp before setting the control + //word +//Because the second parameter in the following call is 0, it +//only returns the floating-point control word +unsigned int cw = _controlfp(0, 0); //Get the default control + //word +//Set the exception masks off for exceptions that you want to +//trap. When a mask bit is set, the corresponding floating-point +//exception is //blocked from being generating. +cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE| + EM_DENORMAL|EM_INVALID); +//For any bit in the second parameter (mask) that is 1, the +//corresponding bit in the first parameter is used to update +//the control word. +unsigned int cwOriginal = _controlfp(cw, MCW_EM); //Set it. + //MCW_EM is defined in float.h. + //Restore the original value when done: + //_controlfp(cwOriginal, MCW_EM); +#endif + Phreeqc phreeqc_instance; + return phreeqc_instance.main_method(argc, argv); +} +//#define TEST_COPY +#ifdef TEST_COPY +int Phreeqc:: +main_method(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + int errors; + std::istream *db_cookie = NULL; + std::istream *input_cookie = NULL; +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 9482; +#endif + + phast = FALSE; +/* + * Open input/output files + */ + errors = process_file_names(argc, argv, &db_cookie, &input_cookie, TRUE); + if (errors != 0) + { + return errors; + } +#ifndef NO_UTF8_ENCODING +#ifdef DOS + SetConsoleOutputCP(CP_UTF8); +#endif + write_banner(); +#endif + +/* + * Initialize arrays + */ + errors = do_initialize(); + if (errors != 0) + { + return errors; + } +/* + * Load database into memory + */ + this->phrq_io->push_istream(db_cookie); + errors = read_database(); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + Phreeqc MyCopy = *this; + this->clean_up(); + // this->init(); + // this->initialize(); +/* + * Read input data for simulation + */ + + MyCopy.phrq_io->push_istream(input_cookie); + errors = MyCopy.run_simulations(); + + //Phreeqc mycopy(*this); + MyCopy.phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } +/* + * Display successful status + */ + pr.headings = TRUE; + // errors = do_status(); + if (errors != 0) + { + return errors; + } + return 0; +} +#else +int Phreeqc:: +main_method(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + int errors; + std::istream *db_cookie = NULL; + std::istream *input_cookie = NULL; +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 9482; +#endif + try + { + phast = FALSE; + /* + * Open input/output files + */ + errors = process_file_names(argc, argv, &db_cookie, &input_cookie, TRUE); + if (errors != 0) + { + return errors; + } +#ifndef NO_UTF8_ENCODING +#ifdef DOS + SetConsoleOutputCP(CP_UTF8); +#endif + write_banner(); +#endif + + /* + * Initialize arrays + */ + errors = do_initialize(); + if (errors != 0) + { + return errors; + } + /* + * Load database into memory + */ + this->phrq_io->push_istream(db_cookie); + errors = read_database(); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + + /* + * Read input data for simulation + */ + + this->phrq_io->push_istream(input_cookie); + errors = run_simulations(); + + //Phreeqc mycopy(*this); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + /* + * Display successful status + */ + pr.headings = TRUE; + errors = do_status(); + if (errors != 0) + { + return errors; + } + } + catch (...) + { + int e = get_input_errors(); + std::cerr << "Unhandled exception in PHREEQC." << std::endl; + if (e > 0) + { + return e; + } + else + { + return 1; + } + } + return 0; +} +#endif //TEST_COPY +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_banner(void) +/* ---------------------------------------------------------------------- */ +{ +#ifdef TESTING + return OK; +#endif +#ifndef NO_UTF8_ENCODING + char buffer[80]; + int len, indent; + screen_msg( + " █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█\n"); + screen_msg( + " â•‘ â•‘\n"); + + /* version */ +#ifdef NPP + len = sprintf(buffer, "* PHREEQC-%s *", "3.7.1"); +#else + len = sprintf(buffer, "* PHREEQC-%s *", "@VERSION@"); +#endif + indent = (44 - len) / 2; + screen_msg(sformatf("%14câ•‘%*c%s%*câ•‘\n", ' ', indent, ' ', buffer, + 44 - indent - len, ' ')); + + screen_msg( + " â•‘ â•‘\n"); + screen_msg( + " â•‘ A hydrogeochemical transport model â•‘\n"); + screen_msg( + " â•‘ â•‘\n"); + screen_msg( + " â•‘ by â•‘\n"); + screen_msg( + " â•‘ D.L. Parkhurst and C.A.J. Appelo â•‘\n"); + screen_msg( + " â•‘ â•‘\n"); + + + /* date */ +#ifdef NPP + len = sprintf(buffer, "%s", "July 5, 2021"); +#else + len = sprintf(buffer, "%s", "@VER_DATE@"); +#endif + indent = (44 - len) / 2; + screen_msg(sformatf("%14câ•‘%*c%s%*câ•‘\n", ' ', indent, ' ', buffer, + 44 - indent - len, ' ')); + + screen_msg( + " █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█\n\n"); +#endif + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +process_file_names(int argc, char *argv[], std::istream **db_cookie, + std::istream **input_cookie, int log) +/* ---------------------------------------------------------------------- */ +{ + std::string token, default_name; + std::string query; + std::string in_file, out_file, db_file; + char *env_ptr; + const char* cptr; +/* + * Prepare error handling + */ + try { + if (phrq_io == NULL) + { + std::cerr << "No PHRQ_io output handler defined in process_file_names" << "\n"; + } +/* + * Prep for get_line + */ + max_line = MAX_LINE; + space((void **) ((void *) &line), INIT, &max_line, sizeof(char)); + space((void **) ((void *) &line_save), INIT, &max_line, sizeof(char)); +/* + * Open error ostream + */ + if (argc > 4) + { + if (!phrq_io->error_open(argv[4])) + { + error_string = sformatf( "Error opening file, %s.", argv[4]); + warning_msg(error_string); + } + } + else + { + phrq_io->error_open(NULL); + } +/* + * Open user-input file + */ + query = "Name of input file?"; + std::ifstream * local_input_stream = NULL; + if (argc <= 1) + { + default_name.clear(); + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, false); + } + else + { + default_name = argv[1]; + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, true); + } + screen_msg(sformatf("Input file: %s\n\n", default_name.c_str())); + in_file = default_name; +/* + * Open file for output + */ + query = "Name of output file?"; + cptr = default_name.c_str(); + copy_token(token, &cptr); + token = default_name; + token.append(".out"); + std::ofstream * local_output_stream = NULL; + if (argc <= 1) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, false); + } + else if (argc == 2) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + else if (argc >= 3) + { + token = argv[2]; + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + screen_msg(sformatf("Output file: %s\n\n", token.c_str())); + out_file = token; + phrq_io->Set_output_ostream(local_output_stream); +/* + * Open log file + */ + if (log == TRUE) + { + if (!phrq_io->log_open("phreeqc.log")) + { + error_msg("Cannot open log file, phreeqc.log.", STOP); + } + } +/* + * Read input file for DATABASE keyword + */ + if (local_input_stream->is_open()) + { + phrq_io->push_istream(local_input_stream); + if (get_line() == KEYWORD) + { + cptr = line; + copy_token(token, &cptr); + if (strcmp_nocase(token.c_str(), "database") == 0) + { + user_database = cptr; + string_trim(user_database); + if (user_database.size() == 0) + { + warning_msg("DATABASE file name is missing; default database will be used."); + } + } + } + phrq_io->pop_istream(); + } + else + { + delete local_input_stream; + error_string = sformatf( "Error opening file, %s.", in_file.c_str()); + error_msg(error_string, STOP); + } + +/* + * Open data base + */ + query = "Name of database file?"; + env_ptr = getenv("PHREEQC_DATABASE"); + if (user_database.size() > 0) + { + token = user_database; + } + else if (env_ptr != NULL) + { + token = env_ptr; + } + else + { + token = default_data_base; + } + + std::ifstream * local_database_file = NULL; + if (argc <= 1) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, false); + } + else if (argc < 4) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + else if (argc >= 4) + { + if (user_database.size() == 0) + { + token = argv[3]; + } + else + { +#ifndef PHREEQCI_GUI + warning_msg ("Database file from DATABASE keyword is used; command line argument ignored."); +#endif + } + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + local_database_file->close(); + delete local_database_file; + user_database = token; + screen_msg(sformatf("Database file: %s\n\n", token.c_str())); + db_file = token; + output_msg(sformatf(" Input file: %s\n", in_file.c_str())); + output_msg(sformatf(" Output file: %s\n", out_file.c_str())); +#ifdef NPP + output_msg(sformatf("Using PHREEQC: version 3.7.1, compiled July 5, 2021\n")); +#endif + output_msg(sformatf("Database file: %s\n\n", token.c_str())); +#ifdef NPP + output_flush(); +#endif + /* + * local cleanup + */ + + line = (char *) free_check_null(line); + line_save = (char *) free_check_null(line_save); + + *db_cookie = new std::ifstream(db_file.c_str(), std::ios_base::in); + *input_cookie = new std::ifstream(in_file.c_str(), std::ios_base::in); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} +/* ---------------------------------------------------------------------- */ +std::ifstream * Phreeqc:: +open_input_stream(std::string query, std::string& default_name, std::ios_base::openmode mode, bool batch) +/* ---------------------------------------------------------------------- */ +{ + std::string name; + std::ifstream *new_stream; + std::ostream * error_ostream_save = phrq_io->Get_error_ostream(); + + for (;;) + { +/* + * Get file name + */ + name = default_name; + if (!batch ) + { + phrq_io->Set_error_ostream(&std::cerr); + screen_msg(sformatf("%s\n", query.c_str())); + if (default_name.size() > 0) + { + screen_msg(sformatf("Default: %s\n", default_name.c_str())); + } + std::getline(std::cin, name); + if (name.size() == 0 && default_name.size() == 0) + { + std::cerr << "No name defined." << std::endl; + continue; + } + if (name.size() == 0) + { + name = default_name; + } + } +/* + * Open existing file to read + */ + new_stream = new std::ifstream(name.c_str(), mode); + if (new_stream == NULL || !new_stream->is_open()) + { + phrq_io->Set_error_ostream(&std::cerr); + error_string = sformatf( "\nERROR: Cannot open file, %s.\n", name.c_str()); + screen_msg(error_string); +#ifdef NPP + error_msg(sformatf( "\nERROR: Cannot open file, %s.\n Please check, and give the correct, full path + name.\n", name.c_str()), STOP); + break; +#endif + error_flush(); + batch = FALSE; + continue; + } + break; + } + default_name = name; + if (!batch ) + { + phrq_io->Set_error_ostream(error_ostream_save); + } + return (new_stream); +} +/* ---------------------------------------------------------------------- */ +std::ofstream * Phreeqc:: +open_output_stream(std::string query, std::string& default_name, std::ios_base::openmode mode, bool batch) +/* ---------------------------------------------------------------------- */ +{ + std::string name; + std::ofstream *new_stream; + std::ostream * error_ostream_save = phrq_io->Get_error_ostream(); + + for (;;) + { +/* + * Get file name + */ + name = default_name; + if (!batch ) + { + phrq_io->Set_error_ostream(&std::cerr); + screen_msg(sformatf("%s\n", query.c_str())); + if (default_name[0] != '\0') + { + screen_msg(sformatf("Default: %s\n", default_name.c_str())); + } + std::getline(std::cin, name); + if (name.size() == 0 && default_name.size() == 0) + { + std::cerr << "No name defined." << std::endl; + } + if (name.size() == 0) + { + name = default_name; + } + } +/* + * Open existing file to read + */ + new_stream = new std::ofstream(name.c_str(), mode); + if (new_stream == NULL || !new_stream->is_open()) + { + phrq_io->Set_error_ostream(&std::cerr); + error_string = sformatf( "\nERROR: Cannot open file, %s.\n", name.c_str()); + screen_msg(error_string); + error_flush(); + batch = FALSE; + continue; + } + break; + } + default_name = name; + if (!batch ) + { + phrq_io->Set_error_ostream(error_ostream_save); + } + return (new_stream); +} diff --git a/phreeqcpp/common/.gitlab-ci.yml b/phreeqcpp/common/.gitlab-ci.yml new file mode 100644 index 00000000..408cc696 --- /dev/null +++ b/phreeqcpp/common/.gitlab-ci.yml @@ -0,0 +1,46 @@ +# +# https://code.chs.usgs.gov/coupled/subtrees/phreeqc3-src-common +# SRC 2020-12-02T18:39:55-07:00 +# +image: ${CI_REGISTRY}/coupled/containers/buildpack-deps:bionic-scm + +stages: + - trigger + +before_script: + - eval $(ssh-agent -s) + - echo "${SSH_PRIVATE_KEY_ENC}" | base64 --decode | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - ssh-keyscan ${CI_SERVER_HOST} >> ~/.ssh/known_hosts + - chmod 644 ~/.ssh/known_hosts + - git config --global user.email "darth@empire.com" + - git config --global user.name "Darth Vader" + +trigger-downstream: + stage: trigger + ## + ## Only run if on the master branch and the variable GROUP is set + ## + ## change this to + ## only: + ## - master@$GROUP/subtrees/phreeqc3-src-common + ## and set GROUP to coupled before merge + only: + refs: + - master + variables: + - $GROUP + + ## Downstream Projects + ## triggers and ids are stored at the group level + ## phreeqc3-src https://code.chs.usgs.gov/coupled/subtrees/phreeqc3-src + ## wphast https://code.chs.usgs.gov/coupled/wphast + script: + - echo triggering phreeqc3-src + - curl -X POST -F token=${PHREEQC3_SRC_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${PHREEQC3_SRC_ID}/trigger/pipeline + - echo triggering wphast + - curl -X POST -F token=${WPHAST_TRIGGER} -F ref=master https://code.chs.usgs.gov/api/v4/projects/${WPHAST_ID}/trigger/pipeline + + ## Upstream Projects + ## none diff --git a/phreeqcpp/common/PHRQ_base.cxx b/phreeqcpp/common/PHRQ_base.cxx new file mode 100644 index 00000000..6e48faaa --- /dev/null +++ b/phreeqcpp/common/PHRQ_base.cxx @@ -0,0 +1,117 @@ +#include "PHRQ_base.h" +#include +#include "PHRQ_io.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +PHRQ_base:: +PHRQ_base(void) +{ + this->io = NULL; + base_error_count = 0; +} + +PHRQ_base:: +PHRQ_base(PHRQ_io * p_io) +{ + this->io = p_io; + base_error_count = 0; +} + +PHRQ_base:: +~PHRQ_base() +{ +} + +void PHRQ_base:: +error_msg(const std::string & stdstr, int stop) +{ + this->base_error_count++; + std::ostringstream msg; + msg << "ERROR: " << stdstr << "\n"; + if (this->io) + { + this->io->output_msg(msg.str().c_str()); + this->io->log_msg(msg.str().c_str()); + this->io->error_msg("\n"); + this->io->error_msg(msg.str().c_str(), stop!=0); + } + else + { +#if !defined(R_SO) + std::cerr << msg.str().c_str(); + std::cout << msg.str().c_str(); +#endif + } + if (stop != 0) + { + throw PhreeqcStop(); + } +} + +void PHRQ_base:: +warning_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->warning_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cerr << stdstr << "\n"; + std::cout << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +output_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->output_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cout << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +screen_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->screen_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cerr << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +echo_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->echo_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cout << stdstr << "\n"; +#endif + } +} diff --git a/phreeqcpp/common/PHRQ_base.h b/phreeqcpp/common/PHRQ_base.h new file mode 100644 index 00000000..26ac25d6 --- /dev/null +++ b/phreeqcpp/common/PHRQ_base.h @@ -0,0 +1,48 @@ +#ifndef _PHRQBASE_H +#define _PHRQBASE_H + +#include + +#include "PHRQ_exports.h" + +class PHRQ_io; +class IPQ_DLL_EXPORT PHRQ_base +{ +public: + // constructors + PHRQ_base(void); + PHRQ_base(PHRQ_io *); + virtual ~ PHRQ_base(); + + + // methods + void output_msg(const std::string &); + void error_msg(const std::string &, int stop=0); + void warning_msg(const std::string &); + void screen_msg(const std::string &); + void echo_msg(const std::string &); + + + void Set_io(PHRQ_io * p_io) + { + this->io = p_io; + } + PHRQ_io * Get_io(void) + { + return this->io; + } + void Set_base_error_count(int i) + { + this->base_error_count = i; + } + int Get_base_error_count(void) + { + return this->base_error_count; + } + // data +protected: + PHRQ_io * io; + int base_error_count; +}; + +#endif /* _PHRQBASE_H */ diff --git a/phreeqcpp/common/PHRQ_exports.h b/phreeqcpp/common/PHRQ_exports.h new file mode 100644 index 00000000..3e74673e --- /dev/null +++ b/phreeqcpp/common/PHRQ_exports.h @@ -0,0 +1,20 @@ +#ifndef INC_PHRQ_EXPORTS_H +#define INC_PHRQ_EXPORTS_H + +#if defined(WIN32) +# if defined(PHREEQCI_GUI) +# ifndef WINVER +# define WINVER 0x0400 +# endif +# include +# endif +# include +#endif + +#if defined(_WINDLL) && defined(IPhreeqc_EXPORTS) +# define IPQ_DLL_EXPORT __declspec(dllexport) +#else +# define IPQ_DLL_EXPORT +#endif + +#endif // INC_PHRQ_EXPORTS_H diff --git a/phreeqcpp/common/PHRQ_io.cpp b/phreeqcpp/common/PHRQ_io.cpp new file mode 100644 index 00000000..15449606 --- /dev/null +++ b/phreeqcpp/common/PHRQ_io.cpp @@ -0,0 +1,913 @@ +#include +#include "PHRQ_io.h" +#include "Parser.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +PHRQ_io:: +PHRQ_io(void) +{ + output_ostream = NULL; + log_ostream = NULL; + punch_ostream = NULL; +#ifdef ERROR_OSTREAM + error_ostream = NULL; +#else + error_file = NULL; +#endif + dump_ostream = NULL; + io_error_count = 0; + + output_on = true; + log_on = false; + punch_on = true; + error_on = true; + dump_on = true; + echo_on = true; + screen_on = true; + echo_destination = ECHO_OUTPUT; + + m_next_keyword = Keywords::KEY_NONE; + accumulate = false; + m_line_type = PHRQ_io::LT_EMPTY; +} + +PHRQ_io:: +~PHRQ_io() +{ +} +// ---------------------------------------------------------------------- */ +// output ostream methods +// ---------------------------------------------------------------------- */ + +bool PHRQ_io:: +ofstream_open(std::ostream **os, const char *file_name, std::ios_base::openmode mode) +{ + std::ofstream *ofs = new std::ofstream(file_name, mode); + if (ofs && ofs->is_open()) + { + *os = ofs; + return true; + } + delete ofs; + return false; +} + +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +output_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&output_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (output_ostream) + { + output_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&output_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (output_ostream != NULL && output_on) + { + (*output_ostream) << str; + } + //output_flush(); +} +// ---------------------------------------------------------------------- */ +// log ostream methods +// ---------------------------------------------------------------------- */ +bool PHRQ_io:: +log_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&log_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (log_ostream) + { + log_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&log_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (log_ostream != NULL && log_on) + { + (*log_ostream) << str; + } +} +// ---------------------------------------------------------------------- */ +// punch ostream methods +// ---------------------------------------------------------------------- */ +bool PHRQ_io:: +punch_open(const char *file_name, std::ios_base::openmode mode, int n_user) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&punch_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream) + { + punch_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&punch_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + (*punch_ostream) << str; + } +} +// ---------------------------------------------------------------------- */ +// error file methods +// ---------------------------------------------------------------------- */ +#ifdef ERROR_OSTREAM +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +error_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + if (file_name != NULL) + { + if (!ofstream_open(&error_ostream, file_name, mode)) + { +#if !defined(R_SO) + error_ostream = &std::cerr; +#endif + return false; + } + } + else + { +#if !defined(R_SO) + error_ostream = &std::cerr; +#endif + } + return true; +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream) + { + error_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&error_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + + io_error_count++; + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << err_str; + screen_msg(err_str); + error_flush(); + } + if (stop) + { + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << "Stopping.\n"; + screen_msg("Stopping.\n"); + error_ostream->flush(); + } + output_msg("Stopping.\n"); + log_msg("Stopping.\n"); + + throw PhreeqcStop(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << err_str << "\n"; + std::string err_stdstr(err_str); + err_stdstr.append("\n"); + screen_msg(err_stdstr.c_str()); + error_ostream->flush(); + } + std::ostringstream warn_str; + warn_str << err_str << "\n"; + log_msg(warn_str.str().c_str()); + log_flush(); + output_msg(warn_str.str().c_str()); + output_flush(); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +screen_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream != NULL && screen_on) + { + (*error_ostream) << str; + } +} +#else +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +error_open(const char *file_name, const char * mode) +/* ---------------------------------------------------------------------- */ +{ + if (file_name != NULL) + { + if ((error_file = fopen(file_name, mode)) == NULL) + { + error_file = stderr; + return false; + } + } + else + { + error_file = stderr; + } + return true; +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (error_file) + { + fflush(error_file); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&error_file); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + + io_error_count++; + if (error_file != NULL && error_on) + { + //(*error_file) << err_str; + screen_msg(err_str); + error_flush(); + } + if (stop) + { + if (error_file != NULL && error_on) + { + //(*error_file) << "Stopping.\n"; + screen_msg("Stopping.\n"); + fflush(error_file); + } + output_msg("Stopping.\n"); + log_msg("Stopping.\n"); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (error_file != NULL && error_on) + { + //(*error_file) << err_str << "\n"; + std::string err_stdstr(err_str); + err_stdstr.append("\n"); + screen_msg(err_stdstr.c_str()); + error_flush(); + } + std::ostringstream warn_str; + warn_str << err_str << "\n"; + log_msg(warn_str.str().c_str()); + log_flush(); + output_msg(warn_str.str().c_str()); + output_flush(); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +screen_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + std::string stdstr(str); + if (error_file != NULL && screen_on) + { + fprintf(error_file, "%s", str); + } +} +#endif +// ---------------------------------------------------------------------- */ +// dump ostream methods +// ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +dump_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&dump_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (dump_ostream) + { + dump_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&dump_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (dump_ostream != NULL && dump_on) + { + (*dump_ostream) << str; + } +} +int PHRQ_io:: +getc(void) +{ + if (std::istream* is = get_istream()) + { + int n = is->get(); + if (n == 13 && is->peek() == 10) + { + n = is->get(); + } + return n; + } + return EOF; +} + +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, double d) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, d); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, char * s) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, s); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, int d) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, d); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf_helper(std::ostream *os, const char *format, ...) +/* ---------------------------------------------------------------------- */ +{ + if (os) + { + const size_t STACK_MAX = 2048; + char stack_buffer[STACK_MAX]; + + va_list args; + va_start(args, format); + int j = ::vsnprintf(stack_buffer, STACK_MAX, format, args); + bool success = (j >= 0 && j < (int) STACK_MAX); + va_end(args); + + if (success) + { + (*os) << stack_buffer; + } + else + { + size_t alloc_buffer_size = STACK_MAX * 2; + char *alloc_buffer = new char[alloc_buffer_size]; + do + { + va_list args; + va_start(args, format); + j = ::vsnprintf(alloc_buffer, alloc_buffer_size, format, args); + success = (j >= 0 && j < (int) alloc_buffer_size); + va_end(args); + if (!success) + { + delete[] alloc_buffer; + alloc_buffer_size *= 2; + alloc_buffer = new char[alloc_buffer_size]; + } + } + while (!success); + + (*os) << alloc_buffer; + delete[] alloc_buffer; + } + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf_helper(std::string *str, const char *format, ...) +/* ---------------------------------------------------------------------- */ +{ + if (str) + { + const size_t STACK_MAX = 2048; + char stack_buffer[STACK_MAX]; + + va_list args; + va_start(args, format); + int j = ::vsnprintf(stack_buffer, STACK_MAX, format, args); + bool success = (j >= 0 && j < (int) STACK_MAX); + va_end(args); + + if (success) + { + (*str) += stack_buffer; + } + else + { + size_t alloc_buffer_size = STACK_MAX * 2; + char *alloc_buffer = new char[alloc_buffer_size]; + do + { + va_list args; + va_start(args, format); + j = ::vsnprintf(alloc_buffer, alloc_buffer_size, format, args); + success = (j >= 0 && j < (int) alloc_buffer_size); + va_end(args); + if (!success) + { + delete[] alloc_buffer; + alloc_buffer_size *= 2; + alloc_buffer = new char[alloc_buffer_size]; + } + } + while (!success); + + (*str) += alloc_buffer; + delete[] alloc_buffer; + } + } +} + +/* ---------------------------------------------------------------------- */ +void PHRQ_io::fpunchf_end_row(const char *format) +/* ---------------------------------------------------------------------- */ +{ + //NOOP for Phreeqc +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +close_ostreams(void) +/* ---------------------------------------------------------------------- */ +{ + std::set streams; + + streams.insert(output_ostream); + streams.insert(log_ostream); +// streams.insert(punch_ostream); // Should be deleted in ~SelectedOutput +#ifdef ERROR_OSTREAM + streams.insert(error_ostream); +#else + safe_close(&error_file); +#endif + streams.insert(dump_ostream); + + std::set::iterator it = streams.begin(); + for (; it != streams.end(); it++) + { + std::ostream * x = *it; + safe_close(&x); + } + + output_ostream = NULL; + log_ostream = NULL; + punch_ostream = NULL; +#ifdef ERROR_OSTREAM + error_ostream = NULL; +#else + error_file = NULL; +#endif + dump_ostream = NULL; +} +//safe_close is static method +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +safe_close(std::ostream **stream_ptr) +/* ---------------------------------------------------------------------- */ +{ + if ( +#if !defined(R_SO) + *stream_ptr != &std::cerr && + *stream_ptr != &std::cout && + *stream_ptr != &std::clog && +#endif + *stream_ptr != NULL) + { + delete *stream_ptr; + *stream_ptr = NULL; + } +} +void PHRQ_io:: +safe_close(FILE **file_ptr) +/* ---------------------------------------------------------------------- */ +{ + if ( +#if !defined(R_SO) + *file_ptr != stderr && + *file_ptr != stdout && + *file_ptr != stdin && +#endif + *file_ptr != NULL) + { + fclose(*file_ptr); + *file_ptr = NULL; + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +echo_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (echo_on) + { + switch (this->echo_destination) + { + case ECHO_LOG: + log_msg(str); + break; + case ECHO_OUTPUT: + output_msg(str); + break; + } + } +} + +std::istream * PHRQ_io:: +get_istream() +{ + if (istream_list.size() > 0) + { + return istream_list.front(); + } + else + { + return NULL; + } +} +void PHRQ_io:: +push_istream(std::istream * cookie, bool auto_delete) +{ + istream_list.push_front(cookie); + delete_istream_list.push_front(auto_delete); +} +void PHRQ_io:: +clear_istream(void) +{ + while (istream_list.size() > 0) + { + pop_istream(); + } +} +void PHRQ_io:: +pop_istream() +{ + if (istream_list.size() > 0) + { + if (delete_istream_list.front()) + { + delete istream_list.front(); + } + istream_list.pop_front(); + delete_istream_list.pop_front(); + } +} +/* ---------------------------------------------------------------------- */ +PHRQ_io::LINE_TYPE PHRQ_io:: +get_line(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line from input file put in "line". + * Copy of input line is stored in "line_save". + * Characters after # are discarded in line but retained in "line_save" + * + * Arguments: + * fp is file name + * Returns: + * EMPTY, + * EOF, + * KEYWORD, + * OK, + * OPTION + */ + std::string stdtoken; + bool continue_loop = true;; + + PHRQ_io::LINE_TYPE return_value; + // loop for include files + for (;;) + { + if (this->get_istream() == NULL) + { + break; + } + return_value = LT_EMPTY; + while (return_value == LT_EMPTY) + { + /* + * Eliminate all characters after # sign as a comment + */ + /* + * Get line, check for eof + */ + continue_loop = false; + + if (get_logical_line() == LT_EOF) + { + //pop next file + this->pop_istream(); + continue_loop = true; + break; + } + /* + * Get long lines + */ + bool empty = true; + m_line = m_line_save.substr(0, m_line_save.find_first_of('#')); + for (unsigned int i = 0; i < m_line.size(); ++i) + { + if (!::isspace(m_line[i])) + { + empty = false; + break; + } + } + + if (this->accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + // + // New line character encountered + // + return_value = (empty ? LT_EMPTY : LT_OK); + + } + if (continue_loop) continue; + // + // Determine return_value + // + if (return_value == LT_OK) + { + if (check_key(m_line.begin(), m_line.end())) + { + return_value = LT_KEYWORD; + } + else + { + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + std::string token; + CParser::copy_token(token, beg, end); + + if (token.size() > 1 && token[0] == '-' &&::isalpha(token[1])) + { + return_value = LT_OPTION; + } + } + } + + // add new include file to stack + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + CParser::copy_token(stdtoken, beg, end); + std::transform(stdtoken.begin(), stdtoken.end(), stdtoken.begin(), ::tolower); + if ((strstr(stdtoken.c_str(),"include$") == stdtoken.c_str()) || + (strstr(stdtoken.c_str(),"include_file") == stdtoken.c_str())) + { + std::string file_name; + file_name.assign(beg, end); + file_name = trim(file_name); + + if (file_name.size() > 0) + { + std::ifstream *next_stream = new std::ifstream(file_name.c_str(), std::ios_base::in); + if (!next_stream->is_open()) + { + std::ostringstream errstr; + errstr << "\n*********** Could not open include file " << file_name + <<".\n Please, write the full path to this file. ***********\n\n"; + delete next_stream; +#if defined(PHREEQCI_GUI) + warning_msg(errstr.str().c_str()); + continue; +#else + output_msg(errstr.str().c_str()); + output_flush(); + error_msg(errstr.str().c_str(), OT_STOP); +#endif + } + else + { + this->push_istream(next_stream); + std::ostringstream errstr; + errstr << "\n\tReading data from " << file_name <<" ...\n"; + output_msg(errstr.str().c_str()); + } + continue; + } + } + return return_value; + } + m_next_keyword = Keywords::KEY_END; + return LT_EOF; +} + +/** + Reads input stream until end of line, ";", or eof + stores characters in line_save + + returns: + EOF on empty line on end of file or + OK otherwise +*/ +PHRQ_io::LINE_TYPE PHRQ_io:: +get_logical_line(void) +{ + int j; + unsigned int pos; + char c; + + m_line_save.erase(m_line_save.begin(), m_line_save.end()); // m_line_save.clear(); + while ((j = getc()) != EOF) + { + c = (char) j; + if (c == '#') + { + // ignore all chars after # until newline + do + { + c = (char) j; + if (c == '\n') + { + break; + } + m_line_save += c; + } + while ((j = getc()) != EOF); + } + if (c == ';') + break; + if (c == '\n') + { + break; + } + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + while ((j = getc()) != EOF) + { + c = (char) j; + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + continue; + } + if (c == '\n') + { + // remove '\\' + m_line_save = m_line_save.substr(0,pos); + break; + } + m_line_save += c; + if (!::isspace(j)) + break; + } + } + else + { + m_line_save += c; + } + } + if (j == std::char_traits < char >::eof() && m_line_save.size() == 0) + { + return (LT_EOF); + } + return (LT_OK); +} + +bool PHRQ_io:: +check_key(std::string::iterator begin, std::string::iterator end) +{ + std::string lowercase; + CParser::copy_token(lowercase, begin, end); + std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), + tolower); + + m_next_keyword = Keywords::Keyword_search(lowercase); + if (m_next_keyword == Keywords::KEY_NONE) + { + return false; + } + return true; +} + diff --git a/phreeqcpp/common/PHRQ_io.h b/phreeqcpp/common/PHRQ_io.h new file mode 100644 index 00000000..25bfec8a --- /dev/null +++ b/phreeqcpp/common/PHRQ_io.h @@ -0,0 +1,207 @@ +#ifndef _PHRQIO_H +#define _PHRQIO_H + +#include "PHRQ_exports.h" + +#include +#include +#include +#include "Keywords.h" +#include + +#define ERROR_OSTREAM + +class PhreeqcStop : public std::exception +{ +}; + +class IPQ_DLL_EXPORT PHRQ_io +{ +public: + enum LINE_TYPE + { + LT_EOF = -1, + LT_OK = 1, + LT_EMPTY = 2, + LT_KEYWORD = 3, + LT_OPTION = 8 + }; + + enum ONERROR_TYPE + { + OT_CONTINUE = 0, + OT_STOP = 1 + }; + // constructor/destructor + PHRQ_io(void); + virtual ~ PHRQ_io(); + + // methods + static void safe_close(std::ostream **stream_ptr); + static void safe_close(FILE **file_ptr); + void close_ostreams(void); + void Set_io_error_count(int i) {this->io_error_count = i;}; + int Get_io_error_count(void)const {return this->io_error_count;}; + + + // istreams + std::istream *get_istream(); + void pop_istream(); + void push_istream(std::istream * cookie, bool auto_delete = true); + void clear_istream(void); + + // helper + bool ofstream_open(std::ostream **os, const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + + // output_ostream + virtual bool output_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void output_flush(void); + void output_close(void); + virtual void output_msg(const char * str); + void Set_output_ostream(std::ostream * out) {this->output_ostream = out;}; + std::ostream *Get_output_ostream(void) {return this->output_ostream;}; + void Set_output_on(bool tf) {this->output_on = tf;}; + bool Get_output_on(void)const {return this->output_on;}; + + // log_ostream + virtual bool log_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void log_flush(void); + void log_close(void); + virtual void log_msg(const char * str); + void Set_log_ostream(std::ostream * out) {this->log_ostream = out;} + std::ostream *Get_log_ostream(void) {return this->log_ostream;} + void Set_log_on(bool tf) {this->log_on = tf;} + bool Get_log_on(void)const {return this->log_on;} + + // punch_ostream + virtual bool punch_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out, int n_user = 1); + void punch_flush(void); + void punch_close(void); + virtual void punch_msg(const char * str); + void Set_punch_ostream(std::ostream * out) {this->punch_ostream = out;} + std::ostream *Get_punch_ostream(void) {return this->punch_ostream;} + void Set_punch_on(bool tf) {this->punch_on = tf;} + bool Get_punch_on(void) {return this->punch_on;} + + // error_ostream +#ifdef ERROR_OSTREAM + virtual bool error_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void error_flush(void); + void error_close(void); + virtual void error_msg(const char * str, bool stop=false); + void Set_error_ostream(std::ostream * out) {this->error_ostream = out;} + std::ostream *Get_error_ostream(void)const {return this->error_ostream;} + void Set_error_on(bool tf) {this->error_on = tf;} + bool Get_error_on(void)const {return this->error_on;} + virtual void warning_msg(const char *err_str); +#else + virtual bool error_open(const char *file_name, const char * mode = "w"); + void error_flush(void); + void error_close(void); + virtual void error_msg(const char * str, bool stop=false); + void Set_error_file(FILE * out) {this->error_file = out;} + FILE *Get_error_file(void)const {return this->error_file;} + void Set_error_on(bool tf) {this->error_on = tf;} + bool Get_error_on(void)const {return this->error_on;} + virtual void warning_msg(const char *err_str); +#endif + + // dump_ostream + virtual bool dump_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void dump_flush(void); + void dump_close(void); + virtual void dump_msg(const char * str); + void Set_dump_ostream(std::ostream * out) {this->dump_ostream = out;}; + std::ostream *Get_dump_ostream(void) {return this->dump_ostream;}; + void Set_dump_on(bool tf) {this->dump_on = tf;}; + bool Get_dump_on(void)const {return this->dump_on;}; + + // fpunchf + virtual void fpunchf(const char *name, const char *format, double d); + virtual void fpunchf(const char *name, const char *format, char * d); + virtual void fpunchf(const char *name, const char *format, int d); + virtual void fpunchf_end_row(const char *format); + static void fpunchf_helper(std::ostream *os, const char *format, ...); + static void fpunchf_helper(std::string *str, const char *format, ...); + + virtual void screen_msg(const char * str); + void Set_screen_on(bool tf) {this->screen_on = tf;}; + bool Get_screen_on(void)const {return this->screen_on;}; + + // input methods + virtual int getc(void); + virtual LINE_TYPE get_line(void); + virtual LINE_TYPE get_logical_line(void); + bool check_key(std::string::iterator begin, std::string::iterator end); + std::string & Get_m_line() {return m_line;} + std::string & Get_m_line_save() {return m_line_save;} + std::string & Get_accumulated() {return accumulated;} + LINE_TYPE Get_m_line_type()const {return m_line_type;}; + void Set_accumulate(bool tf) + { + if (tf) + { + accumulated.clear(); + } + this->accumulate = tf; + } + Keywords::KEYWORDS Get_m_next_keyword() const {return m_next_keyword;} + + // echo + enum ECHO_OPTION + { + ECHO_LOG, + ECHO_OUTPUT + }; + virtual void echo_msg(const char * str); + void Set_echo_on(bool tf) {this->echo_on = tf;}; + bool Get_echo_on(void)const {return this->echo_on;}; + void Set_echo_destination(ECHO_OPTION eo) {this->echo_destination = eo;}; + ECHO_OPTION Get_echo_destination(void)const {return this->echo_destination;}; + + // data +protected: + std::ostream *output_ostream; + std::ostream *log_ostream; + std::ostream *punch_ostream; +#ifdef ERROR_OSTREAM + std::ostream *error_ostream; +#else + FILE * error_file; +#endif + std::ostream *dump_ostream; + int io_error_count; + + bool output_on; + bool log_on; + bool punch_on; + bool error_on; + bool dump_on; + bool echo_on; + bool screen_on; + ECHO_OPTION echo_destination; + +#if defined(_MSC_VER) +/* disable warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' */ +#pragma warning(disable:4251) +#endif + + std::list istream_list; + std::list delete_istream_list; + + std::string m_line; + std::string m_line_save; + std::string accumulated; + +#if defined(_MSC_VER) +/* reset warning C4251 */ +#pragma warning(default:4251) +#endif + + // input data members + Keywords::KEYWORDS m_next_keyword; + bool accumulate; + LINE_TYPE m_line_type; +}; + +#endif /* _PHRQIO_H */ diff --git a/phreeqcpp/common/Parser.cxx b/phreeqcpp/common/Parser.cxx new file mode 100644 index 00000000..db7d3556 --- /dev/null +++ b/phreeqcpp/common/Parser.cxx @@ -0,0 +1,1331 @@ +// Parser.cpp: implementation of the CParser class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif + +#include // std::transform +#include // std::map +#include // assert +#include +#include // std::cout std::cerr +#include "Utils.h" +#include +#include "Parser.h" +#include "PHRQ_io.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CParser::CParser(PHRQ_io *io): +PHRQ_base(io), +m_input_stream(std::cin), +m_input_error(0), +m_next_keyword(Keywords::KEY_NONE) +{ + if (!io) + { + error_msg("This parser constructor requires non-null phrq_io", PHRQ_io::OT_STOP); + echo_file = EO_ALL; + echo_stream = EO_NONE; + accumulate = false; + phrq_io_only = true; + m_line_type = PHRQ_io::LT_EMPTY; + } + else + { + m_line_save = io->Get_m_line(); + m_line = io->Get_m_line(); + m_line_type = io->Get_m_line_type(); + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + echo_file = EO_ALL; + echo_stream = EO_NONE; + accumulate = false; + phrq_io_only = true; + } +} + +CParser::CParser(std::istream & input, PHRQ_io *io): +PHRQ_base(io), +m_input_stream(input), +m_input_error(0), +m_next_keyword(Keywords::KEY_NONE) +{ + m_line_save.reserve(80); + m_line.reserve(80); + echo_file = EO_ALL; + echo_stream = EO_NONE; + accumulate = false; + phrq_io_only = false; + m_line_type = PHRQ_io::LT_EMPTY; +} + +CParser::~CParser() +{ +} + +PHRQ_io::LINE_TYPE CParser::check_line(const std::string & str, + bool allow_empty, bool allow_eof, + bool allow_keyword, bool print) +{ + PHRQ_io::LINE_TYPE + i; + + // Get line + do + { + i = get_line(); + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + // output for stream + switch (this->echo_stream) + { + case EO_NONE: + break; + case EO_ALL: + if (i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + case EO_KEYWORDS: + if (i == PHRQ_io::LT_KEYWORD) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + case EO_NOKEYWORDS: + if (i != PHRQ_io::LT_KEYWORD && i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + } + // output for file + switch (this->echo_file) + { + case EO_NONE: + break; + case EO_ALL: + if (i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str()); + } + break; + case EO_KEYWORDS: + if (i == PHRQ_io::LT_KEYWORD) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str()); + } + break; + + case EO_NOKEYWORDS: + if (i != PHRQ_io::LT_KEYWORD && i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str().c_str()); + } + break; + } + } + while (i == PHRQ_io::LT_EMPTY && allow_empty == false); + + // Check eof + if (i == PHRQ_io::LT_EOF && allow_eof == false) + { + std::ostringstream msg; + msg << "Unexpected eof while reading " << str << + "\nExecution terminated.\n"; + error_msg(msg.str().c_str(), PHRQ_io::OT_STOP); + } + + // Check keyword + if (i == PHRQ_io::LT_KEYWORD && allow_keyword == false) + { + std::ostringstream msg; + msg << "Expected data for " << str << + ", but got a keyword ending data block."; + error_msg(msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + } + m_line_type = i; + return i; +} +PHRQ_io::LINE_TYPE CParser::get_line_phrq_io() +{ + m_line_type = io->get_line(); + m_line_save = io->Get_m_line_save(); + m_line = io->Get_m_line(); + m_next_keyword = io->Get_m_next_keyword(); + if (accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + return m_line_type; +} + +PHRQ_io::LINE_TYPE CParser::get_line() +{ + if (this->phrq_io_only) + { + return get_line_phrq_io(); + } + PHRQ_io::LINE_TYPE return_value = PHRQ_io::LT_EMPTY; + while (return_value == PHRQ_io::LT_EMPTY) + { + // + // Eliminate all characters after # sign as a comment + // + + // + // Get line, check for eof + // + if (get_logical_line() == PHRQ_io::LT_EOF) + { + if (!m_input_stream.eof()) + { + error_msg("Reading input file.", PHRQ_io::OT_CONTINUE); + error_msg("istream::get() returned an error.", PHRQ_io::OT_STOP); + } + else + { + //{{MOD + m_line.erase(m_line.begin(), m_line.end()); // m_line.clear(); + //}}MOD + m_next_keyword = Keywords::KEY_END; + return PHRQ_io::LT_EOF; + } + } + + // + // Get long lines + // + bool + empty = true; + m_line = m_line_save.substr(0, m_line_save.find_first_of('#')); + for (unsigned int i = 0; i < m_line.size(); ++i) + { + if (!::isspace(m_line[i])) + { + empty = false; + break; + } + } + + if (this->accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + // + // New line character encountered + // + return_value = (empty ? PHRQ_io::LT_EMPTY : PHRQ_io::LT_OK); + } + + // + // Determine return_value + // + if (return_value == PHRQ_io::LT_OK) + { + if (check_key(m_line.begin(), m_line.end())) + { + return_value = PHRQ_io::LT_KEYWORD; + } + else + { + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + std::string token; + copy_token(token, beg, end); + + if (token.size() > 1 && token[0] == '-' &&::isalpha(token[1])) + { + return_value = PHRQ_io::LT_OPTION; + } + } + } + return return_value; +} + +/** + Reads input stream until end of line, ";", or eof + stores characters in line_save + + returns: + EOF on empty line on end of file or + OK otherwise +*/ +PHRQ_io::LINE_TYPE CParser::get_logical_line() +{ + int + j; + unsigned int + pos; + char + c; + + m_line_save.erase(m_line_save.begin(), m_line_save.end()); // m_line_save.clear(); + + while ((j = m_input_stream.get()) != std::char_traits < char >::eof()) + { + c = (char) j; + if (c == '#') + { + // ignore all chars after # until newline + do + { + c = (char) j; + if (c == '\n') + { + break; + } + m_line_save += c; + } + while ((j = + m_input_stream.get()) != std::char_traits < + char >::eof()); + } + if (c == ';') + break; + if (c == '\n') + { + break; + } + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + while ((j = + m_input_stream.get()) != std::char_traits < char >::eof()) + { + c = (char) j; + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + continue; + } + if (c == '\n') + { + // remove '\\' + for (; pos < m_line_save.size(); pos++) + { + m_line_save[pos] = m_line_save[(size_t) pos + 1]; + } + m_line_save.erase(m_line_save.size() - 1, 1); + break; + } + m_line_save += c; + if (!::isspace(j)) + break; + } + } + else + { + m_line_save += c; + } + } + if (j == std::char_traits < char >::eof() && m_line_save.size() == 0) + { + return (PHRQ_io::LT_EOF); + } + return (PHRQ_io::LT_OK); +} + +bool +CParser::check_key(std::string::iterator begin, std::string::iterator end) +{ + std::string lowercase; + copy_token(lowercase, begin, end); + std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), + tolower); + m_next_keyword = Keywords::Keyword_search(lowercase); + if (m_next_keyword == Keywords::KEY_NONE) + { + return false; + } + return true; +} +CParser::STATUS_TYPE CParser::check_units(std::string & tot_units, + bool alkalinity, + bool check_compatibility, + const std::string & default_units, + bool print) +{ +/* + * Check if legitimate units + * Input: + * tot_units character string to check, + * alkalinity true if alkalinity, false if any other total, + * check_compatibility true check alk and default units, false otherwise + * default_units character string of default units (check /L, /kg, etc) + * print true print warning messages + * Output: + * tot_units standard form for unit + */ + using + Utilities::str_tolower; + using + Utilities::replace; + using + Utilities::squeeze_white; + + static const char * + units[] = { + "Mol/l", /* 0 */ + "mMol/l", /* 1 */ + "uMol/l", /* 2 */ + "g/l", /* 3 */ + "mg/l", /* 4 */ + "ug/l", /* 5 */ + "Mol/kgs", /* 6 */ + "mMol/kgs", /* 7 */ + "uMol/kgs", /* 8 */ + "g/kgs", /* 9 = ppt */ + "mg/kgs", /* 10 = ppm */ + "ug/kgs", /* 11 = ppb */ + "Mol/kgw", /* 12 = mol/kg H2O */ + "mMol/kgw", /* 13 = mmol/kg H2O */ + "uMol/kgw", /* 14 = umol/kg H2O */ + "g/kgw", /* 15 = mol/kg H2O */ + "mg/kgw", /* 16 = mmol/kg H2O */ + "ug/kgw", /* 17 = umol/kg H2O */ + "eq/l", /* 18 */ + "meq/l", /* 19 */ + "ueq/l", /* 20 */ + "eq/kgs", /* 21 */ + "meq/kgs", /* 22 */ + "ueq/kgs", /* 23 */ + "eq/kgw", /* 24 */ + "meq/kgw", /* 25 */ + "ueq/kgw", /* 26 */ + }; + + squeeze_white(tot_units); + str_tolower(tot_units); + replace("milli", "m", tot_units); + replace("micro", "u", tot_units); + replace("grams", "g", tot_units); + replace("gram", "g", tot_units); + replace("moles", "Mol", tot_units); + replace("mole", "Mol", tot_units); + replace("mol", "Mol", tot_units); + replace("liter", "l", tot_units); + replace("kgh", "kgw", tot_units); + replace("ppt", "g/kgs", tot_units); + replace("ppm", "mg/kgs", tot_units); + replace("ppb", "ug/kgs", tot_units); + replace("equivalents", "eq", tot_units); + replace("equivalent", "eq", tot_units); + replace("equiv", "eq", tot_units); + + std::string::size_type end; + if ((end = tot_units.find("/l")) != std::string::npos) + { + tot_units.resize(end + 2); + } + if ((end = tot_units.find("/kgs")) != std::string::npos) + { + tot_units.resize(end + 4); + } + if ((end = tot_units.find("/kgw")) != std::string::npos) + { + tot_units.resize(end + 4); + } + + // + // Check if unit in list + // + bool + found = false; + for (unsigned int i = 0; i < sizeof(units) / sizeof(char *); ++i) + { + if (tot_units.compare(units[i]) == 0) + { + found = true; + break; + } + } + if (!found) + { + if (print) + { + std::ostringstream err; + err << "Unknown unit, " << tot_units; + this->error_msg(err.str().c_str(), PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; + } + + // + // Check if units are compatible with default_units + // + if (check_compatibility == false) + return PARSER_OK; + + // + // Special cases for alkalinity + // + if (alkalinity == true && tot_units.find("Mol") != std::string::npos) + { + if (print) + { + this->warning_msg + ("Alkalinity given in moles, assumed to be equivalents."); + } + replace("Mol", "eq", tot_units); + } + if (alkalinity == false && tot_units.find("eq") != std::string::npos) + { + if (print) + { + this->error_msg("Only alkalinity can be entered in equivalents.", + PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; + } + + // + // See if default_units are compatible with tot_units + // + if (default_units.find("/l") != std::string::npos + && tot_units.find("/l") != std::string::npos) + return PARSER_OK; + if (default_units.find("/kgs") != std::string::npos + && tot_units.find("/kgs") != std::string::npos) + return PARSER_OK; + if (default_units.find("/kgw") != std::string::npos + && tot_units.find("/kgw") != std::string::npos) + return PARSER_OK; + + std::string str = default_units; + replace("kgs", "kg solution", str); + replace("kgs", "kg solution", tot_units); + replace("kgw", "kg water", str); + replace("kgw", "kg water", tot_units); + replace("/l", "/L", str); + replace("Mol", "mol", str); + replace("/l", "/L", tot_units); + replace("Mol", "mol", tot_units); + + if (print) + { + std::ostringstream err; + err << "Units for master species, " << tot_units << + ", are not compatible with default units, " << str << "."; + this->error_msg(err.str().c_str(), PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; +} +CParser::TOKEN_TYPE CParser::token_type(const std::string & token) +{ + if (!token.empty()) + { + if (::isupper(token[0])) + { + return CParser::TT_UPPER; + } + else if (::islower(token[0])) + { + return CParser::TT_LOWER; + } + else if (::isdigit(token[0]) || token[0] == '.' || token[0] == '-') + { + return CParser::TT_DIGIT; + } + else + { + assert(!::isspace(token[0])); + return CParser::TT_UNKNOWN; + } + } + else + { + return CParser::TT_EMPTY; + } +} + +CParser::TOKEN_TYPE CParser::peek_token() +{ + std::istringstream::pos_type pos = m_line_iss.tellg(); + std::string token; + m_line_iss >> token; + m_line_iss.seekg(pos); + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::string::iterator & begin, + std::string::iterator & end) +{ + if (begin != end) + { + std::string::iterator b = begin; + for (; b < end &&::isspace(*b); ++b); + + begin = b; + for (; begin < end && !::isspace(*begin); ++begin); + + token.assign(b, begin); + } + else + { + token.resize(0); + } + + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::istream & is) +{ + is >> token; + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::istream::pos_type & pos) +{ + m_line_iss.seekg(pos); + if (!(m_line_iss >> token)) + { + token.erase(token.begin(), token.end()); // token.clear(); + } + pos = m_line_iss.tellg(); + return token_type(token); +} + +CParser::FIND_TYPE CParser::find_option(const std::string & item, int *n, + const std::vector < std::string > + &list, bool exact) +{ + std::string token(item); + std::transform(token.begin(), token.end(), token.begin(), tolower); + for (unsigned int i = 0; i < list.size(); i++) + { + if (exact == true) + { + if (list[i].compare(token) == 0) + { + *n = i; + return FT_OK; + } + } + else + { + if (list[i].find(token) == 0) + { + *n = i; + return FT_OK; + } + } + } + + *n = -1; + return FT_ERROR; +} + +int +CParser::get_option(const std::vector < std::string > &opt_list, + std::string::iterator & next_char) +{ + // + // Read a line and check for options + // + int j; + int /* opt_l, */ opt; + std::string::iterator opt_ptr; + std::string option; + +#if !defined(R_SO) + fprintf(stderr, "Did not think this get_option was called\n"); +#endif + // + // Read line + // + PHRQ_io::LINE_TYPE lt = check_line("get_option", false, true, true, true); + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, false) == CParser::FT_OK) + { + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + next_char = opt_ptr; + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + //get_output() << "\t" << m_line_save << "\n"; + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (true) // (database_file == NULL) + { + //get_output() << "\t" << m_line_save << "\n"; + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + + std::ostringstream err; + err << "Unknown option." << "\n"; + err << m_line_save << "\n"; + error_msg(err.str().c_str()); + + j = OPT_ERROR; + next_char = m_line.begin(); + } + } + else + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_char = opt_ptr; + } + else + { + j = OPT_DEFAULT; + next_char = m_line.begin(); + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { +#if !defined(R_SO) + std::cout << "\t" << m_line_save << "\n"; +#endif + } + } + } + return (j); +} + +int +CParser::get_option(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos) +{ + // + // Read a line and check for options + // + int j; + int opt; + std::istream::pos_type pos_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = check_line("get_option", false, true, true, true); + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + std::string::iterator opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option.substr(1), &opt, opt_list, false) == FT_OK) + { + // replace -option with option + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + pos_ptr = 0; + copy_token(option, pos_ptr); + next_pos = pos_ptr; + } + else + { + j = OPT_ERROR; + next_pos = pos_ptr; + } + } + else + { + pos_ptr = m_line_iss.tellg(); + m_line_iss >> option; + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_pos = m_line_iss.tellg(); + } + else + { + j = OPT_DEFAULT; + m_line_iss.seekg(pos_ptr); + m_line_iss.clear(); + next_pos = pos_ptr; + } + } + return (j); +} +CParser::STATUS_TYPE CParser::get_elt(std::string::iterator & begin, + const std::string::iterator end, + std::string & element) +{ + element.erase(element.begin(), element.end()); // element.clear(); + + if (begin == end) + { + error_msg("Empty string in get_elt. Expected an element name.", + PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + + // + // Load name into char array element + // + char + c = *begin; + ++begin; + element.insert(element.end(), c); // element.push_back(c); + if (c == '[') + { + while ((c = *begin) != ']') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if ((c = *begin) == ']') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + break; + } + else if (begin == end) + { + error_msg("No ending bracket (]) for element name", + PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + } + while (::islower(c = *begin) || c == '_') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if (begin == end) + break; + } + } + else + { + while (::islower(c = *begin) || c == '_') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if (begin == end) + break; + } + } + return PARSER_OK; +} + +CParser::STATUS_TYPE CParser::parse_couple(std::string & token) +{ + // Parse couple puts redox couples in standard form + // "+" is removed and couples are rewritten in sort + // order. + + if (Utilities::strcmp_nocase_arg1(token.c_str(), "pe") == 0) + { + Utilities::str_tolower(token); + return PARSER_OK; + } + + while (Utilities::replace("+", "", token)); + + std::string::iterator ptr = token.begin(); + std::string elt1; + get_elt(ptr, token.end(), elt1); + + if (*ptr != '(') + { + std::ostringstream err_msg; + err_msg << "Element name must be followed by " << + "parentheses in redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + + int + paren_count = 1; + std::string paren1 = "("; + while (ptr != token.end()) + { + ++ptr; + if (*ptr == '/' || ptr == token.end()) + { + std::ostringstream err_msg; + err_msg << "End of line or " "/" + " encountered before end of parentheses, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + paren1.insert(paren1.end(), *ptr); // element.push_back(c); + if (*ptr == '(') + ++paren_count; + if (*ptr == ')') + --paren_count; + if (paren_count == 0) + break; + } + + ++ptr; + if (ptr == token.end() || *ptr != '/') + { + std::ostringstream err_msg; + err_msg << " " "/" " must follow parentheses " << + "ending first half of redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + ++ptr; + std::string elt2; + get_elt(ptr, token.end(), elt2); + if (elt1.compare(elt2) != 0) + { + std::ostringstream err_msg; + err_msg << "Redox couple must be two redox states " << + "of the same element, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + if (*ptr != '(') + { + std::ostringstream err_msg; + err_msg << "Element name must be followed by " + "parentheses in redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + std::string paren2 = "("; + paren_count = 1; + + while (ptr != token.end()) + { + ++ptr; + if (*ptr == '/' || ptr == token.end()) + { + std::ostringstream err_msg; + err_msg << "End of line or " "/" + " encountered before end of parentheses, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + paren2.insert(paren2.end(), *ptr); // element.push_back(c); + if (*ptr == '(') + ++paren_count; + if (*ptr == ')') + --paren_count; + if (paren_count == 0) + break; + } + if (paren1.compare(paren2) < 0) + { + token = elt1 + paren1 + std::string("/") + elt2 + paren2; + } + else if (paren1.compare(paren2) > 0) + { + token = elt2 + paren2 + std::string("/") + elt1 + paren1; + } + else + { + std::ostringstream err_msg; + err_msg << "Both parts of redox couple are the same, " << + token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + return PARSER_OK; +} + +template +CParser::STATUS_TYPE CParser::addPair(std::map < std::string, T >&totals, + std::istream::pos_type & pos) +{ + std::string token; + T d; + CParser::TOKEN_TYPE j; + + m_line_iss.seekg(pos); + + j = copy_token(token, pos); + + if (j == CParser::TT_EMPTY) + return PARSER_OK; + + if (!(m_line_iss >> d)) + { + return PARSER_ERROR; + } + totals[token] = d; + return PARSER_OK; +} + +int +CParser::getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::string::iterator & next_char, bool flag_error) +{ + // + // Read a line and check for options + // + int j; + int /* opt_l, */ opt; + std::string::iterator opt_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = m_line_type; + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, false) == CParser::FT_OK) + { + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + next_char = opt_ptr; + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (flag_error) + { + if (true) // (database_file == NULL) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + std::ostringstream err; + err << "Unknown option." << "\n"; + err << m_line_save << "\n"; + error_msg(err.str().c_str()); + } + j = OPT_ERROR; + next_char = m_line.begin(); + } + } + else + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_char = opt_ptr; + } + else + { + j = OPT_DEFAULT; + next_char = m_line.begin(); + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { +#if !defined(R_SO) + std::cout << "\t" << m_line_save << "\n"; +#endif + } + } + } + return (j); +} + +int +CParser::getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos, bool flag_error) +{ + // + // Read a line and check for options + // + int j; + int opt; + std::istream::pos_type pos_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = m_line_type; + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + std::string::iterator opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option.substr(1), &opt, opt_list, false) == FT_OK) + { + // replace -option with option + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + pos_ptr = 0; + copy_token(option, pos_ptr); + next_pos = pos_ptr; + //if (this->echo_file) // pr.echo_input == TRUE + if (false) + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (flag_error) // don`t throw error + { + if (true) // (database_file == NULL) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + error_msg("Unknown option.", PHRQ_io::OT_CONTINUE); + error_msg(m_line_save.c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + } + j = OPT_ERROR; + next_pos = pos_ptr; + } + } + else + { + pos_ptr = 0; + copy_token(option, pos_ptr); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_pos = pos_ptr; + } + else + { + j = OPT_DEFAULT; + next_pos = 0; + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + return (j); +} +int CParser:: +incr_input_error() +{ + return ++m_input_error; +} + +CParser::TOKEN_TYPE CParser::copy_title(std::string & token, + std::string::iterator & begin, + std::string::iterator & end) +{ + if (begin != end) + { + std::string::iterator b = begin; + std::string::iterator e = end; + for (; b < end && (::isspace(*b) || (*b == ',')); ++b); + begin = b; + if (*begin == '"') + { + begin = ++b; + for (; begin != end && !(*begin == '"'); ++begin); + e = begin; + if (begin != end && *begin == '"') + { + e = begin++; + } + } + else if (*begin == '\'') + { + begin = ++b; + for (; begin != end && !(*begin == '\''); ++begin); + e = begin; + if (begin != end && *begin == '\'') + { + e = begin++; + } + } + else + { + for (; begin < end && !(*begin == ',') && !(::isspace(*begin)); ++begin); + e = begin; + } + token.assign(b, e); + } + else + { + token.resize(0); + } + token = trim(token); + return token_type(token); +} +bool CParser::get_true_false(std::istream::pos_type & pos, bool def) +{ + std::string token; + this->copy_token(token, pos); + std::string::iterator b = token.begin(); + for (; b != token.end() && (::isspace(*b)); ++b); + if (b != token.end()) + { + if (*b == 'f' || *b == 'F') + { + return false; + } + else if (*b == 't' || *b == 'T') + { + return true; + } + } + return def; +} +CParser::TOKEN_TYPE CParser::get_rest_of_line(std::string &token) +{ + token.clear(); + int j; + while ((j = m_line_iss.get()) != std::char_traits < char >::eof()) + { + char c = (char) j; + token += c; + } + token = trim(token); + return token_type(token); +} +CParser::TOKEN_TYPE CParser::parse_delimited(std::string & source, std::string & result, + const std::string& t = " \t") +{ + + size_t pos = source.find_first_of(t); + std::string temp; + if (pos != std::string::npos) + { + result = source.substr(0, pos); + temp = source.substr(pos+1); + source = temp; + } + else + { + result = source; + source.clear(); + } + std::string str = result; + + return token_type(trim_left(str)); +} diff --git a/phreeqcpp/common/Parser.h b/phreeqcpp/common/Parser.h new file mode 100644 index 00000000..ac6620bc --- /dev/null +++ b/phreeqcpp/common/Parser.h @@ -0,0 +1,310 @@ +#if !defined(PARSER_H_INCLUDED) +#define PARSER_H_INCLUDED +#if defined(WIN32) +# if defined(PHREEQCI_GUI) +# ifndef WINVER +# define WINVER 0x0400 +# endif +# include +# endif +# include +#endif +#include // std::string +#include // std::map +#include // std::vector +#include // std::istringstream std::ostringstream +#include // std::ostream +#include // std::istream +#include // std::isspace +#include // std::find_if +#include // std::ptr_fun std::not1 + +#include "PHRQ_base.h" +#include "Keywords.h" +#include "PHRQ_io.h" + +#ifdef _DEBUG +#define isspace(a) isspace((a) < -1 ? (a) + 256 : (a)) +#define isupper(a) isupper((a) < -1 ? (a) + 256 : (a)) +#define islower(a) islower((a) < -1 ? (a) + 256 : (a)) +#define isdigit(a) isdigit((a) < -1 ? (a) + 256 : (a)) +#define isalpha(a) isalpha((a) < -1 ? (a) + 256 : (a)) +#endif + +class CParser: public PHRQ_base +{ + public: + CParser(PHRQ_io *io=NULL); + CParser(std::istream & input, PHRQ_io *io=NULL); + //CParser(std::istream & input, std::ostream & output, PHRQ_io *io=NULL); + //CParser(std::istream & input, std::ostream & output, + // std::ostream & error, PHRQ_io *io=NULL); + + virtual ~ CParser(); + enum TOKEN_TYPE + { + TT_EMPTY = 2, + TT_UPPER = 4, + TT_LOWER = 5, + TT_DIGIT = 6, + TT_UNKNOWN = 7 + }; + + enum FIND_TYPE + { + FT_OK = 0, + FT_ERROR = 1 + }; + + enum OPT_TYPE + { + OPT_DEFAULT = -4, + OPT_ERROR = -3, + OPT_KEYWORD = -2, + OPT_EOF = -1 + }; + enum ECHO_OPTION + { + EO_NONE = 0, + EO_ALL = 1, + EO_KEYWORDS = 2, + EO_NOKEYWORDS = 3 + }; + + enum STATUS_TYPE + { + PARSER_ERROR = 0, + PARSER_OK = 1 + }; + + + /** + Function gets a new line and checks for empty, eof, and keywords. + + Arguments: + string Input, character string used in printing error message + allow_empty Input, True or false, if a blank line is accepable + if false, another line is read + allow_eof Input, True or false, if EOF is acceptable + allow_keyword Input, True or false, if a keyword is acceptable + + Returns: + LT_EMPTY if empty line read and allow_empty == true + LT_KEYWORD if line begins with keyword + LT_EOF if eof and allow_eof == true + LT_OK otherwise + LT_OPTION if line begins with -[alpha] + + Terminates if EOF and allow_eof == false. + */ + PHRQ_io::LINE_TYPE check_line(const std::string & str, bool allow_empty, + bool allow_eof, bool allow_keyword, bool print); + + /** + Read a line from input file put in "line". + Copy of input line is stored in "line_save". + Characters after # are discarded in line but retained in "line_save" + + Arguments: + None + Returns: + LT_EMPTY, + LT_EOF, + LT_KEYWORD, + LT_OK, + LT_OPTION + */ + PHRQ_io::LINE_TYPE get_line(); + PHRQ_io::LINE_TYPE get_line_phrq_io(); + + // bool check_key(const std::string::iterator ptr); + bool check_key(std::string::iterator begin, std::string::iterator end); + + STATUS_TYPE check_units(std::string & tot_units, bool alkalinity, + bool check_compatibility, + const std::string & default_units, bool print); + + + //KEY_TYPE next_keyword() const + Keywords::KEYWORDS next_keyword() const + { + return m_next_keyword; + } + int get_option(const std::vector < std::string > &opt_list, + std::string::iterator & next_char); + int get_option(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos); + int getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::string::iterator & next_char, bool flag_error); + int getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos, bool flag_error); + + + std::string & line() + { + return m_line; + } + std::string & line_save() + { + return m_line_save; + } + std::string & get_accumulated() + { + return accumulated; + } + void set_accumulate(bool tf) + { + if (tf) + { + accumulated.clear(); + } + this->accumulate = tf; + } + std::istringstream & get_iss() + { + return m_line_iss; + } + int incr_input_error(); + int get_input_error() + { + return m_input_error; + } + + /** + Copies from begin to token until first space is encountered. + + Arguments: + token output, the token + begin input, begin iterator + end input, end iterator + + Returns: + TT_EMPTY + TT_UPPER + TT_LOWER + TT_DIGIT + TT_UNKNOWN + */ + static CParser::TOKEN_TYPE copy_token(std::string & token, + std::string::iterator & begin, + std::string::iterator & end); + static CParser::TOKEN_TYPE copy_title(std::string & token, + std::string::iterator & begin, + std::string::iterator & end); + static CParser::TOKEN_TYPE token_type(const std::string & token); + static CParser::TOKEN_TYPE copy_token(std::string & token, std::istream & is); + CParser::TOKEN_TYPE copy_token(std::string & token, std::istream::pos_type & pos); + bool get_true_false(std::istream::pos_type & pos, bool def); + CParser::TOKEN_TYPE get_rest_of_line(std::string &token); + static CParser::TOKEN_TYPE parse_delimited(std::string & source, std::string & result, const std::string& t); + CParser::TOKEN_TYPE peek_token(); + PHRQ_io::LINE_TYPE get_m_line_type(void) const {return this->m_line_type;} + + /** + Function reads an element name out of the equation string. + An element name is composed of a capital letter followed by any number + of lower case characters. + + Arguments: + begin input, points to position in the equation to begin + output, points to next character of equation after + element name. + end input, points to last position in the equation + element input pointer to place to return element character string + */ + STATUS_TYPE get_elt(std::string::iterator & begin, + const std::string::iterator end, + std::string & element); + + + /** + Compares a string value to match beginning letters of a list of options + + Arguments: + item entry: pointer to string to compare + n exit: item in list that was matched + list entry: pointer to list of character values, assumed to + be lower case + count_list entry: number of character values in list + + Returns: + OK item matched + ERROR item not matched + n -1 item not matched + i position of match in list + */ + static FIND_TYPE find_option(const std::string & item, int *n, + const std::vector < std::string > &list, + bool exact); + + void set_echo_file(ECHO_OPTION opt) + { + echo_file = opt; + } + ECHO_OPTION get_echo_file() + { + return this->echo_file; + }; + + void set_echo_stream(ECHO_OPTION opt) + { + echo_stream = opt; + } + ECHO_OPTION get_echo_stream() + { + return this->echo_stream; + }; + + STATUS_TYPE parse_couple(std::string & token); + + template + STATUS_TYPE addPair(std::map < std::string, T >&totals, + std::istream::pos_type & pos); + + protected: + PHRQ_io::LINE_TYPE get_logical_line(); + + protected: + std::istream & m_input_stream; + //std::ostream & m_output_stream; + //std::ostream & m_error_stream; + int m_input_error; + //KEY_TYPE m_next_keyword; + Keywords::KEYWORDS m_next_keyword; + std::string m_line; + std::string m_line_save; + std::istringstream m_line_iss; + PHRQ_io::LINE_TYPE m_line_type; + ECHO_OPTION echo_stream; + ECHO_OPTION echo_file; + std::string accumulated; + bool accumulate; + bool phrq_io_only; + +}; + +// Global functions +static inline std::string &trim_left(std::string &s) +{ +#if (__GNUC__ && (__cplusplus >= 201103L)) || (_MSC_VER >= 1600) + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) {return !std::isspace(c);})); +#else + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); +#endif + return s; +} +static inline std::string &trim_right(std::string &s) +{ +#if (__GNUC__ && (__cplusplus >= 201103L)) || (_MSC_VER >= 1600) + s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) {return !std::isspace(c);}).base(), s.end()); +#else + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); +#endif + return s; +} +static inline std::string &trim(std::string &s) +{ + return trim_left(trim_right(s)); +} + +#endif // PARSER_H_INCLUDED diff --git a/phreeqcpp/common/Utils.cxx b/phreeqcpp/common/Utils.cxx new file mode 100644 index 00000000..ad96a147 --- /dev/null +++ b/phreeqcpp/common/Utils.cxx @@ -0,0 +1,194 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // ::tolower +#include // ::tolower +#include // std::transform +#include // std::cout std::cerr +#include + +#include "Utils.h" +#include "Parser.h" +#include "float.h" +#include "math.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +//////////////////////////////////////////////////////////////////////////// +int +Utilities::strcmp_nocase_arg1(const char *str1, const char *str2) +//////////////////////////////////////////////////////////////////////////// +{ + // + // Compare two strings disregarding case + // + int c1, c2; + while ((c1 =::tolower(*str1++)) == (c2 = *str2++)) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +//////////////////////////////////////////////////////////////////////////// +int +Utilities::strcmp_nocase(const char *str1, const char *str2) +//////////////////////////////////////////////////////////////////////////// +{ + // + // Compare two strings disregarding case + // + int c1, c2; + while ((c1 =::tolower(*str1++)) == (c2 =::tolower(*str2++))) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::str_tolower(std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::transform(str.begin(), str.end(), str.begin(), tolower); +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::str_toupper(std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::transform(str.begin(), str.end(), str.begin(), toupper); +} +//////////////////////////////////////////////////////////////////////////// +std::string +Utilities::pad_right(const std::string & str, size_t l) +//////////////////////////////////////////////////////////////////////////// +{ + std::string new_str(str); + size_t length = new_str.size(); + if (length < l) + { + new_str = new_str.insert(length, l - length, ' '); + } + return new_str; +} + + +//////////////////////////////////////////////////////////////////////////// +bool +Utilities::replace(const char *str1, const char *str2, std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::string::size_type n = str.find(str1, 0); + if (n == std::string::npos) + return false; + + str.replace(n, ::strlen(str1), str2); + return true; +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::squeeze_white(std::string & s_l) +//////////////////////////////////////////////////////////////////////////// +{ + std::string str; + std::string::iterator beg = s_l.begin(); + std::string::iterator end = s_l.end(); + //CParser::copy_token(str, beg, end); + std::string::iterator pos; + for (pos = beg; pos != end; pos++) + { + int c = *pos; + if (!::isspace(c)) + { + str += c; + } + } + s_l = str; +} +//////////////////////////////////////////////////////////////////////////// +double +Utilities::convert_time(double t, std::string in, std::string out) +//////////////////////////////////////////////////////////////////////////// +{ + Utilities::str_tolower(in); + + // convert t to seconds + if (in.substr(0,1) == "m") + { + t = t * 60.; + } + if (in.substr(0,1) == "h") + { + t = t * 3600.; + } + if (in.substr(0,1) == "d") + { + t = t * 3600. * 24.; + } + if (in.substr(0,1) == "y") + { + t = t * 3600. * 24. * 365.25; + } + // convert to ouput units + if (out.substr(0,1) == "m") + { + t = t / 60.; + } + if (out.substr(0,1) == "h") + { + t = t / 3600.; + } + if (out.substr(0,1) == "d") + { + t = t / ( 3600. * 24.); + } + if (out.substr(0,1) == "y") + { + t = t / (3600. * 24. * 365.25); + } + return t; + +} +LDBLE +Utilities::safe_exp(LDBLE t) +//////////////////////////////////////////////////////////////////////////// +{ + LDBLE f = 1.442695*t; // convert to exp for 2.0 + + if (f > DBL_MAX_EXP - 50.0) + { + return pow(2, DBL_MAX_EXP - 50.0); + } + else if (f < DBL_MIN_EXP + 50.0) + { + return pow(2, DBL_MIN_EXP + 50.0); + } + return exp(t); +} +//+NAN LDBLE: 7ff8000000000000 +//-NAN LDBLE: fff8000000000000 +/* +LDBLE Utilities::get_nan(void) +{ + unsigned long long raw = 0x7ff0000000000000; + LDBLE d = *( LDBLE* )&raw; + return(d); + +} +*/ diff --git a/phreeqcpp/common/Utils.h b/phreeqcpp/common/Utils.h new file mode 100644 index 00000000..74997731 --- /dev/null +++ b/phreeqcpp/common/Utils.h @@ -0,0 +1,28 @@ +#if !defined(UTILITIES_H_INCLUDED) +#define UTILITIES_H_INCLUDED + +#include +#include // std::istringstream std::ostringstream +#include // std::ostream +#include // std::istream +#include // std::map +#include "phrqtype.h" +namespace Utilities +{ + + const char INDENT[] = " "; + + int strcmp_nocase(const char *str1, const char *str2); + + int strcmp_nocase_arg1(const char *str1, const char *str2); + + void str_tolower(std::string & str); + void str_toupper(std::string & str); + std::string pad_right(const std::string & str, size_t l); + bool replace(const char *str1, const char *str2, std::string & str); + + void squeeze_white(std::string & s_l); + double convert_time(double t, std::string in, std::string out); + LDBLE safe_exp(LDBLE t); +} +#endif // UTILITIES_H_INCLUDED diff --git a/phreeqcpp/common/phrqtype.h b/phreeqcpp/common/phrqtype.h new file mode 100644 index 00000000..ab7160b5 --- /dev/null +++ b/phreeqcpp/common/phrqtype.h @@ -0,0 +1,18 @@ +#ifndef _INC_PHRQTYPE_H +#define _INC_PHRQTYPE_H +/* + * The following implements long double + * Many machines long double = double so there is no advantage + * Check float.h include file for number of digits (LDBL_DIG) + * Need to define here and in cl1.c + */ + +/*#define USE_LONG_DOUBLE*/ +#ifdef USE_LONG_DOUBLE +#define LDBLE long double +#define SCANFORMAT "%Lf" +#else +#define LDBLE double +#define SCANFORMAT "%lf" +#endif +#endif /* _INC_PHRQTYPE_H */ diff --git a/phreeqcpp/cvdense.cpp b/phreeqcpp/cvdense.cpp new file mode 100644 index 00000000..d4f6e3fa --- /dev/null +++ b/phreeqcpp/cvdense.cpp @@ -0,0 +1,566 @@ +/************************************************************************** + * * + * File : cvdense.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for the CVODE dense linear * + * solver, CVDENSE. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + + +#include +#include +#include +#include "Phreeqc.h" +#include "cvdense.h" +#include "cvode.h" +#include "dense.h" +#include "sundialstypes.h" +#include "nvector.h" +#include "sundialsmath.h" + + +#if !defined(WIN32_MEMORY_DEBUG) +#define malloc PHRQ_malloc +#endif +#define MACHENV machEnv->phreeqc_ptr-> +#define CVMEM cv_mem->cv_machenv->phreeqc_ptr-> +#define MACHENV_MALLOC MACHENV +#define CVMEM_MALLOC CVMEM + +#include "phqalloc.h" +/* WARNING don`t include any headers below here */ + +/* Error Messages */ + +#define CVDENSE "CVDense/CVReInitDense-- " + +#define MSG_CVMEM_NULL CVDENSE "CVode Memory is NULL.\n\n" + +#define MSG_MEM_FAIL CVDENSE "A memory request failed.\n\n" + +#define MSG_WRONG_NVEC CVDENSE "Incompatible NVECTOR implementation.\n\n" + +/* Other Constants */ + +#define MIN_INC_MULT RCONST(1000.0) +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) +#define TWO RCONST(2.0) + + +/****************************************************************** + * * + * Types : CVDenseMemRec, CVDenseMem * + *----------------------------------------------------------------* + * The type CVDenseMem is pointer to a CVDenseMemRec. This * + * structure contains CVDense solver-specific data. * + * * + ******************************************************************/ + +typedef struct +{ + + CVDenseJacFn d_jac; /* jac = Jacobian routine to be called */ + + DenseMat d_M; /* M = I - gamma J, gamma = h / l1 */ + + integertype *d_pivots; /* pivots = pivot array for PM = LU */ + + DenseMat d_savedJ; /* savedJ = old Jacobian */ + + long int d_nstlj; /* nstlj = nst at last Jacobian eval. */ + + long int d_nje; /* nje = no. of calls to jac */ + + void *d_J_data; /* J_data is passed to jac */ + +} CVDenseMemRec, *CVDenseMem; + + +/* CVDENSE linit, lsetup, lsolve, lfree, and DQJac routines */ + +static int CVDenseInit(CVodeMem cv_mem); + +static int CVDenseSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, + N_Vector fpred, booleantype * jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); + +static int CVDenseSolve(CVodeMem cv_mem, N_Vector b, N_Vector ycur, + N_Vector fcur); + +static void CVDenseFree(CVodeMem cv_mem); + +static void CVDenseDQJac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype t, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/*************** CVDenseDQJac **************************************** + + This routine generates a dense difference quotient approximation to + the Jacobian of f(t,y). It assumes that a dense matrix of type + DenseMat is stored column-wise, and that elements within each column + are contiguous. The address of the jth column of J is obtained via + the macro DENSE_COL and an N_Vector with the jth column as the + component array is created using N_VMake and N_VSetData. Finally, the + actual computation of the jth column of the Jacobian is done with a + call to N_VLinearSum. + +**********************************************************************/ + +static void +CVDenseDQJac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype tn, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3) +{ + realtype fnorm, minInc, inc, inc_inv, yjsaved, srur; + realtype *y_data, *ewt_data; + N_Vector ftemp, jthCol; + M_Env machEnv; + integertype j; + + machEnv = y->menv; /* Get machine environment */ + + ftemp = vtemp1; /* Rename work vector for use as f vector value */ + + /* Obtain pointers to the data for ewt, y */ + ewt_data = N_VGetData(ewt); + y_data = N_VGetData(y); + + /* Set minimum increment based on uround and norm of f */ + srur = RSqrt(uround); + fnorm = N_VWrmsNorm(fy, ewt); + minInc = (fnorm != ZERO) ? + (MIN_INC_MULT * ABS(h) * uround * N * fnorm) : ONE; + + jthCol = N_VMake(N, y_data, machEnv); /* j loop overwrites this data address */ + + /* This is the only for loop for 0..N-1 in CVODE */ + for (j = 0; j < N; j++) + { + + /* Generate the jth col of J(tn,y) */ + + N_VSetData(DENSE_COL(J, j), jthCol); + yjsaved = y_data[j]; + inc = MAX(srur * ABS(yjsaved), minInc / ewt_data[j]); + y_data[j] += inc; + f(N, tn, y, ftemp, f_data); + inc_inv = ONE / inc; + N_VLinearSum(inc_inv, ftemp, -inc_inv, fy, jthCol); + y_data[j] = yjsaved; + } + + N_VDispose(jthCol); + + /* Increment counter nfe = *nfePtr */ + *nfePtr += N; +} + + +/* Readability Replacements */ + +#define N (cv_mem->cv_N) +#define lmm (cv_mem->cv_lmm) +#define f (cv_mem->cv_f) +#define f_data (cv_mem->cv_f_data) +#define uround (cv_mem->cv_uround) +#define nst (cv_mem->cv_nst) +#define tn (cv_mem->cv_tn) +#define h (cv_mem->cv_h) +#define gamma (cv_mem->cv_gamma) +#define gammap (cv_mem->cv_gammap) +#define gamrat (cv_mem->cv_gamrat) +#define ewt (cv_mem->cv_ewt) +#define nfe (cv_mem->cv_nfe) +#define errfp (cv_mem->cv_errfp) +#define iopt (cv_mem->cv_iopt) +#define linit (cv_mem->cv_linit) +#define lsetup (cv_mem->cv_lsetup) +#define lsolve (cv_mem->cv_lsolve) +#define lfree (cv_mem->cv_lfree) +#define lmem (cv_mem->cv_lmem) +#define setupNonNull (cv_mem->cv_setupNonNull) +#define machenv (cv_mem->cv_machenv) + +#define jac (cvdense_mem->d_jac) +#define M (cvdense_mem->d_M) +#define pivots (cvdense_mem->d_pivots) +#define savedJ (cvdense_mem->d_savedJ) +#define nstlj (cvdense_mem->d_nstlj) +#define nje (cvdense_mem->d_nje) +#define J_data (cvdense_mem->d_J_data) + + +/*************** CVDense ********************************************* + + This routine initializes the memory record and sets various function + fields specific to the dense linear solver module. CVDense first + calls the existing lfree routine if this is not NULL. Then it sets + the cv_linit, cv_lsetup, cv_lsolve, cv_lfree fields in (*cvode_mem) + to be CVDenseInit, CVDenseSetup, CVDenseSolve, and CVDenseFree, + respectively. It allocates memory for a structure of type + CVDenseMemRec and sets the cv_lmem field in (*cvode_mem) to the + address of this structure. It sets setupNonNull in (*cvode_mem) to + TRUE, the d_J_data field in CVDenseMemRec to be the input + parameter jac_data, and the d_jac field to be: + (1) the input parameter djac if djac != NULL or + (2) CVDenseDQJac if djac == NULL. + Finally, it allocates memory for M, savedJ, and pivots. + The return value is SUCCESS = 0, or LMEM_FAIL = -1. + + NOTE: The dense linear solver assumes a serial implementation + of the NVECTOR package. Therefore, CVDense will first + test for compatible a compatible N_Vector internal + representation by checking (1) the machine environment + ID tag and (2) that the functions N_VMake, N_VDispose, + N_VGetData, and N_VSetData are implemented. + +**********************************************************************/ + +int +CVDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data) +{ + CVodeMem cv_mem; + CVDenseMem cvdense_mem; + + /* Return immediately if cvode_mem is NULL */ + cv_mem = (CVodeMem) cvode_mem; + if (cv_mem == NULL) + { /* CVode reports this error */ + return (LMEM_FAIL); + } + + /* Test if the NVECTOR package is compatible with the DENSE solver */ + if ((strcmp(machenv->tag, "serial")) || + machenv->ops->nvmake == NULL || + machenv->ops->nvdispose == NULL || + machenv->ops->nvgetdata == NULL || machenv->ops->nvsetdata == NULL) + { + CVMEM warning_msg( MSG_WRONG_NVEC); + return (LMEM_FAIL); + } + + if (lfree != NULL) + lfree(cv_mem); + + /* Set four main function fields in cv_mem */ + linit = CVDenseInit; + lsetup = CVDenseSetup; + lsolve = CVDenseSolve; + lfree = CVDenseFree; + + /* Get memory for CVDenseMemRec */ +#if defined(WIN32_MEMORY_DEBUG) + lmem = cvdense_mem = (CVDenseMem) malloc(sizeof(CVDenseMemRec)); +#else + lmem = cvdense_mem = (CVDenseMem) CVMEM_MALLOC malloc(sizeof(CVDenseMemRec)); +#endif + if (cvdense_mem == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + return (LMEM_FAIL); + } + + /* Set Jacobian routine field, J_data, and setupNonNull */ + if (djac == NULL) + { + jac = CVDenseDQJac; + } + else + { + jac = djac; + } + J_data = jac_data; + setupNonNull = TRUE; + + /* Allocate memory for M, savedJ, and pivot array */ + + M = DenseAllocMat(N); + if (M == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + return (LMEM_FAIL); + } + savedJ = DenseAllocMat(N); + if (savedJ == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + DenseFreeMat(M); + return (LMEM_FAIL); + } + pivots = DenseAllocPiv(N); + if (pivots == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + DenseFreeMat(M); + DenseFreeMat(savedJ); + return (LMEM_FAIL); + } + + return (SUCCESS); +} + +/*************** CVReInitDense**************************************** + + This routine resets the link between the main CVODE module and the + dense linear solver module CVDENSE. No memory freeing or allocation + operations are done, as the existing linear solver memory is assumed + sufficient. All other initializations are the same as in CVDense. + The return value is SUCCESS = 0, or LMEM_FAIL = -1. + +**********************************************************************/ + +int +CVReInitDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data) +{ + CVodeMem cv_mem; + CVDenseMem cvdense_mem; + + /* Return immediately if cvode_mem is NULL */ + cv_mem = (CVodeMem) cvode_mem; + if (cv_mem == NULL) + { /* CVode reports this error */ + //CVMEM warning_msg( MSG_CVMEM_NULL); +#if !defined(R_SO) + std::cerr << MSG_CVMEM_NULL << std::endl; +#endif + return (LMEM_FAIL); + } + + /* Test if the NVECTOR package is compatible with the DENSE solver */ + if ((strcmp(machenv->tag, "serial")) || + machenv->ops->nvmake == NULL || + machenv->ops->nvdispose == NULL || + machenv->ops->nvgetdata == NULL || machenv->ops->nvsetdata == NULL) + { + CVMEM warning_msg( MSG_WRONG_NVEC); + return (LMEM_FAIL); + } + + cvdense_mem = (CVDenseMem) lmem; /* Use existing linear solver memory pointer */ + + /* Set four main function fields in cv_mem */ + linit = CVDenseInit; + lsetup = CVDenseSetup; + lsolve = CVDenseSolve; + lfree = CVDenseFree; + + /* Set Jacobian routine field, J_data, and setupNonNull */ + if (djac == NULL) + { + jac = CVDenseDQJac; + } + else + { + jac = djac; + } + J_data = jac_data; + setupNonNull = TRUE; + + return (SUCCESS); +} + +/*************** CVDenseInit ***************************************** + + This routine does remaining initializations specific to the dense + linear solver. + +**********************************************************************/ + +static int +CVDenseInit(CVodeMem cv_mem) +{ + CVDenseMem cvdense_mem; + cvdense_mem = (CVDenseMem) lmem; + + /* Initialize nje and nstlj, and set workspace lengths */ + + nje = 0; + if (iopt != NULL) + { + iopt[DENSE_NJE] = nje; + iopt[DENSE_LRW] = 2 * N * N; + iopt[DENSE_LIW] = N; + } + nstlj = 0; + + return (LINIT_OK); +} + +/*************** CVDenseSetup **************************************** + + This routine does the setup operations for the dense linear solver. + It makes a decision whether or not to call the Jacobian evaluation + routine based on various state variables, and if not it uses the + saved copy. In any case, it constructs the Newton matrix + M = I - gamma*J, updates counters, and calls the dense LU + factorization routine. + +**********************************************************************/ + +static int +CVDenseSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, + N_Vector fpred, booleantype * jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) +{ + booleantype jbad, jok; + realtype dgamma; + integertype ier; + CVDenseMem cvdense_mem; + + cvdense_mem = (CVDenseMem) lmem; + + /* Use nst, gamma/gammap, and convfail to set J eval. flag jok */ + + dgamma = ABS((gamma / gammap) - ONE); + jbad = (nst == 0) || (nst > nstlj + CVD_MSBJ) || + ((convfail == FAIL_BAD_J) && (dgamma < CVD_DGMAX)) || + (convfail == FAIL_OTHER); + jok = !jbad; + + if (jok) + { + /* If jok = TRUE, use saved copy of J */ + *jcurPtr = FALSE; + DenseCopy(savedJ, M); + } + else + { + /* If jok = FALSE, call jac routine for new J value */ + nje++; + if (iopt != NULL) + iopt[DENSE_NJE] = nje; + nstlj = nst; + *jcurPtr = TRUE; + DenseZero(M); + jac(N, M, f, f_data, tn, ypred, fpred, ewt, h, + uround, J_data, &nfe, vtemp1, vtemp2, vtemp3); + DenseCopy(M, savedJ); + } + + /* Scale and add I to get M = I - gamma*J */ + DenseScale(-gamma, M); + DenseAddI(M); + + /* Do LU factorization of M */ + ier = DenseFactor(M, pivots); + + /* Return 0 if the LU was complete; otherwise return 1 */ + if (ier > 0) + return (1); + return (0); +} + +/*************** CVDenseSolve **************************************** + + This routine handles the solve operation for the dense linear solver + by calling the dense backsolve routine. The returned value is 0. + +**********************************************************************/ + +static int +CVDenseSolve(CVodeMem cv_mem, N_Vector b, N_Vector ycur, N_Vector fcur) +{ + CVDenseMem cvdense_mem; + realtype *bd; + + cvdense_mem = (CVDenseMem) lmem; + + bd = N_VGetData(b); + DenseBacksolve(M, pivots, bd); + N_VSetData(bd, b); + + /* If BDF, scale the correction to account for change in gamma */ + if ((lmm == BDF) && (gamrat != ONE)) + { + N_VScale(TWO / (ONE + gamrat), b, b); + } + + return (0); +} + +/*************** CVDenseFree ***************************************** + + This routine frees memory specific to the dense linear solver. + +**********************************************************************/ + +static void +CVDenseFree(CVodeMem cv_mem) +{ + CVDenseMem cvdense_mem; + + cvdense_mem = (CVDenseMem) lmem; + + DenseFreeMat(M); + DenseFreeMat(savedJ); + DenseFreePiv(pivots); + CVMEM_MALLOC PHRQ_free(cvdense_mem); +} diff --git a/phreeqcpp/cvdense.h b/phreeqcpp/cvdense.h new file mode 100644 index 00000000..cd2bcc25 --- /dev/null +++ b/phreeqcpp/cvdense.h @@ -0,0 +1,267 @@ +#ifndef _INC_CVDENSE_H +#define _INC_CVDENSE_H +/************************************************************************** + * * + * File : cvdense.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for the CVODE dense linear solver, * + * CVDENSE. * + * * + * Note: The type integertype must be large enough to store the * + * value of the linear system size N. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#ifndef _cvdense_h +#define _cvdense_h + + +#include +#include "cvode.h" +#include "sundialstypes.h" +#include "dense.h" +#include "nvector.h" + + +/****************************************************************** + * * + * CVDENSE solver statistics indices * + *----------------------------------------------------------------* + * The following enumeration gives a symbolic name to each * + * CVDENSE statistic. The symbolic names are used as indices into * + * the iopt and ropt arrays passed to CVodeMalloc. * + * The CVDENSE statistics are: * + * * + * iopt[DENSE_NJE] : number of Jacobian evaluations, i.e. of * + * calls made to the dense Jacobian routine * + * (default or user-supplied). * + * * + * iopt[DENSE_LRW] : size (in realtype words) of real workspace * + * matrices and vectors used by this solver. * + * * + * iopt[DENSE_LIW] : size (in integertype words) of integer * + * workspace vectors used by this solver. * + * * + ******************************************************************/ + + enum + { DENSE_NJE = CVODE_IOPT_SIZE, DENSE_LRW, DENSE_LIW }; + + +/****************************************************************** + * * + * CVDENSE solver constants * + *----------------------------------------------------------------* + * CVD_MSBJ : maximum number of steps between dense Jacobian * + * evaluations * + * * + * CVD_DGMAX : maximum change in gamma between dense Jacobian * + * evaluations * + * * + ******************************************************************/ + +#define CVD_MSBJ 50 + +#define CVD_DGMAX RCONST(0.2) + + +/****************************************************************** + * * + * Type : CVDenseJacFn * + *----------------------------------------------------------------* + * A dense Jacobian approximation function Jac must have the * + * prototype given below. Its parameters are: * + * * + * N is the length of all vector arguments. * + * * + * J is the dense matrix (of type DenseMat) that will be loaded * + * by a CVDenseJacFn with an approximation to the Jacobian matrix * + * J = (df_i/dy_j) at the point (t,y). * + * J is preset to zero, so only the nonzero elements need to be * + * loaded. Two efficient ways to load J are: * + * * + * (1) (with macros - no explicit data structure references) * + * for (j=0; j < N; j++) { * + * col_j = DENSE_COL(J,j); * + * for (i=0; i < N; i++) { * + * generate J_ij = the (i,j)th Jacobian element * + * col_j[i] = J_ij; * + * } * + * } * + * * + * (2) (without macros - explicit data structure references) * + * for (j=0; j < N; j++) { * + * col_j = (J->data)[j]; * + * for (i=0; i < N; i++) { * + * generate J_ij = the (i,j)th Jacobian element * + * col_j[i] = J_ij; * + * } * + * } * + * * + * The DENSE_ELEM(A,i,j) macro is appropriate for use in small * + * problems in which efficiency of access is NOT a major concern. * + * * + * f is the right hand side function for the ODE problem. * + * * + * f_data is a pointer to user data to be passed to f, the same * + * as the F_data parameter passed to CVodeMalloc. * + * * + * t is the current value of the independent variable. * + * * + * y is the current value of the dependent variable vector, * + * namely the predicted value of y(t). * + * * + * fy is the vector f(t,y). * + * * + * ewt is the error weight vector. * + * * + * h is a tentative step size in t. * + * * + * uround is the machine unit roundoff. * + * * + * jac_data is a pointer to user data - the same as the jac_data * + * parameter passed to CVDense. * + * * + * nfePtr is a pointer to the memory location containing the * + * CVODE problem data nfe = number of calls to f. The Jacobian * + * routine should update this counter by adding on the number * + * of f calls made in order to approximate the Jacobian, if any. * + * For example, if the routine calls f a total of N times, then * + * the update is *nfePtr += N. * + * * + * vtemp1, vtemp2, and vtemp3 are pointers to memory allocated * + * for vectors of length N which can be used by a CVDenseJacFn * + * as temporary storage or work space. * + * * + ******************************************************************/ + + typedef void (*CVDenseJacFn) (integertype N, DenseMat J, RhsFn f, + void *f_data, realtype t, N_Vector y, + N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + +/****************************************************************** + * * + * Function : CVDense * + *----------------------------------------------------------------* + * A call to the CVDense function links the main CVODE integrator * + * with the CVDENSE linear solver. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * djac is the dense Jacobian approximation routine to be used. * + * A user-supplied djac routine must be of type * + * CVDenseJacFn. Pass NULL for djac to use the default * + * difference quotient routine CVDenseDQJac supplied * + * with this solver. * + * * + * jac_data is a pointer to user data which is passed to the * + * djac routine every time it is called. * + * * + * The return values of CVDense are: * + * SUCCESS = 0 if successful * + * LMEM_FAIL = -1 if there was a memory allocation failure * + * * + * NOTE: The dense linear solver assumes a serial implementation * + * of the NVECTOR package. Therefore, CVDense will first * + * test for a compatible N_Vector internal representation * + * by checking (1) the machine environment ID tag and * + * (2) that the functions N_VMake, N_VDispose, N_VGetData, * + * and N_VSetData are implemented. * + * * + ******************************************************************/ + + int CVDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data); + + +/****************************************************************** + * * + * Function : CVReInitDense * + *----------------------------------------------------------------* + * A call to the CVReInitDense function resets the link between * + * the main CVODE integrator and the CVDENSE linear solver. * + * After solving one problem using CVDENSE, call CVReInit and then* + * CVReInitDense to solve another problem of the same size, if * + * there is a change in the CVDense parameters djac or jac_data. * + * If there is no change in parameters, it is not necessary to * + * call either CVReInitDense or CVDense for the new problem. * + * * + * All arguments to CVReInitDense have the same names and meanings* + * as those of CVDense. The cvode_mem argument must be identical * + * to its value in the previous CVDense call. * + * * + * The return values of CVReInitDense are: * + * SUCCESS = 0 if successful, or * + * LMEM_FAIL = -1 if the cvode_mem argument is NULL * + * * + * NOTE: CVReInitDense performs the same compatibility tests as * + * CVDense. * + * * + ******************************************************************/ + + int CVReInitDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_CVDENSE_H */ diff --git a/phreeqcpp/cvode.cpp b/phreeqcpp/cvode.cpp new file mode 100644 index 00000000..509412aa --- /dev/null +++ b/phreeqcpp/cvode.cpp @@ -0,0 +1,3939 @@ +/*#define DEBUG_CVODE*/ +/************************************************************************** + * * + * File : cvode.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, Radu Serban, * + * and Dan Shumaker @ LLNL * + * Version of : 24 July 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for the main CVODE integrator. * + * It is independent of the CVODE linear solver in use. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#include "Phreeqc.h" +#include "nvector_serial.h" +#define Ith(v,i) NV_Ith_S(v,i-1) +/************************************************************/ +/******************* BEGIN Imports **************************/ +/************************************************************/ +#include +#include +#include "cvode.h" +#include "sundialstypes.h" +#include "nvector.h" +#include "sundialsmath.h" + + +#if !defined(WIN32_MEMORY_DEBUG) +#define malloc MACHENV_MALLOC PHRQ_malloc +#endif +#define MACHENV machEnv->phreeqc_ptr-> +#define CVMEM cv_mem->cv_machenv->phreeqc_ptr-> +#define MACHENV_MALLOC MACHENV +#define CVMEM_MALLOC CVMEM + +#include "phqalloc.h" +/* WARNING don`t include any headers below here */ + + +/************************************************************/ +/******************** END Imports ***************************/ +/************************************************************/ + + +/***************************************************************/ +/*********************** BEGIN Macros **************************/ +/***************************************************************/ + +/* Macro: loop */ + +#define loop for(;;) + +/***************************************************************/ +/************************ END Macros ***************************/ +/***************************************************************/ + + + +/************************************************************/ +/************** BEGIN CVODE Private Constants ***************/ +/***************************************** +*******************/ + +#define FOURTH RCONST(0.25) /* real 0.25 */ +#define THREE RCONST(3.0) /* real 3.0 */ +#define FOUR RCONST(4.0) /* real 4.0 */ +#define HUN RCONST(100.0) /* real 100.0 */ +#define TINY RCONST(1.0e-10) /* small number */ +#define HALF RCONST(0.5) /* real 0.5 */ +#define ZERO RCONST(0.0) /* real 0.0 */ +#define ONE RCONST(1.0) /* real 1.0 */ +#define TWO RCONST(2.0) /* real 2.0 */ +#define TWELVE RCONST(12.0) /* real 12.0 */ + +/***************************************************************/ +/************** BEGIN Default Constants ************************/ +/***************************************************************/ + +#define HMIN_DEFAULT ZERO /* hmin default value */ +#define HMAX_INV_DEFAULT ZERO /* hmax_inv default value */ +#define MXHNIL_DEFAULT 10 /* mxhnil default value */ +#define MXSTEP_DEFAULT 1000 /* mxstep default value */ + + +/***************************************************************/ +/*************** END Default Constants *************************/ +/***************************************************************/ + + +/***************************************************************/ +/************ BEGIN Routine-Specific Constants *****************/ +/***************************************************************/ + +/* CVodeDky */ + +#define FUZZ_FACTOR RCONST(100.0) + +/* CVHin */ + +#define HLB_FACTOR RCONST(100.0) +#define HUB_FACTOR RCONST(0.1) +#define H_BIAS HALF +#define MAX_ITERS 40 + +/* CVSet */ + +#define CORTES RCONST(0.1) + +/* CVStep return values */ + +#define SUCCESS_STEP 0 +#define REP_ERR_FAIL -1 +#define REP_CONV_FAIL -2 +#define SETUP_FAILED -3 +#define SOLVE_FAILED -4 + +/* CVStep control constants */ + +#define PREDICT_AGAIN -5 +#define DO_ERROR_TEST 1 + +/* CVStep */ + +#define THRESH RCONST(1.5) +#define ETAMX1 RCONST(10000.0) +#define ETAMX2 RCONST(10.0) +#define ETAMX3 RCONST(10.0) +#define ETAMXF RCONST(0.2) +#define ETAMIN RCONST(0.1) +#define ETACF RCONST(0.25) +#define ADDON RCONST(0.000001) +#define BIAS1 RCONST(6.0) +#define BIAS2 RCONST(6.0) +#define BIAS3 RCONST(10.0) +#define ONEPSM RCONST(1.000001) + +#define SMALL_NST 10 /* nst > SMALL_NST => use ETAMX3 */ +#define MXNCF 10 /* max no. of convergence failures during */ + /* one step try */ +#define MXNEF 7 /* max no. of error test failures during */ + /* one step try */ +#define MXNEF1 3 /* max no. of error test failures before */ + /* forcing a reduction of order */ +#define SMALL_NEF 2 /* if an error failure occurs and */ + /* SMALL_NEF <= nef <= MXNEF1, then */ + /* reset eta = MIN(eta, ETAMXF) */ +#define LONG_WAIT 10 /* number of steps to wait before */ + /* considering an order change when */ + /* q==1 and MXNEF1 error test failures */ + /* have occurred */ + +/* CVnls return values */ + +#define SOLVED 0 +#define CONV_FAIL -1 +#define SETUP_FAIL_UNREC -2 +#define SOLVE_FAIL_UNREC -3 + +/* CVnls input flags */ + +#define FIRST_CALL 0 +#define PREV_CONV_FAIL -1 +#define PREV_ERR_FAIL -2 + +/* CVnls other constants */ + +#define FUNC_MAXCOR 3 /* maximum no. of corrector iterations */ + /* for iter == FUNCTIONAL */ +#define NEWT_MAXCOR 3 /* maximum no. of corrector iterations */ + /* for iter == NEWTON */ + +#define CRDOWN RCONST(0.3) /* constant used in the estimation of the */ + /* convergence rate (crate) of the */ + /* iterates for the nonlinear equation */ +#define DGMAX RCONST(0.3) /* iter == NEWTON, |gamma/gammap-1| > DGMAX */ + /* => call lsetup */ + +#define RDIV TWO /* declare divergence if ratio del/delp > RDIV */ +#define MSBP 20 /* max no. of steps between lsetup calls */ + +#define TRY_AGAIN_CVODE 99 /* control constant for CVnlsNewton - should be */ + /* distinct from CVnls return values */ + + +/***************************************************************/ +/*************** END Routine-Specific Constants ***************/ +/***************************************************************/ + + +/***************************************************************/ +/***************** BEGIN Error Messages ************************/ +/***************************************************************/ + +/* CVodeMalloc/CVReInit Error Messages */ + +#define CVM "CVodeMalloc/CVReInit-- " + +#define MSG_Y0_NULL CVM "y0=NULL illegal.\n\n" + +#define MSG_BAD_N CVM "N=%ld < 1 illegal.\n\n" + +#define MSG_BAD_LMM_1 CVM "lmm=%d illegal.\n" +#define MSG_BAD_LMM_2 "The legal values are ADAMS=%d and BDF=%d.\n\n" +#define MSG_BAD_LMM MSG_BAD_LMM_1 MSG_BAD_LMM_2 + +#define MSG_BAD_ITER_1 CVM "iter=%d illegal.\n" +#define MSG_BAD_ITER_2 "The legal values are FUNCTIONAL=%d " +#define MSG_BAD_ITER_3 "and NEWTON=%d.\n\n" +#define MSG_BAD_ITER MSG_BAD_ITER_1 MSG_BAD_ITER_2 MSG_BAD_ITER_3 + +#define MSG_BAD_ITOL_1 CVM "itol=%d illegal.\n" +#define MSG_BAD_ITOL_2 "The legal values are SS=%d and SV=%d.\n\n" +#define MSG_BAD_ITOL MSG_BAD_ITOL_1 MSG_BAD_ITOL_2 + +#define MSG_F_NULL CVM "f=NULL illegal.\n\n" + +#define MSG_RELTOL_NULL CVM "reltol=NULL illegal.\n\n" + +#define MSG_BAD_RELTOL CVM "*reltol=%g < 0 illegal.\n\n" + +#define MSG_ABSTOL_NULL CVM "abstol=NULL illegal.\n\n" + +#define MSG_BAD_ABSTOL CVM "Some abstol component < 0.0 illegal.\n\n" + +#define MSG_BAD_OPTIN_1 CVM "optIn=%d illegal.\n" +#define MSG_BAD_OPTIN_2 "The legal values are FALSE=%d and TRUE=%d.\n\n" +#define MSG_BAD_OPTIN MSG_BAD_OPTIN_1 MSG_BAD_OPTIN_2 + +#define MSG_BAD_OPT CVM "optIn=TRUE, but iopt=ropt=NULL.\n\n" + +#define MSG_MEM_FAIL CVM "A memory request failed.\n\n" + +#define MSG_BAD_EWT CVM "Some initial ewt component = 0.0 illegal.\n\n" + +#define MSG_REI_NO_MEM "CVReInit-- cvode_mem = NULL illegal.\n\n" + +#define MSG_REI_MAXORD1 "CVReInit-- Illegal attempt to increase " +#define MSG_REI_MAXORD2 "maximum method order from %d to %d.\n\n" +#define MSG_REI_MAXORD MSG_REI_MAXORD1 MSG_REI_MAXORD2 + + +/* CVode error messages */ + +#define CVODE "CVode-- " + +#define NO_MEM "cvode_mem=NULL illegal.\n\n" + +#define MSG_CVODE_NO_MEM CVODE NO_MEM + +#define MSG_LINIT_NULL CVODE "The linear solver's init routine is NULL.\n\n" + +#define MSG_LSETUP_NULL CVODE "The linear solver's setup routine is NULL.\n\n" + +#define MSG_LSOLVE_NULL CVODE "The linear solver's solve routine is NULL.\n\n" + +#define MSG_LFREE_NULL CVODE "The linear solver's free routine is NULL.\n\n" + +#define MSG_LINIT_FAIL CVODE "The linear solver's init routine failed.\n\n" + +#define MSG_YOUT_NULL CVODE "yout=NULL illegal.\n\n" + +#define MSG_T_NULL CVODE "t=NULL illegal.\n\n" + +#define MSG_BAD_ITASK_1 CVODE "itask=%d illegal.\nThe legal values are" +#define MSG_BAD_ITASK_2 " NORMAL=%d and ONE_STEP=%d.\n\n" +#define MSG_BAD_ITASK MSG_BAD_ITASK_1 MSG_BAD_ITASK_2 + +#define MSG_BAD_HMIN_HMAX_1 CVODE "Inconsistent step size limits:\n" +#define MSG_BAD_HMIN_HMAX_2 "ropt[HMIN]=%g > ropt[HMAX]=%g.\n\n" +#define MSG_BAD_HMIN_HMAX MSG_BAD_HMIN_HMAX_1 MSG_BAD_HMIN_HMAX_2 + +#define MSG_BAD_H0 CVODE "h0=%g and tout-t0=%g inconsistent.\n\n" + +#define MSG_BAD_TOUT_1 CVODE "Trouble interpolating at tout = %g.\n" +#define MSG_BAD_TOUT_2 "tout too far back in direction of integration.\n\n" +#define MSG_BAD_TOUT MSG_BAD_TOUT_1 MSG_BAD_TOUT_2 + +#define MSG_MAX_STEPS_1 CVODE "At t=%g, mxstep=%d steps taken on " +#define MSG_MAX_STEPS_2 "this call before\nreaching tout=%g.\n\n" +#define MSG_MAX_STEPS MSG_MAX_STEPS_1 MSG_MAX_STEPS_2 + +#define MSG_EWT_NOW_BAD_1 CVODE "At t=%g, " +#define MSG_EWT_NOW_BAD_2 "some ewt component has become <= 0.0.\n\n" +#define MSG_EWT_NOW_BAD MSG_EWT_NOW_BAD_1 MSG_EWT_NOW_BAD_2 + +#define MSG_TOO_MUCH_ACC CVODE "At t=%g, too much accuracy requested.\n\n" + +#define MSG_HNIL_1 CVODE "Warning.. internal t=%g and step size h=%g\n" +#define MSG_HNIL_2 "are such that t + h == t on the next step.\n" +#define MSG_HNIL_3 "The solver will continue anyway.\n\n" +#define MSG_HNIL MSG_HNIL_1 MSG_HNIL_2 MSG_HNIL_3 + +#define MSG_HNIL_DONE_1 CVODE "The above warning has been issued %d times " +#define MSG_HNIL_DONE_2 "and will not be\nissued again for this problem.\n\n" +#define MSG_HNIL_DONE MSG_HNIL_DONE_1 MSG_HNIL_DONE_2 + +#define MSG_ERR_FAILS_1 CVODE "At t=%g and step size h=%g, the error test\n" +#define MSG_ERR_FAILS_2 "failed repeatedly or with |h| = hmin.\n\n" +#define MSG_ERR_FAILS MSG_ERR_FAILS_1 MSG_ERR_FAILS_2 + +#define MSG_CONV_FAILS_1 CVODE "At t=%g and step size h=%g, the corrector\n" +#define MSG_CONV_FAILS_2 "convergence failed repeatedly or " +#define MSG_CONV_FAILS_3 "with |h| = hmin.\n\n" +#define MSG_CONV_FAILS MSG_CONV_FAILS_1 MSG_CONV_FAILS_2 MSG_CONV_FAILS_3 + +#define MSG_SETUP_FAILED_1 CVODE "At t=%g, the setup routine failed in an " +#define MSG_SETUP_FAILED_2 "unrecoverable manner.\n\n" +#define MSG_SETUP_FAILED MSG_SETUP_FAILED_1 MSG_SETUP_FAILED_2 + +#define MSG_SOLVE_FAILED_1 CVODE "At t=%g, the solve routine failed in an " +#define MSG_SOLVE_FAILED_2 "unrecoverable manner.\n\n" +#define MSG_SOLVE_FAILED MSG_SOLVE_FAILED_1 MSG_SOLVE_FAILED_2 + +#define MSG_TOO_CLOSE_1 CVODE "tout=%g too close to t0=%g to start" +#define MSG_TOO_CLOSE_2 " integration.\n\n" +#define MSG_TOO_CLOSE MSG_TOO_CLOSE_1 MSG_TOO_CLOSE_2 + + +/* CVodeDky Error Messages */ + +#define DKY "CVodeDky-- " + +#define MSG_DKY_NO_MEM DKY NO_MEM + +#define MSG_BAD_K DKY "k=%d illegal.\n\n" + +#define MSG_BAD_T_1 DKY "t=%g illegal.\n" +#define MSG_BAD_T_2 "t not in interval tcur-hu=%g to tcur=%g.\n\n" +#define MSG_BAD_T MSG_BAD_T_1 MSG_BAD_T_2 + +#define MSG_BAD_DKY DKY "dky=NULL illegal.\n\n" + +/***************************************************************/ +/****************** END Error Messages *************************/ +/***************************************************************/ + + +/************************************************************/ +/*************** END CVODE Private Constants ****************/ +/************************************************************/ + + +/**************************************************************/ +/********* BEGIN Private Helper Functions Prototypes **********/ +/**************************************************************/ + +static booleantype CVAllocVectors(CVodeMem cv_mem, integertype neq, + int maxord, M_Env machEnv); +static void CVFreeVectors(CVodeMem cv_mem, int maxord); + +static booleantype CVEwtSet(CVodeMem cv_mem, N_Vector ycur); +static booleantype CVEwtSetSS(CVodeMem cv_mem, N_Vector ycur); +static booleantype CVEwtSetSV(CVodeMem cv_mem, N_Vector ycur); + +static booleantype CVHin(CVodeMem cv_mem, realtype tout); +static realtype CVUpperBoundH0(CVodeMem cv_mem, realtype tdist); +static realtype CVYddNorm(CVodeMem cv_mem, realtype hg); + +static int CVStep(CVodeMem cv_mem); + +#ifdef ORIGINAL_CVBDFStab +static int CVsldet(CVodeMem cv_mem); +#endif +static void CVAdjustParams(CVodeMem cv_mem); +static void CVAdjustOrder(CVodeMem cv_mem, int deltaq); +static void CVAdjustAdams(CVodeMem cv_mem, int deltaq); +static void CVAdjustBDF(CVodeMem cv_mem, int deltaq); +static void CVIncreaseBDF(CVodeMem cv_mem); +static void CVDecreaseBDF(CVodeMem cv_mem); + +static void CVRescale(CVodeMem cv_mem); + +static void CVPredict(CVodeMem cv_mem); + +static void CVSet(CVodeMem cv_mem); +static void CVSetAdams(CVodeMem cv_mem); +static realtype CVAdamsStart(CVodeMem cv_mem, realtype m[]); +static void CVAdamsFinish(CVodeMem cv_mem, realtype m[], realtype M[], + realtype hsum); +static realtype CVAltSum(int iend, realtype a[], int k); +static void CVSetBDF(CVodeMem cv_mem); +static void CVSetTqBDF(CVodeMem cv_mem, realtype hsum, realtype alpha0, + realtype alpha0_hat, realtype xi_inv, + realtype xistar_inv); + +static int CVnls(CVodeMem cv_mem, int nflag); +static int CVnlsFunctional(CVodeMem cv_mem); +static int CVnlsNewton(CVodeMem cv_mem, int nflag); +static int CVNewtonIteration(CVodeMem cv_mem); + +static int CVHandleNFlag(CVodeMem cv_mem, int *nflagPtr, realtype saved_t, + int *ncfPtr); + +static void CVRestore(CVodeMem cv_mem, realtype saved_t); + +static booleantype CVDoErrorTest(CVodeMem cv_mem, int *nflagPtr, + int *kflagPtr, realtype saved_t, + int *nefPtr, realtype * dsmPtr); + +static void CVCompleteStep(CVodeMem cv_mem); + +static void CVPrepareNextStep(CVodeMem cv_mem, realtype dsm); +static void CVSetEta(CVodeMem cv_mem); +static realtype CVComputeEtaqm1(CVodeMem cv_mem); +static realtype CVComputeEtaqp1(CVodeMem cv_mem); +static void CVChooseEta(CVodeMem cv_mem); +static void CVBDFStab(CVodeMem cv_mem); + +static int CVHandleFailure(CVodeMem cv_mem, int kflag); + + +/**************************************************************/ +/********** END Private Helper Functions Prototypes ***********/ +/**************************************************************/ + + +/**************************************************************/ +/**************** BEGIN Readability Constants *****************/ +/**************************************************************/ + + +#define uround (cv_mem->cv_uround) +#define zn (cv_mem->cv_zn) +#define ewt (cv_mem->cv_ewt) +#define y (cv_mem->cv_y) +#define acor (cv_mem->cv_acor) +#define tempv (cv_mem->cv_tempv) +#define ftemp (cv_mem->cv_ftemp) +#define q (cv_mem->cv_q) +#define qprime (cv_mem->cv_qprime) +#define qwait (cv_mem->cv_qwait) +#define L (cv_mem->cv_L) +#define h (cv_mem->cv_h) +#define hprime (cv_mem->cv_hprime) +#define eta (cv_mem-> cv_eta) +#define etaqm1 (cv_mem-> cv_etaqm1) +#define etaq (cv_mem-> cv_etaq) +#define etaqp1 (cv_mem-> cv_etaqp1) +#define nscon (cv_mem->cv_nscon) +#define ssdat (cv_mem->cv_ssdat) +#define hscale (cv_mem->cv_hscale) +#define tn (cv_mem->cv_tn) +#define tau (cv_mem->cv_tau) +#define tq (cv_mem->cv_tq) +#define l (cv_mem->cv_l) +#define rl1 (cv_mem->cv_rl1) +#define gamma (cv_mem->cv_gamma) +#define gammap (cv_mem->cv_gammap) +#define gamrat (cv_mem->cv_gamrat) +#define crate (cv_mem->cv_crate) +#define acnrm (cv_mem->cv_acnrm) +#define mnewt (cv_mem->cv_mnewt) +#define qmax (cv_mem->cv_qmax) +#define mxstep (cv_mem->cv_mxstep) +#define maxcor (cv_mem->cv_maxcor) +#define mxhnil (cv_mem->cv_mxhnil) +#define hmin (cv_mem->cv_hmin) +#define hmax_inv (cv_mem->cv_hmax_inv) +#define etamax (cv_mem->cv_etamax) +#define nst (cv_mem->cv_nst) +#define nfe (cv_mem->cv_nfe) +#define ncfn (cv_mem->cv_ncfn) +#define netf (cv_mem->cv_netf) +#define nni (cv_mem-> cv_nni) +#define nsetups (cv_mem->cv_nsetups) +#define nhnil (cv_mem->cv_nhnil) +#define lrw (cv_mem->cv_lrw) +#define liw (cv_mem->cv_liw) +#define linit (cv_mem->cv_linit) +#define lsetup (cv_mem->cv_lsetup) +#define lsolve (cv_mem->cv_lsolve) +#define lfree (cv_mem->cv_lfree) +#define lmem (cv_mem->cv_lmem) +#define qu (cv_mem->cv_qu) +#define nstlp (cv_mem->cv_nstlp) +#define hu (cv_mem->cv_hu) +#define saved_tq5 (cv_mem->cv_saved_tq5) +#define jcur (cv_mem->cv_jcur) +#define tolsf (cv_mem->cv_tolsf) +#define setupNonNull (cv_mem->cv_setupNonNull) +#define machenv (cv_mem->cv_machenv) +#define sldeton (cv_mem->cv_sldeton) + +/**************************************************************/ +/***************** END Readability Constants ******************/ +/**************************************************************/ + + +/***************************************************************/ +/************* BEGIN CVODE Implementation **********************/ +/***************************************************************/ + + +/***************************************************************/ +/********* BEGIN Exported Functions Implementation *************/ +/***************************************************************/ + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/******************** CVodeMalloc ******************************* + + CVodeMalloc allocates and initializes memory for a problem. All + problem specification inputs are checked for errors. If any + error occurs during initialization, it is reported to the file + whose file pointer is errfp and NULL is returned. Otherwise, the + pointer to successfully initialized problem memory is returned. + +*****************************************************************/ + +void * +CVodeMalloc(integertype N, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, + realtype * reltol, void *abstol, + void *f_data, FILE * errfp, booleantype optIn, + long int iopt[], realtype ropt[], M_Env machEnv) +{ + booleantype allocOK, ioptExists, roptExists, neg_abstol, ewtsetOK; + int maxord; + CVodeMem cv_mem; +#if !defined(R_SO) + FILE *fp; +#endif + int i, k; + + /* Check for legal input parameters */ + +#if !defined(R_SO) + fp = (errfp == NULL) ? stdout : errfp; +#endif + + if (y0 == NULL) + { + MACHENV warning_msg( MSG_Y0_NULL); + return (NULL); + } + + if (N <= 0) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_N, N)); + return (NULL); + } + + if ((lmm != ADAMS) && (lmm != BDF)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_LMM, lmm, ADAMS, BDF)); + return (NULL); + } + + if ((iter != FUNCTIONAL) && (iter != NEWTON)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITER, iter, FUNCTIONAL, NEWTON)); + return (NULL); + } + + if ((itol != SS) && (itol != SV)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITOL, itol, SS, SV)); + return (NULL); + } + + if (f == NULL) + { + MACHENV warning_msg(MSG_F_NULL); + return (NULL); + } + + if (reltol == NULL) + { + MACHENV warning_msg(MSG_RELTOL_NULL); + return (NULL); + } + + if (*reltol < ZERO) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_RELTOL, (double) *reltol)); + return (NULL); + } + + if (abstol == NULL) + { + MACHENV warning_msg(MSG_ABSTOL_NULL); + return (NULL); + } + + if (itol == SS) + { + neg_abstol = (*((realtype *) abstol) < ZERO); + } + else + { + neg_abstol = (N_VMin((N_Vector) abstol) < ZERO); + } + if (neg_abstol) + { + MACHENV warning_msg(MSG_BAD_ABSTOL); + return (NULL); + } + + if ((optIn != FALSE) && (optIn != TRUE)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_OPTIN, optIn, FALSE, TRUE)); + return (NULL); + } + + if ((optIn) && (iopt == NULL) && (ropt == NULL)) + { + MACHENV warning_msg(MSG_BAD_OPT); + return (NULL); + } + + ioptExists = (iopt != NULL); + roptExists = (ropt != NULL); + + /* Compute maxord */ + + maxord = (lmm == ADAMS) ? ADAMS_Q_MAX : BDF_Q_MAX; + + if (optIn && ioptExists) + { + if (iopt[MAXORD] > 0) + maxord = MIN(maxord, iopt[MAXORD]); + } + + cv_mem = (CVodeMem) malloc(sizeof(struct CVodeMemRec)); + if (cv_mem == NULL) + { + MACHENV warning_msg(MSG_MEM_FAIL); + return (NULL); + } + + /* Allocate the vectors */ + + allocOK = CVAllocVectors(cv_mem, N, maxord, machEnv); + if (!allocOK) + { + MACHENV warning_msg(MSG_MEM_FAIL); + MACHENV_MALLOC PHRQ_free(cv_mem); + return (NULL); + } + + /* Copy tolerances into memory, and set the ewt vector */ + + cv_mem->cv_itol = itol; + cv_mem->cv_reltol = reltol; + cv_mem->cv_abstol = abstol; + ewtsetOK = CVEwtSet(cv_mem, y0); + if (!ewtsetOK) + { + MACHENV warning_msg(MSG_BAD_EWT); + CVFreeVectors(cv_mem, maxord); + MACHENV_MALLOC PHRQ_free(cv_mem); + return (NULL); + } + + /* All error checking is complete at this point */ + + /* Copy the remaining input parameters into CVODE memory */ + + cv_mem->cv_N = N; + cv_mem->cv_f = f; + cv_mem->cv_f_data = f_data; + cv_mem->cv_lmm = lmm; + cv_mem->cv_iter = iter; + cv_mem->cv_optIn = optIn; + cv_mem->cv_iopt = iopt; + cv_mem->cv_ropt = ropt; +#if !defined(R_SO) + cv_mem->cv_errfp = fp; +#endif + tn = t0; + machenv = machEnv; + + /* Set step parameters */ + + q = 1; + L = 2; + qwait = L; + qmax = maxord; + etamax = ETAMX1; + + /* Set uround */ + + uround = UnitRoundoff(); + + /* Set the linear solver addresses to NULL. + (We check != NULL later, in CVode, if using NEWTON.) */ + + linit = NULL; + lsetup = NULL; + lsolve = NULL; + lfree = NULL; + lmem = NULL; + + /* Initialize zn[0] in the history array */ + + N_VScale(ONE, y0, zn[0]); + + /* Handle the remaining optional inputs (CVode checks ropt[HMAX]) */ + + hmax_inv = HMAX_INV_DEFAULT; + hmin = HMIN_DEFAULT; + if (optIn && roptExists) + { + if (ropt[HMIN] > ZERO) + hmin = ropt[HMIN]; + } + + mxhnil = MXHNIL_DEFAULT; + mxstep = MXSTEP_DEFAULT; + if (optIn && ioptExists) + { + if (iopt[MXHNIL] != 0) + mxhnil = iopt[MXHNIL]; + if (iopt[MXSTEP] > 0) + mxstep = iopt[MXSTEP]; + } + + if ((!optIn) && roptExists) + ropt[H0] = ZERO; + + /* Set maxcor */ + + maxcor = (iter == NEWTON) ? NEWT_MAXCOR : FUNC_MAXCOR; + + /* Initialize all the counters */ + + nst = nfe = ncfn = netf = nni = nsetups = nhnil = nstlp = 0; + + /* Initialize all other variables corresponding to optional outputs */ + + qu = 0; + hu = ZERO; + tolsf = ONE; + + /* Initialize optional output locations in iopt, ropt */ + /* and Stablilty Limit Detection data. */ + + nscon = 0; + sldeton = FALSE; + if (ioptExists) + { + iopt[NST] = iopt[NFE] = iopt[NSETUPS] = iopt[NNI] = 0; + iopt[NCFN] = iopt[NETF] = 0; + iopt[QU] = qu; + iopt[QCUR] = 0; + iopt[LENRW] = lrw; + iopt[LENIW] = liw; + if (optIn && iopt[SLDET] && (lmm == BDF)) + { + sldeton = TRUE; + iopt[NOR] = 0; + for (i = 1; i <= 5; i++) + { + for (k = 1; k <= 3; k++) + ssdat[i - 1][k - 1] = ZERO; + } + } + } + + if (roptExists) + { + ropt[HU] = hu; + ropt[HCUR] = ZERO; + ropt[TCUR] = t0; + ropt[TOLSF] = tolsf; + } + + + /* Problem has been successfully initialized */ + + return ((void *) cv_mem); +} + + +/******************** CVReInit ********************************** + + CVReInit re-initializes CVODE's memory for a problem, assuming + it has already been allocated in a prior CVodeMalloc call. + All problem specification inputs are checked for errors. + The problem size N is assumed to be unchanged since the call to + CVodeMalloc, and the maximum order maxord must not be larger. + If any error occurs during initialization, it is reported to the + file whose file pointer is errfp. + The return value is SUCCESS = 0 if no errors occurred, or + a negative value otherwise. + +*****************************************************************/ + +int +CVReInit(void *cvode_mem, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, + realtype * reltol, void *abstol, + void *f_data, FILE * errfp, booleantype optIn, + long int iopt[], realtype ropt[], M_Env machEnv) +{ + booleantype ioptExists, roptExists, neg_abstol, ewtsetOK; + int maxord, i, k; + CVodeMem cv_mem; +#if !defined(R_SO) + FILE *fp; +#endif + /* Check for legal input parameters */ + +#if !defined(R_SO) + fp = (errfp == NULL) ? stdout : errfp; +#endif + + if (cvode_mem == NULL) + { + MACHENV warning_msg(MSG_REI_NO_MEM); + return (CVREI_NO_MEM); + } + cv_mem = (CVodeMem) cvode_mem; + + if (y0 == NULL) + { + MACHENV warning_msg(MSG_Y0_NULL); + return (CVREI_ILL_INPUT); + } + + if ((lmm != ADAMS) && (lmm != BDF)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_LMM, lmm, ADAMS, BDF)); + return (CVREI_ILL_INPUT); + } + + if ((iter != FUNCTIONAL) && (iter != NEWTON)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITER, iter, FUNCTIONAL, NEWTON)); + return (CVREI_ILL_INPUT); + } + + if ((itol != SS) && (itol != SV)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITOL, itol, SS, SV)); + return (CVREI_ILL_INPUT); + } + + if (f == NULL) + { + MACHENV warning_msg(MSG_F_NULL); + return (CVREI_ILL_INPUT); + } + + if (reltol == NULL) + { + MACHENV warning_msg(MSG_RELTOL_NULL); + return (CVREI_ILL_INPUT); + } + + if (*reltol < ZERO) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_RELTOL, (double) *reltol)); + return (CVREI_ILL_INPUT); + } + + if (abstol == NULL) + { + MACHENV warning_msg(MSG_ABSTOL_NULL); + return (CVREI_ILL_INPUT); + } + + if (itol == SS) + { + neg_abstol = (*((realtype *) abstol) < ZERO); + } + else + { + neg_abstol = (N_VMin((N_Vector) abstol) < ZERO); + } + if (neg_abstol) + { + MACHENV warning_msg(MSG_BAD_ABSTOL); + return (CVREI_ILL_INPUT); + } + + if ((optIn != FALSE) && (optIn != TRUE)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_OPTIN, optIn, FALSE, TRUE)); + return (CVREI_ILL_INPUT); + } + + if ((optIn) && (iopt == NULL) && (ropt == NULL)) + { + MACHENV warning_msg(MSG_BAD_OPT); + return (CVREI_ILL_INPUT); + } + + ioptExists = (iopt != NULL); + roptExists = (ropt != NULL); + + /* Compute new maxord and check against old value */ + + maxord = (lmm == ADAMS) ? ADAMS_Q_MAX : BDF_Q_MAX; + if (optIn && ioptExists) + { + if (iopt[MAXORD] > 0) + maxord = MIN(maxord, iopt[MAXORD]); + } + if (maxord > qmax) + { + MACHENV warning_msg(MACHENV sformatf(MSG_REI_MAXORD, qmax, maxord)); + return (CVREI_ILL_INPUT); + } + + /* Copy tolerances into memory, and set the ewt vector */ + + cv_mem->cv_itol = itol; + cv_mem->cv_reltol = reltol; + cv_mem->cv_abstol = abstol; + ewtsetOK = CVEwtSet(cv_mem, y0); + if (!ewtsetOK) + { + MACHENV warning_msg(MSG_BAD_EWT); + return (CVREI_ILL_INPUT); + } + + /* All error checking is complete at this point */ + + /* Copy the remaining input parameters into CVODE memory */ + + cv_mem->cv_f = f; + cv_mem->cv_f_data = f_data; + cv_mem->cv_lmm = lmm; + cv_mem->cv_iter = iter; + cv_mem->cv_optIn = optIn; + cv_mem->cv_iopt = iopt; + cv_mem->cv_ropt = ropt; +#if !defined(R_SO) + cv_mem->cv_errfp = fp; +#endif + tn = t0; + machenv = machEnv; + + /* Set step parameters */ + + q = 1; + L = 2; + qwait = L; + qmax = maxord; + etamax = ETAMX1; + + /* Set uround */ + + uround = UnitRoundoff(); + + /* Initialize zn[0] in the history array */ + + N_VScale(ONE, y0, zn[0]); + + /* Handle the remaining optional inputs (CVode checks ropt[HMAX]) */ + + hmax_inv = HMAX_INV_DEFAULT; + hmin = HMIN_DEFAULT; + if (optIn && roptExists) + { + if (ropt[HMIN] > ZERO) + hmin = ropt[HMIN]; + } + + mxhnil = MXHNIL_DEFAULT; + mxstep = MXSTEP_DEFAULT; + if (optIn && ioptExists) + { + if (iopt[MXHNIL] != 0) + mxhnil = iopt[MXHNIL]; + if (iopt[MXSTEP] > 0) + mxstep = iopt[MXSTEP]; + } + + if ((!optIn) && roptExists) + ropt[H0] = ZERO; + + /* Set maxcor */ + + maxcor = (iter == NEWTON) ? NEWT_MAXCOR : FUNC_MAXCOR; + + /* Initialize all the counters */ + + nst = nfe = ncfn = netf = nni = nsetups = nhnil = nstlp = 0; + + /* Initialize all other vars corresponding to optional outputs */ + + qu = 0; + hu = ZERO; + tolsf = ONE; + + /* Initialize optional output locations in iopt, ropt */ + /* and Stablilty Limit Detection data. */ + + nscon = 0; + sldeton = FALSE; + if (ioptExists) + { + iopt[NST] = iopt[NFE] = iopt[NSETUPS] = iopt[NNI] = 0; + iopt[NCFN] = iopt[NETF] = 0; + iopt[QU] = qu; + iopt[QCUR] = 0; + iopt[LENRW] = lrw; + iopt[LENIW] = liw; + if (optIn && iopt[SLDET] && (lmm == BDF)) + { + sldeton = TRUE; + iopt[NOR] = 0; + for (i = 1; i <= 5; i++) + { + for (k = 1; k <= 3; k++) + ssdat[i - 1][k - 1] = ZERO; + } + } + } + + if (roptExists) + { + ropt[HU] = hu; + ropt[HCUR] = ZERO; + ropt[TCUR] = t0; + ropt[TOLSF] = tolsf; + } + + /* Problem has been successfully re-initialized */ + + return (SUCCESS); +} + + +/**************************************************************/ +/************** BEGIN More Readability Constants **************/ +/**************************************************************/ + +#define N (cv_mem->cv_N) +#define f (cv_mem->cv_f) +#define f_data (cv_mem->cv_f_data) +#define lmm (cv_mem->cv_lmm) +#define iter (cv_mem->cv_iter) +#define itol (cv_mem->cv_itol) +#define reltol (cv_mem->cv_reltol) +#define abstol (cv_mem->cv_abstol) +#define optIn (cv_mem->cv_optIn) +#define iopt (cv_mem->cv_iopt) +#define ropt (cv_mem->cv_ropt) +#define errfp (cv_mem->cv_errfp) + +/**************************************************************/ +/*************** END More Readability Constants ***************/ +/**************************************************************/ + + +/********************* CVode **************************************** + + This routine is the main driver of the CVODE package. + + It integrates over a time interval defined by the user, by calling + CVStep to do internal time steps. + + The first time that CVode is called for a successfully initialized + problem, it computes a tentative initial step size h. + + CVode supports two modes, specified by itask: NORMAL and ONE_STEP. + In the NORMAL mode, the solver steps until it reaches or passes tout + and then interpolates to obtain y(tout). + In the ONE_STEP mode, it takes one internal step and returns. + +********************************************************************/ + +int +CVode(void *cvode_mem, realtype tout, N_Vector yout, realtype * t, int itask) +{ + int nstloc, kflag, istate, next_q, ier; + realtype rh, next_h; + booleantype hOK, ewtsetOK; + CVodeMem cv_mem; + realtype t0; + + /* Check for legal inputs in all cases */ + + cv_mem = (CVodeMem) cvode_mem; + if (cvode_mem == NULL) + { + CVMEM warning_msg(MSG_CVODE_NO_MEM); + return (CVODE_NO_MEM); + } + + if ((y = yout) == NULL) + { + CVMEM warning_msg(MSG_YOUT_NULL); + return (ILL_INPUT); + } + + if (t == NULL) + { + CVMEM warning_msg(MSG_T_NULL); + return (ILL_INPUT); + } + t0 = tn; + *t = tn; + + if ((itask != NORMAL) && (itask != ONE_STEP)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_ITASK, itask, NORMAL, ONE_STEP)); + return (ILL_INPUT); + } + + /* Set hmax_inv from ropt[HMAX] and test for hmin > hmax */ + + if (optIn && ropt != NULL) + { + if (ropt[HMAX] > ZERO) + hmax_inv = ONE / ropt[HMAX]; + if (hmin * hmax_inv > ONE) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_HMIN_HMAX, (double) hmin, + (double) ropt[HMAX])); + return (ILL_INPUT); + } + } + + /* On first call, check solver functions and call linit function */ + + if (nst == 0) + { + if (iter == NEWTON) + { + if (linit == NULL) + { + CVMEM warning_msg(MSG_LINIT_NULL); + return (ILL_INPUT); + } + if (lsetup == NULL) + { + CVMEM warning_msg(MSG_LSETUP_NULL); + return (ILL_INPUT); + } + if (lsolve == NULL) + { + CVMEM warning_msg(MSG_LSOLVE_NULL); + return (ILL_INPUT); + } + if (lfree == NULL) + { + CVMEM warning_msg(MSG_LFREE_NULL); + return (ILL_INPUT); + } + ier = linit(cv_mem); + if (ier != LINIT_OK) + { + CVMEM warning_msg(MSG_LINIT_FAIL); + return (ILL_INPUT); + } + } + + /* On the first call, call f at (t0,y0), set zn[1] = y'(t0), + set initial h (from H0 or CVHin), and scale zn[1] by h */ + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tn; + CVMEM cvode_step_fraction = 0; + + f(N, tn, zn[0], zn[1], f_data); + nfe = 1; + h = ZERO; + if (ropt != NULL) + h = ropt[H0]; + if ((h != ZERO) && ((tout - tn) * h < ZERO)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_H0, (double) h, + (double) (tout - tn))); + return (ILL_INPUT); + } + if (h == ZERO) + { + hOK = CVHin(cv_mem, tout); + if (!hOK) + { + CVMEM warning_msg(CVMEM sformatf(MSG_TOO_CLOSE, (double) tout, + (double) tn)); + return (ILL_INPUT); + } + } + rh = ABS(h) * hmax_inv; + if (rh > ONE) + h /= rh; + if (ABS(h) < hmin) + h *= hmin / ABS(h); + hscale = h; + N_VScale(h, zn[1], zn[1]); + + } /* end of first call block */ + + /* If not the first call, check if tout already reached */ + + if ((itask == NORMAL) && (nst > 0) && ((tn - tout) * h >= ZERO)) + { + *t = tout; + ier = CVodeDky(cv_mem, tout, 0, yout); + if (ier != OKAY) + { /* ier must be == BAD_T */ + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_TOUT, (double) tout)); + return (ILL_INPUT); + } + return (SUCCESS); + } + + /* Looping point for internal steps */ + + nstloc = 0; + loop + { + + next_h = h; + next_q = q; + + /* Reset and check ewt */ + + if (nst > 0) + { + ewtsetOK = CVEwtSet(cv_mem, zn[0]); + if (!ewtsetOK) + { + CVMEM warning_msg(CVMEM sformatf(MSG_EWT_NOW_BAD, (double) tn)); + istate = ILL_INPUT; + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + } + + /* Check for too many steps */ + + if (nstloc >= mxstep) + { + istate = TOO_MUCH_WORK; + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + + /* Check for too much accuracy requested */ + + if ((tolsf = uround * N_VWrmsNorm(zn[0], ewt)) > ONE) + { + CVMEM warning_msg(CVMEM sformatf(MSG_TOO_MUCH_ACC, (double) tn)); + istate = TOO_MUCH_ACC; + *t = tn; + N_VScale(ONE, zn[0], yout); + tolsf *= TWO; + break; + } + + /* Check for h below roundoff level in tn */ + + if (tn + h == tn) + { + nhnil++; + if (nhnil <= mxhnil) + CVMEM warning_msg(CVMEM sformatf(MSG_HNIL, (double) tn, (double) h)); + if (nhnil == mxhnil) + CVMEM warning_msg(CVMEM sformatf(MSG_HNIL_DONE, mxhnil)); + } + + /* Call CVStep to take a step */ + + kflag = CVStep(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVStep, y Fail\n"); + } + else + { + CVMEM warning_msg("After CVStep, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CCVMEM warning_msg("After CVStep, zn Fail\n"); + } + else + { + CVMEM warning_msg("After CVStep, zn OK\n"); + } +#endif + /* Process failed step cases, and exit loop */ + + if (kflag != SUCCESS_STEP) + { + istate = CVHandleFailure(cv_mem, kflag); + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + + nstloc++; + + /* Check if in one-step mode, and if so copy y and exit loop */ + + if (itask == ONE_STEP) + { + istate = SUCCESS; + *t = tn; + N_VScale(ONE, zn[0], yout); + next_q = qprime; + next_h = hprime; + break; + } + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tn; + CVMEM cvode_step_fraction = (tn - t0) / (tout - t0); + /* + CVMEM warning_msg(CVMEM sformatf("ODE: tn %e, t0 %e, tout %e, step_frac %e\n", (double) tn, (double) t0, (double) tout, (double) cvode_step_fraction)); + */ + /* Check if tout reached, and if so interpolate and exit loop */ + + if ((tn - tout) * h >= ZERO) + { + /* + CVMEM warning_msg("*tn %e, t0 %e, tout %e, h %e\n", tn, t0, tout,h).c_str()); + */ + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tout; + CVMEM cvode_step_fraction = 1.0; + istate = SUCCESS; + *t = tout; + (void) CVodeDky(cv_mem, tout, 0, yout); + next_q = qprime; + next_h = hprime; + break; + } + } + + /* End of step loop; load optional outputs and return */ + + if (iopt != NULL) + { + iopt[NST] = nst; + iopt[NFE] = nfe; + iopt[NSETUPS] = nsetups; + iopt[NNI] = nni; + iopt[NCFN] = ncfn; + iopt[NETF] = netf; + iopt[QU] = q; + iopt[QCUR] = next_q; + } + + if (ropt != NULL) + { + ropt[HU] = h; + ropt[HCUR] = next_h; + ropt[TCUR] = tn; + ropt[TOLSF] = tolsf; + } +#ifdef DEBUG_CVODE + /* + * check interpolation + */ + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + //CVMEM warning_msg("End of cvode, Interpolated y Fail\n"); + fprintf(stderr, "End of cvode, Interpolated y Fail\n"); + return (-1); + } + else + { + //CVMEM warning_msg("End of cvode, Interpolated y OK\n"); + //fprintf(stderr, "End of cvode, Interpolated y OK\n"); + //for (int i = 0; i <= N; i++) + //{ + // fprintf(stderr, "%d %e\n", i, Ith(y, i)); + //} + } +#endif + return (istate); +} + +/*************** CVodeDky ******************************************** + + This routine computes the k-th derivative of the interpolating + polynomial at the time t and stores the result in the vector dky. + The formula is: + q + dky = SUM c(j,k) * (t - tn)^(j-k) * h^(-j) * zn[j] , + j=k + where c(j,k) = j*(j-1)*...*(j-k+1), q is the current order, and + zn[j] is the j-th column of the Nordsieck history array. + + This function is called by CVode with k = 0 and t = tout, but + may also be called directly by the user. + +**********************************************************************/ + +int +CVodeDky(void *cvode_mem, realtype t, int k, N_Vector dky) +{ + realtype s, c, r; + realtype tfuzz, tp, tn1; + int i, j; + CVodeMem cv_mem; + + cv_mem = (CVodeMem) cvode_mem; + + /* Check all inputs for legality */ + + if (cvode_mem == NULL) + { + /* + CVMEM warning_msg(MSG_DKY_NO_MEM); + */ + return (DKY_NO_MEM); + } + + if (dky == NULL) + { + CVMEM warning_msg(MSG_BAD_DKY); + return (BAD_DKY); + } + + if ((k < 0) || (k > q)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_K, k)); + return (BAD_K); + } + + tfuzz = FUZZ_FACTOR * uround * (ABS(tn) + ABS(hu)); + if (hu < ZERO) + tfuzz = -tfuzz; + tp = tn - hu - tfuzz; + tn1 = tn + tfuzz; + if ((t - tp) * (t - tn1) > ZERO) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_T, (double) t, (double) (tn - hu), + (double) tn)); + return (BAD_T); + } + + /* Sum the differentiated interpolating polynomial */ + + s = (t - tn) / h; + for (j = q; j >= k; j--) + { + c = ONE; + for (i = j; i >= j - k + 1; i--) + c *= i; + if (j == q) + { + N_VScale(c, zn[q], dky); + } + else + { + N_VLinearSum(c, zn[j], s, dky, dky); + } + } + if (k == 0) + return (OKAY); + r = RPowerI(h, -k); + N_VScale(r, dky, dky); + return (OKAY); +} + +/********************* CVodeFree ********************************** + + This routine frees the problem memory allocated by CVodeMalloc. + Such memory includes all the vectors allocated by CVAllocVectors, + and the memory lmem for the linear solver (deallocated by a call + to lfree). + +*******************************************************************/ + +void +CVodeFree(void *cvode_mem) +{ + CVodeMem cv_mem; + + cv_mem = (CVodeMem) cvode_mem; + + if (cvode_mem == NULL) + return; + + CVFreeVectors(cv_mem, qmax); + if (iter == NEWTON) + lfree(cv_mem); + CVMEM_MALLOC PHRQ_free(cv_mem); +} + + +/***************************************************************/ +/********** END Exported Functions Implementation **************/ +/***************************************************************/ + + +/*******************************************************************/ +/******** BEGIN Private Helper Functions Implementation ************/ +/*******************************************************************/ + +/****************** CVAllocVectors *********************************** + + This routine allocates the CVODE vectors ewt, acor, tempv, ftemp, and + zn[0], ..., zn[maxord]. The length of the vectors is the input + parameter neq and the maximum order (needed to allocate zn) is the + input parameter maxord. If all memory allocations are successful, + CVAllocVectors returns TRUE. Otherwise all allocated memory is freed + and CVAllocVectors returns FALSE. + This routine also sets the optional outputs lrw and liw, which are + (respectively) the lengths of the real and integer work spaces + allocated here. + +**********************************************************************/ + +static booleantype +CVAllocVectors(CVodeMem cv_mem, integertype neq, int maxord, M_Env machEnv) +{ + int i, j; + + /* Allocate ewt, acor, tempv, ftemp */ + + ewt = N_VNew(neq, machEnv); + if (ewt == NULL) + return (FALSE); + acor = N_VNew(neq, machEnv); + if (acor == NULL) + { + N_VFree(ewt); + return (FALSE); + } + tempv = N_VNew(neq, machEnv); + if (tempv == NULL) + { + N_VFree(ewt); + N_VFree(acor); + return (FALSE); + } + ftemp = N_VNew(neq, machEnv); + if (ftemp == NULL) + { + N_VFree(tempv); + N_VFree(ewt); + N_VFree(acor); + return (FALSE); + } + + /* Allocate zn[0] ... zn[maxord] */ + + for (j = 0; j <= maxord; j++) + { + zn[j] = N_VNew(neq, machEnv); + if (zn[j] == NULL) + { + N_VFree(ewt); + N_VFree(acor); + N_VFree(tempv); + N_VFree(ftemp); + for (i = 0; i < j; i++) + N_VFree(zn[i]); + return (FALSE); + } + } + + /* Set solver workspace lengths */ + + lrw = (maxord + 5) * neq; + liw = 0; + + return (TRUE); +} + +/***************** CVFreeVectors ********************************* + + This routine frees the CVODE vectors allocated in CVAllocVectors. + +******************************************************************/ + +static void +CVFreeVectors(CVodeMem cv_mem, int maxord) +{ + int j; + + N_VFree(ewt); + N_VFree(acor); + N_VFree(tempv); + N_VFree(ftemp); + for (j = 0; j <= maxord; j++) + N_VFree(zn[j]); +} + +/*********************** CVEwtSet ************************************** + + This routine is responsible for setting the error weight vector ewt, + according to tol_type, as follows: + + (1) ewt[i] = 1 / (*reltol * ABS(ycur[i]) + *abstol), i=0,...,neq-1 + if tol_type = SS + (2) ewt[i] = 1 / (*reltol * ABS(ycur[i]) + abstol[i]), i=0,...,neq-1 + if tol_type = SV + + CVEwtSet returns TRUE if ewt is successfully set as above to a + positive vector and FALSE otherwise. In the latter case, ewt is + considered undefined after the FALSE return from CVEwtSet. + + All the real work is done in the routines CVEwtSetSS, CVEwtSetSV. + +***********************************************************************/ + +static booleantype +CVEwtSet(CVodeMem cv_mem, N_Vector ycur) +{ + switch (itol) + { + case SS: + return (CVEwtSetSS(cv_mem, ycur)); + case SV: + return (CVEwtSetSV(cv_mem, ycur)); + } + return (-99); +} + +/*********************** CVEwtSetSS ********************************* + + This routine sets ewt as decribed above in the case tol_type = SS. + It tests for non-positive components before inverting. CVEwtSetSS + returns TRUE if ewt is successfully set to a positive vector + and FALSE otherwise. In the latter case, ewt is considered + undefined after the FALSE return from CVEwtSetSS. + +********************************************************************/ + +static booleantype +CVEwtSetSS(CVodeMem cv_mem, N_Vector ycur) +{ + realtype rtoli, atoli; + + rtoli = *reltol; + atoli = *((realtype *) abstol); + N_VAbs(ycur, tempv); + N_VScale(rtoli, tempv, tempv); + N_VAddConst(tempv, atoli, tempv); + if (N_VMin(tempv) <= ZERO) + return (FALSE); + N_VInv(tempv, ewt); + return (TRUE); +} + +/*********************** CVEwtSetSV ********************************* + + This routine sets ewt as decribed above in the case tol_type = SV. + It tests for non-positive components before inverting. CVEwtSetSV + returns TRUE if ewt is successfully set to a positive vector + and FALSE otherwise. In the latter case, ewt is considered + undefined after the FALSE return from CVEwtSetSV. + +********************************************************************/ + +static booleantype +CVEwtSetSV(CVodeMem cv_mem, N_Vector ycur) +{ + realtype rtoli; + rtoli = *reltol; + N_VAbs(ycur, tempv); + N_VLinearSum(rtoli, tempv, ONE, (N_Vector) abstol, tempv); + if (N_VMin(tempv) <= ZERO) + return (FALSE); + N_VInv(tempv, ewt); + return (TRUE); +} + +/******************* CVHin *************************************** + + This routine computes a tentative initial step size h0. + If tout is too close to tn (= t0), then CVHin returns FALSE and + h remains uninitialized. Otherwise, CVHin sets h to the chosen + value h0 and returns TRUE. + + The algorithm used seeks to find h0 as a solution of + (WRMS norm of (h0^2 ydd / 2)) = 1, + where ydd = estimated second derivative of y. + +*****************************************************************/ + +static booleantype +CVHin(CVodeMem cv_mem, realtype tout) +{ + int sign, count; + realtype tdiff, tdist, tround, hlb, hub; + realtype hg, hgs, hnew, hrat, h0, yddnrm; + + /* Test for tout too close to tn */ + + if ((tdiff = tout - tn) == ZERO) + return (FALSE); + + sign = (tdiff > ZERO) ? 1 : -1; + tdist = ABS(tdiff); + tround = uround * MAX(ABS(tn), ABS(tout)); + if (tdist < TWO * tround) + return (FALSE); + + /* Set lower and upper bounds on h0, and take geometric mean + Exit with this value if the bounds cross each other */ + + hlb = HLB_FACTOR * tround; + hub = CVUpperBoundH0(cv_mem, tdist); + hg = RSqrt(hlb * hub); + hnew = hg; + if (hub < hlb) + { + if (sign == -1) + hg = -hg; + h = hg; + return (TRUE); + } + + /* Loop up to MAX_ITERS times to find h0. + Stop if new and previous values differ by a factor < 2. + Stop if hnew/hg > 2 after one iteration, as this probably means + that the ydd value is bad because of cancellation error. */ + + count = 0; + loop + { + count++; + if (count >= MAX_ITERS) + break; + hgs = hg * sign; + yddnrm = CVYddNorm(cv_mem, hgs); + if (CVMEM cvode_error == TRUE) + { + hg /= 2.; +#ifdef DEBUG_CVODE + CVMEM warning_msg("halving step in CVHin\n"); +#endif + continue; + } + + hnew = + (yddnrm * hub * hub > + TWO) ? RSqrt(TWO / yddnrm) : RSqrt(hg * hub); + + hrat = hnew / hg; + if ((hrat > HALF) && (hrat < TWO)) + break; + if ((count >= 2) && (hrat > TWO)) + { + hnew = hg; + break; + } + hg = hnew; + } + + /* Apply bounds, bias factor, and attach sign */ + + h0 = H_BIAS * hnew; + if (h0 < hlb) + h0 = hlb; + if (h0 > hub) + h0 = hub; + if (sign == -1) + h0 = -h0; + h = h0; + return (TRUE); +} + +/******************** CVUpperBoundH0 ****************************** + + This routine sets an upper bound on abs(h0) based on + tdist = abs(tout - t0) and the values of y[i]/y'[i]. + +******************************************************************/ + +static realtype +CVUpperBoundH0(CVodeMem cv_mem, realtype tdist) +{ + realtype atoli, hub_inv, hub; + booleantype vectorAtol; + N_Vector temp1, temp2; + + atoli = 0; + vectorAtol = (itol == SV); + if (!vectorAtol) + atoli = *((realtype *) abstol); + temp1 = tempv; + temp2 = acor; + N_VAbs(zn[0], temp1); + N_VAbs(zn[1], temp2); + if (vectorAtol) + { + N_VLinearSum(HUB_FACTOR, temp1, ONE, (N_Vector) abstol, temp1); + } + else + { + N_VScale(HUB_FACTOR, temp1, temp1); + N_VAddConst(temp1, atoli, temp1); + } + N_VDiv(temp2, temp1, temp1); + hub_inv = N_VMaxNorm(temp1); + hub = HUB_FACTOR * tdist; + if (hub * hub_inv > ONE) + hub = ONE / hub_inv; + return (hub); +} + +/****************** CVYddNorm ************************************* + + This routine computes an estimate of the second derivative of y + using a difference quotient, and returns its WRMS norm. + +******************************************************************/ + +static realtype +CVYddNorm(CVodeMem cv_mem, realtype hg) +{ + realtype yddnrm; + + N_VLinearSum(hg, zn[1], ONE, zn[0], y); + f(N, tn + hg, y, tempv, f_data); +#ifdef DEBUG_CVODE + if (cvode_error == TRUE) + { + CVMEM warning_msg("CVYddNorm error\n"); + } +#endif + nfe++; + N_VLinearSum(ONE, tempv, -ONE, zn[1], tempv); + N_VScale(ONE / hg, tempv, tempv); + + yddnrm = N_VWrmsNorm(tempv, ewt); + return (yddnrm); +} + +/********************* CVStep ************************************** + + This routine performs one internal cvode step, from tn to tn + h. + It calls other routines to do all the work. + + The main operations done here are as follows: + * preliminary adjustments if a new step size was chosen; + * prediction of the Nordsieck history array zn at tn + h; + * setting of multistep method coefficients and test quantities; + * solution of the nonlinear system; + * testing the local error; + * updating zn and other state data if successful; + * resetting stepsize and order for the next step. + * if SLDET is on, check for stability, reduce order if necessary. + On a failure in the nonlinear system solution or error test, the + step may be reattempted, depending on the nature of the failure. + +********************************************************************/ + +static int +CVStep(CVodeMem cv_mem) +{ + realtype saved_t, dsm; + int ncf, nef, nflag; + booleantype passed; + + int kflag; + + saved_t = tn; + ncf = nef = 0; + nflag = FIRST_CALL; + + + if ((nst > 0) && (hprime != h)) + CVAdjustParams(cv_mem); + + /* Looping point for attempts to take a step */ + loop + { + bool predict_fail = false; + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + predict_fail = true; +#ifdef DEBUG_CVODE + CVMEM warning_msg("Before predict, y Fail, time %e\n", tn); +#endif + } + else + { + CVMEM cvode_prev_good_time = CVMEM cvode_last_good_time; + N_VScale(1.0, CVMEM cvode_last_good_y, CVMEM cvode_prev_good_y); + CVMEM cvode_last_good_time = tn; + N_VScale(1.0, y, CVMEM cvode_last_good_y); +#ifdef DEBUG_CVODE + CVMEM warning_msg("Before predict, y OK, time %e\n", tn); +#endif + } +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before predict, zn Fail\n"); + } + else + { + CVMEM warning_msg("Before predict, zn OK\n"); + } + saved_t = tn; +#endif + CVPredict(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After predict, y Fail\n"); + } + else + { + CVMEM warning_msg("After predict, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After predict, zn Fail\n"); + + } + else + { + CVMEM warning_msg("After predict, zn OK\n"); + } +#endif + CVSet(cv_mem); + nflag = CVnls(cv_mem, nflag); + if (CVMEM cvode_error == TRUE || predict_fail) + { + nflag = -1; + } +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVnls, y Fail\n"); + } + else + { + CVMEM warning_msg("After CVnls, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVnls, zn Fail\n"); + } + else + { + CVMEM warning_msg("After CVnls, zn OK\n"); + } +#endif + //fprintf(stderr, "\nTime %e,\th %e\n", tn, h); + kflag = CVHandleNFlag(cv_mem, &nflag, saved_t, &ncf); + if (kflag == PREDICT_AGAIN) + continue; + if (kflag != DO_ERROR_TEST) + return (kflag); + /* Return if nonlinear solve failed and recovery not possible. */ +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before error test, y Fail\n"); + } + else + { + CVMEM warning_msg("Before error test, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before error test, zn Fail\n"); + } + else + { + CVMEM warning_msg("Before error test, zn OK\n"); + } +#endif + passed = CVDoErrorTest(cv_mem, &nflag, &kflag, saved_t, &nef, &dsm); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg(CVMEM sformatf("After error test, y Fail, passed %d\n", + passed)); + } + else + { + CVMEM warning_msg(CVMEM sformatf("After error test, y OK, passed %d\n", + passed)); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After error test, zn Fail\n"); + } + else + { + CVMEM warning_msg("After error test, zn OK\n"); + } +#endif + /* Return if error test failed and recovery not possible. */ + if ((!passed) && (kflag == REP_ERR_FAIL)) + return (kflag); + if (passed) + break; + /* Retry step if error test failed, nflag == PREV_ERR_FAIL */ + } +#ifdef DEBUG_CVODE + CVMEM warning_msg("Finished step in CVStep\n"); +#endif + /* Nonlinear system solve and error test were both successful. + Update data, and consider change of step and/or order. */ + + + CVCompleteStep(cv_mem); +#ifdef DEBUG_CVODE + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("After complete step, y Fail\n"); + } + else + { + CVMEM warning_msg("After complete step, y OK\n"); + } + CVMEM cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("After complete step, zn Fail\n"); + } + else + { + CVMEM warning_msg("After complete step, zn OK\n"); + } +#endif + CVPrepareNextStep(cv_mem, dsm); + + /* If Stablilty Limit Detection is turned on, call stability limit + detection routine for possible order reduction. */ + + if (sldeton) + CVBDFStab(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After cvbfdstab, y Fail\n"); + } + else + { + CVMEM warning_msg("After cvbfdstab, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After cvbfdstab, zn Fail\n"); + } + else + { + CVMEM warning_msg("After cvbfdstab, zn OK\n"); + } +#endif + etamax = (nst <= SMALL_NST) ? ETAMX2 : ETAMX3; + + /* Finally, we rescale the acor array to be the + estimated local error vector. */ + + N_VScale(ONE / tq[2], acor, acor); + return (SUCCESS_STEP); + +} + + +/********************* CVAdjustParams ******************************** + + This routine is called when a change in step size was decided upon, + and it handles the required adjustments to the history array zn. + If there is to be a change in order, we call CVAdjustOrder and reset + q, L = q+1, and qwait. Then in any case, we call CVRescale, which + resets h and rescales the Nordsieck array. + +**********************************************************************/ + +static void +CVAdjustParams(CVodeMem cv_mem) +{ + if (qprime != q) + { + CVAdjustOrder(cv_mem, qprime - q); + q = qprime; + L = q + 1; + qwait = L; + } + CVRescale(cv_mem); +} + +/********************* CVAdjustOrder ***************************** + + This routine is a high level routine which handles an order + change by an amount deltaq (= +1 or -1). If a decrease in order + is requested and q==2, then the routine returns immediately. + Otherwise CVAdjustAdams or CVAdjustBDF is called to handle the + order change (depending on the value of lmm). + +******************************************************************/ + +static void +CVAdjustOrder(CVodeMem cv_mem, int deltaq) +{ + if ((q == 2) && (deltaq != 1)) + return; + + switch (lmm) + { + case ADAMS: + CVAdjustAdams(cv_mem, deltaq); + break; + case BDF: + CVAdjustBDF(cv_mem, deltaq); + break; + } +} + +/*************** CVAdjustAdams *********************************** + + This routine adjusts the history array on a change of order q by + deltaq, in the case that lmm == ADAMS. + +*****************************************************************/ + +static void +CVAdjustAdams(CVodeMem cv_mem, int deltaq) +{ + int i, j; + realtype xi, hsum; + + /* On an order increase, set new column of zn to zero and return */ + + if (deltaq == 1) + { + N_VConst(ZERO, zn[L]); + return; + } + + /* On an order decrease, each zn[j] is adjusted by a multiple + of zn[q]. The coefficients in the adjustment are the + coefficients of the polynomial x*x*(x+xi_1)*...*(x+xi_j), + integrated, where xi_j = [t_n - t_(n-j)]/h. */ + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[1] = ONE; + hsum = ZERO; + for (j = 1; j <= q - 2; j++) + { + hsum += tau[j]; + xi = hsum / hscale; + for (i = j + 1; i >= 1; i--) + l[i] = l[i] * xi + l[i - 1]; + } + + for (j = 1; j <= q - 2; j++) + l[j + 1] = q * (l[j] / (j + 1)); + + for (j = 2; j < q; j++) + N_VLinearSum(-l[j], zn[q], ONE, zn[j], zn[j]); +} + +/********************** CVAdjustBDF ******************************* + + This is a high level routine which handles adjustments to the + history array on a change of order by deltaq in the case that + lmm == BDF. CVAdjustBDF calls CVIncreaseBDF if deltaq = +1 and + CVDecreaseBDF if deltaq = -1 to do the actual work. + +******************************************************************/ + +static void +CVAdjustBDF(CVodeMem cv_mem, int deltaq) +{ + switch (deltaq) + { + case 1: + CVIncreaseBDF(cv_mem); + return; + case -1: + CVDecreaseBDF(cv_mem); + return; + } +} + +/******************** CVIncreaseBDF ********************************** + + This routine adjusts the history array on an increase in the + order q in the case that lmm == BDF. + A new column zn[q+1] is set equal to a multiple of the saved + vector (= acor) in zn[qmax]. Then each zn[j] is adjusted by + a multiple of zn[q+1]. The coefficients in the adjustment are the + coefficients of the polynomial x*x*(x+xi_1)*...*(x+xi_(q-1)), + where xi_j = [t_n - t_(n-j)]/h. + +*********************************************************************/ + +static void +CVIncreaseBDF(CVodeMem cv_mem) +{ + realtype alpha0, alpha1, prod, xi, xiold, hsum, A1; + int i, j; + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[2] = alpha1 = prod = xiold = ONE; + alpha0 = -ONE; + hsum = hscale; + if (q > 1) + { + for (j = 1; j < q; j++) + { + hsum += tau[j + 1]; + xi = hsum / hscale; + prod *= xi; + alpha0 -= ONE / (j + 1); + alpha1 += ONE / xi; + for (i = j + 2; i >= 2; i--) + l[i] = l[i] * xiold + l[i - 1]; + xiold = xi; + } + } + A1 = (-alpha0 - alpha1) / prod; + if (L >= 0 && L <= qmax) + { + N_VScale(A1, zn[qmax], zn[L]); + for (j = 2; j <= q; j++) + { + N_VLinearSum(l[j], zn[L], ONE, zn[j], zn[j]); + } + } +} + +/********************* CVDecreaseBDF ****************************** + + This routine adjusts the history array on a decrease in the + order q in the case that lmm == BDF. + Each zn[j] is adjusted by a multiple of zn[q]. The coefficients + in the adjustment are the coefficients of the polynomial + x*x*(x+xi_1)*...*(x+xi_(q-2)), where xi_j = [t_n - t_(n-j)]/h. + +******************************************************************/ + +static void +CVDecreaseBDF(CVodeMem cv_mem) +{ + realtype hsum, xi; + int i, j; + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[2] = ONE; + hsum = ZERO; + for (j = 1; j <= q - 2; j++) + { + hsum += tau[j]; + xi = hsum / hscale; + for (i = j + 2; i >= 2; i--) + l[i] = l[i] * xi + l[i - 1]; + } + + for (j = 2; j < q; j++) + N_VLinearSum(-l[j], zn[q], ONE, zn[j], zn[j]); +} + +/**************** CVRescale *********************************** + + This routine rescales the Nordsieck array by multiplying the + jth column zn[j] by eta^j, j = 1, ..., q. Then the value of + h is rescaled by eta, and hscale is reset to h. + +***************************************************************/ + +static void +CVRescale(CVodeMem cv_mem) +{ + int j; + realtype factor; + + factor = eta; + for (j = 1; j <= q; j++) + { + N_VScale(factor, zn[j], zn[j]); + factor *= eta; + } + h = hscale * eta; + hscale = h; + nscon = 0; +} + +/********************* CVPredict ************************************* + + This routine advances tn by the tentative step size h, and computes + the predicted array z_n(0), which is overwritten on zn. The + prediction of zn is done by repeated additions. + +*********************************************************************/ + +static void +CVPredict(CVodeMem cv_mem) +{ + int j, k; + + tn += h; + for (k = 1; k <= q; k++) + for (j = q; j >= k; j--) + N_VLinearSum(ONE, zn[j - 1], ONE, zn[j], zn[j - 1]); +} + +/************************** CVSet ********************************* + + This routine is a high level routine which calls CVSetAdams or + CVSetBDF to set the polynomial l, the test quantity array tq, + and the related variables rl1, gamma, and gamrat. + +******************************************************************/ + +static void +CVSet(CVodeMem cv_mem) +{ + switch (lmm) + { + case ADAMS: + CVSetAdams(cv_mem); + break; + case BDF: + CVSetBDF(cv_mem); + break; + } + rl1 = ONE / l[1]; + gamma = h * rl1; + if (nst == 0) + gammap = gamma; + gamrat = (nst > 0) ? gamma / gammap : ONE; /* protect x / x != 1.0 */ +} + +/******************** CVSetAdams ********************************* + + This routine handles the computation of l and tq for the + case lmm == ADAMS. + + The components of the array l are the coefficients of a + polynomial Lambda(x) = l_0 + l_1 x + ... + l_q x^q, given by + q-1 + (d/dx) Lambda(x) = c * PRODUCT (1 + x / xi_i) , where + i=1 + Lambda(-1) = 0, Lambda(0) = 1, and c is a normalization factor. + Here xi_i = [t_n - t_(n-i)] / h. + + The array tq is set to test quantities used in the convergence + test, the error test, and the selection of h at a new order. + +*****************************************************************/ + +static void +CVSetAdams(CVodeMem cv_mem) +{ + realtype m[L_MAX], M[3], hsum; + + if (q == 1) + { + l[0] = l[1] = tq[1] = tq[5] = ONE; + tq[2] = TWO; + tq[3] = TWELVE; + tq[4] = CORTES * tq[2]; /* = 0.1 * tq[2] */ + return; + } + + hsum = CVAdamsStart(cv_mem, m); + + M[0] = CVAltSum(q - 1, m, 1); + M[1] = CVAltSum(q - 1, m, 2); + + CVAdamsFinish(cv_mem, m, M, hsum); +} + +/****************** CVAdamsStart ******************************** + + This routine generates in m[] the coefficients of the product + polynomial needed for the Adams l and tq coefficients for q > 1. + +******************************************************************/ + +static realtype +CVAdamsStart(CVodeMem cv_mem, realtype m[]) +{ + realtype hsum, xi_inv, sum; + int i, j; + + hsum = h; + m[0] = ONE; + for (i = 1; i <= q; i++) + m[i] = ZERO; + for (j = 1; j < q; j++) + { + if ((j == q - 1) && (qwait == 1)) + { + sum = CVAltSum(q - 2, m, 2); + tq[1] = m[q - 2] / (q * sum); + } + xi_inv = h / hsum; + for (i = j; i >= 1; i--) + m[i] += m[i - 1] * xi_inv; + hsum += tau[j]; + /* The m[i] are coefficients of product(1 to j) (1 + x/xi_i) */ + } + return (hsum); +} + +/****************** CVAdamsFinish ******************************* + + This routine completes the calculation of the Adams l and tq. + +******************************************************************/ + +static void +CVAdamsFinish(CVodeMem cv_mem, realtype m[], realtype M[], realtype hsum) +{ + int i; + realtype M0_inv, xi, xi_inv; + + M0_inv = ONE / M[0]; + + l[0] = ONE; + for (i = 1; i <= q; i++) + l[i] = M0_inv * (m[i - 1] / i); + xi = hsum / h; + xi_inv = ONE / xi; + + tq[2] = xi * M[0] / M[1]; + tq[5] = xi / l[q]; + + if (qwait == 1) + { + for (i = q; i >= 1; i--) + m[i] += m[i - 1] * xi_inv; + M[2] = CVAltSum(q, m, 2); + tq[3] = L * M[0] / M[2]; + } + + tq[4] = CORTES * tq[2]; +} + +/****************** CVAltSum ************************************** + + CVAltSum returns the value of the alternating sum + sum (i= 0 ... iend) [ (-1)^i * (a[i] / (i + k)) ]. + If iend < 0 then CVAltSum returns 0. + This operation is needed to compute the integral, from -1 to 0, + of a polynomial x^(k-1) M(x) given the coefficients of M(x). + +******************************************************************/ + +static realtype +CVAltSum(int iend, realtype a[], int k) +{ + int i, sign; + realtype sum; + + if (iend < 0) + return (ZERO); + + sum = ZERO; + sign = 1; + for (i = 0; i <= iend; i++) + { + sum += sign * (a[i] / (i + k)); + sign = -sign; + } + return (sum); +} + +/***************** CVSetBDF ************************************** + + This routine computes the coefficients l and tq in the case + lmm == BDF. CVSetBDF calls CVSetTqBDF to set the test + quantity array tq. + + The components of the array l are the coefficients of a + polynomial Lambda(x) = l_0 + l_1 x + ... + l_q x^q, given by + q-1 + Lambda(x) = (1 + x / xi*_q) * PRODUCT (1 + x / xi_i) , where + i=1 + xi_i = [t_n - t_(n-i)] / h. + + The array tq is set to test quantities used in the convergence + test, the error test, and the selection of h at a new order. + + +*****************************************************************/ + +static void +CVSetBDF(CVodeMem cv_mem) +{ + realtype alpha0, alpha0_hat, xi_inv, xistar_inv, hsum; + int i, j; + + l[0] = l[1] = xi_inv = xistar_inv = ONE; + for (i = 2; i <= q; i++) + l[i] = ZERO; + alpha0 = alpha0_hat = -ONE; + hsum = h; + if (q > 1) + { + for (j = 2; j < q; j++) + { + hsum += tau[j - 1]; + xi_inv = h / hsum; + alpha0 -= ONE / j; + for (i = j; i >= 1; i--) + l[i] += l[i - 1] * xi_inv; + /* The l[i] are coefficients of product(1 to j) (1 + x/xi_i) */ + } + + /* j = q */ + alpha0 -= ONE / q; + xistar_inv = -l[1] - alpha0; + hsum += tau[q - 1]; + xi_inv = h / hsum; + alpha0_hat = -l[1] - xi_inv; + for (i = q; i >= 1; i--) + l[i] += l[i - 1] * xistar_inv; + } + + CVSetTqBDF(cv_mem, hsum, alpha0, alpha0_hat, xi_inv, xistar_inv); +} + +/****************** CVSetTqBDF ************************************ + + This routine sets the test quantity array tq when lmm == BDF. + +******************************************************************/ + +static void +CVSetTqBDF(CVodeMem cv_mem, realtype hsum, realtype alpha0, + realtype alpha0_hat, realtype xi_inv, realtype xistar_inv) +{ + realtype A1, A2, A3, A4, A5, A6; + realtype C, CPrime, CPrimePrime; + + A1 = ONE - alpha0_hat + alpha0; + A2 = ONE + q * A1; + tq[2] = ABS(alpha0 * (A2 / A1)); + tq[5] = ABS((A2) / (l[q] * xi_inv / xistar_inv)); + if (qwait == 1) + { + C = xistar_inv / l[q]; + A3 = alpha0 + ONE / q; + A4 = alpha0_hat + xi_inv; + CPrime = A3 / (ONE - A4 + A3); + tq[1] = ABS(CPrime / C); + hsum += tau[q]; + xi_inv = h / hsum; + A5 = alpha0 - (ONE / (q + 1)); + A6 = alpha0_hat - xi_inv; + CPrimePrime = A2 / (ONE - A6 + A5); + tq[3] = ABS(CPrimePrime * xi_inv * (q + 2) * A5); + } + tq[4] = CORTES * tq[2]; +} + +/****************** CVnls ***************************************** + + This routine attempts to solve the nonlinear system associated + with a single implicit step of the linear multistep method. + Depending on iter, it calls CVnlsFunctional or CVnlsNewton + to do the work. + +******************************************************************/ + +static int +CVnls(CVodeMem cv_mem, int nflag) +{ + switch (iter) + { + case FUNCTIONAL: + return (CVnlsFunctional(cv_mem)); + case NEWTON: + return (CVnlsNewton(cv_mem, nflag)); + } + return (-99); +} + +/***************** CVnlsFunctional ******************************** + + This routine attempts to solve the nonlinear system using + functional iteration (no matrices involved). + +******************************************************************/ + +static int +CVnlsFunctional(CVodeMem cv_mem) +{ + int m; + realtype del, delp, dcon; + + /* Initialize counter and evaluate f at predicted y */ + + delp = 0; + crate = ONE; + m = 0; + f(N, tn, zn[0], tempv, f_data); + nfe++; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, Fail at beginning\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, OK at beginning\n"); +#endif + } + N_VConst(ZERO, acor); + + /* Loop until convergence; accumulate corrections in acor */ + + loop + { + /* Correct y directly from the last f value */ + N_VLinearSum(h, tempv, -ONE, zn[1], tempv); + N_VScale(rl1, tempv, tempv); + N_VLinearSum(ONE, zn[0], ONE, tempv, y); + /* Get WRMS norm of current correction to use in convergence test */ + N_VLinearSum(ONE, tempv, -ONE, acor, acor); + del = N_VWrmsNorm(acor, ewt); + N_VScale(ONE, tempv, acor); + + /* Test for convergence. If m > 0, an estimate of the convergence + rate constant is stored in crate, and used in the test. */ + if (m > 0) + crate = MAX(CRDOWN * crate, del / delp); + dcon = del * MIN(ONE, crate) / tq[4]; + if (dcon <= ONE) + { + acnrm = (m == 0) ? del : N_VWrmsNorm(acor, ewt); + return (SOLVED); /* Convergence achieved */ + } + + /* Stop at maxcor iterations or if iter. seems to be diverging */ + m++; + if ((m == maxcor) || ((m >= 2) && (del > RDIV * delp))) + return (CONV_FAIL); + /* Save norm of correction, evaluate f, and loop again */ + delp = del; + f(N, tn, y, tempv, f_data); + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, Fail at end\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, OK at end\n"); +#endif + } + nfe++; + } +} + +/*********************** CVnlsNewton ********************************** + + This routine handles the Newton iteration. It calls lsetup if + indicated, calls CVNewtonIteration to perform the iteration, and + retries a failed attempt at Newton iteration if that is indicated. + See return values at top of this file. + +**********************************************************************/ + +static int +CVnlsNewton(CVodeMem cv_mem, int nflag) +{ + N_Vector vtemp1, vtemp2, vtemp3; + int convfail, ier; + booleantype callSetup; + + vtemp1 = acor; /* rename acor as vtemp1 for readability */ + vtemp2 = y; /* rename y as vtemp2 for readability */ + vtemp3 = tempv; /* rename tempv as vtemp3 for readability */ + + /* Set flag convfail, input to lsetup for its evaluation decision */ + convfail = ((nflag == FIRST_CALL) || (nflag == PREV_ERR_FAIL)) ? + NO_FAILURES : FAIL_OTHER; + + /* Decide whether or not to call setup routine (if one exists) */ + if (setupNonNull) + { + callSetup = (nflag == PREV_CONV_FAIL) || (nflag == PREV_ERR_FAIL) || + (nst == 0) || (nst >= nstlp + MSBP) + || (ABS(gamrat - ONE) > DGMAX); + } + else + { + crate = ONE; + callSetup = FALSE; + } + + /* Looping point for the solution of the nonlinear system. + Evaluate f at the predicted y, call lsetup if indicated, and + call CVNewtonIteration for the Newton iteration itself. */ + + loop + { + f(N, tn, zn[0], ftemp, f_data); + + nfe++; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf("CVnlsNewton, start of loop, time %e\n", + tn)); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf( + "CVnlsNewton, OK, start of loop, time %e\n", tn)); +#endif + } + + if (callSetup) + { + ier = lsetup(cv_mem, convfail, zn[0], ftemp, &jcur, + vtemp1, vtemp2, vtemp3); + nsetups++; + callSetup = FALSE; + gamrat = crate = ONE; + gammap = gamma; + nstlp = nst; + /* Return if lsetup failed */ + if (ier < 0) + return (SETUP_FAIL_UNREC); + if (ier > 0) + return (CONV_FAIL); + } + + /* Set acor to zero and load prediction into y vector */ + N_VConst(ZERO, acor); + N_VScale(ONE, zn[0], y); + + /* Do the Newton iteration */ + ier = CVNewtonIteration(cv_mem); + if (CVMEM cvode_error == TRUE) + { + return (CONV_FAIL); + } + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf( + "After CVNewtonIteration, Fail, ier %d\n", ier)); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf("After CVNewtonIteration, OK, ier %d\n", + ier)); +#endif + } + /* If there is a convergence failure and the Jacobian-related + data appears not to be current, loop again with a call to lsetup + in which convfail=FAIL_BAD_J. Otherwise return. */ + if (ier != TRY_AGAIN_CVODE) + return (ier); + callSetup = TRUE; + convfail = FAIL_BAD_J; + } +} + +/********************** CVNewtonIteration **************************** + + This routine performs the Newton iteration. If the iteration succeeds, + it returns the value SOLVED. If not, it may signal the CVnlsNewton + routine to call lsetup again and reattempt the iteration, by + returning the value TRY_AGAIN_CVODE. (In this case, CVnlsNewton must set + convfail to FAIL_BAD_J before calling setup again). + Otherwise, this routine returns one of the appropriate values + SOLVE_FAIL_UNREC or CONV_FAIL back to CVnlsNewton. + +*********************************************************************/ + +static int +CVNewtonIteration(CVodeMem cv_mem) +{ + int m, ret; + realtype del, delp, dcon; + N_Vector b; + + + mnewt = m = 0; + delp = 0; + + /* Looping point for Newton iteration */ + loop + { + + /* Evaluate the residual of the nonlinear system */ + N_VLinearSum(rl1, zn[1], ONE, acor, tempv); + N_VLinearSum(gamma, ftemp, -ONE, tempv, tempv); + + /* Call the lsolve function */ + b = tempv; + ret = lsolve(cv_mem, b, y, ftemp); + nni++; + + if (ret < 0) + return (SOLVE_FAIL_UNREC); + + /* If lsolve had a recoverable failure and Jacobian data is + not current, signal to try the solution again */ + if (ret > 0) + { + if ((!jcur) && (setupNonNull)) + return (TRY_AGAIN_CVODE); + return (CONV_FAIL); + } + /* Get WRMS norm of correction; add correction to acor and y */ + del = N_VWrmsNorm(b, ewt); + N_VLinearSum(ONE, acor, ONE, b, acor); + N_VLinearSum(ONE, zn[0], ONE, acor, y); + + /* Test for convergence. If m > 0, an estimate of the convergence + rate constant is stored in crate, and used in the test. */ + if (m > 0) + { + crate = MAX(CRDOWN * crate, del / delp); + } + dcon = del * MIN(ONE, crate) / tq[4]; + +#ifdef DEBUG_CVODE + /* added before SOLVED */ + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("CVnlsNewton, Fail at SOLVED\n"); + return (CONV_FAIL); + } + else + { + CVMEM warning_msg("CVnlsNewton, OK at SOLVED\n"); + } +#endif + if (dcon <= ONE) + { + acnrm = (m == 0) ? del : N_VWrmsNorm(acor, ewt); + jcur = FALSE; + return (SOLVED); /* Nonlinear system was solved successfully */ + } + + mnewt = ++m; + + /* Stop at maxcor iterations or if iter. seems to be diverging. + If still not converged and Jacobian data is not current, + signal to try the solution again */ + if ((m == maxcor) || ((m >= 2) && (del > RDIV * delp))) + { + if ((!jcur) && (setupNonNull)) + return (TRY_AGAIN_CVODE); + return (CONV_FAIL); + } + + /* Save norm of correction, evaluate f, and loop again */ + delp = del; + f(N, tn, y, ftemp, f_data); + + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsNewton, Fail at end\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsNewton, OK at end\n"); +#endif + } + nfe++; + } +} + +/********************** CVHandleNFlag ******************************* + + This routine takes action on the return value nflag = *nflagPtr + returned by CVnls, as follows: + + If CVnls succeeded in solving the nonlinear system, then + CVHandleNFlag returns the constant DO_ERROR_TEST, which tells CVStep + to perform the error test. + + If the nonlinear system was not solved successfully, then ncfn and + ncf = *ncfPtr are incremented and Nordsieck array zn is restored. + + If the solution of the nonlinear system failed due to an + unrecoverable failure by setup, we return the value SETUP_FAILED. + + If it failed due to an unrecoverable failure in solve, then we return + the value SOLVE_FAILED. + + Otherwise, a recoverable failure occurred when solving the + nonlinear system (CVnls returned nflag == CONV_FAIL). + In this case, we return the value REP_CONV_FAIL if ncf is now + equal to MXNCF or |h| = hmin. + If not, we set *nflagPtr = PREV_CONV_FAIL and return the value + PREDICT_AGAIN, telling CVStep to reattempt the step. + +*********************************************************************/ + +static int +CVHandleNFlag(CVodeMem cv_mem, int *nflagPtr, realtype saved_t, int *ncfPtr) +{ + int nflag; + + nflag = *nflagPtr; + + if (nflag == SOLVED) + return (DO_ERROR_TEST); + + /* The nonlinear soln. failed; increment ncfn and restore zn */ + ncfn++; + CVRestore(cv_mem, saved_t); + + /* Return if lsetup or lsolve failed unrecoverably */ + if (nflag == SETUP_FAIL_UNREC) + return (SETUP_FAILED); + if (nflag == SOLVE_FAIL_UNREC) + return (SOLVE_FAILED); + + /* At this point, nflag == CONV_FAIL; increment ncf */ + + (*ncfPtr)++; + etamax = ONE; + /* If we had MXNCF failures or |h| = hmin, return REP_CONV_FAIL */ + if ((ABS(h) <= hmin * ONEPSM) || (*ncfPtr == MXNCF)) + return (REP_CONV_FAIL); + + /* Reduce step size; return to reattempt the step */ + eta = MAX(ETACF, hmin / ABS(h)); + *nflagPtr = PREV_CONV_FAIL; + CVRescale(cv_mem); + return (PREDICT_AGAIN); +} + +/********************** CVRestore ************************************ + + This routine restores the value of tn to saved_t and undoes the + prediction. After execution of CVRestore, the Nordsieck array zn has + the same values as before the call to CVPredict. + +********************************************************************/ + +static void +CVRestore(CVodeMem cv_mem, realtype saved_t) +{ + int j, k; + + tn = saved_t; + for (k = 1; k <= q; k++) + for (j = q; j >= k; j--) + N_VLinearSum(ONE, zn[j - 1], -ONE, zn[j], zn[j - 1]); +} + +/******************* CVDoErrorTest ******************************** + + This routine performs the local error test. + The weighted local error norm dsm is loaded into *dsmPtr, and + the test dsm ?<= 1 is made. + + If the test passes, CVDoErrorTest returns TRUE. + + If the test fails, we undo the step just taken (call CVRestore), + set *nflagPtr to PREV_ERR_FAIL, and return FALSE. + + If MXNEF error test failures have occurred or if ABS(h) = hmin, + we set *kflagPtr = REP_ERR_FAIL. (Otherwise *kflagPtr has the + value last returned by CVHandleNflag.) + + If more than MXNEF1 error test failures have occurred, an order + reduction is forced. + +******************************************************************/ + +static booleantype +CVDoErrorTest(CVodeMem cv_mem, int *nflagPtr, int *kflagPtr, + realtype saved_t, int *nefPtr, realtype * dsmPtr) +{ + realtype dsm; + + dsm = acnrm / tq[2]; + + /* If est. local error norm dsm passes test, return TRUE */ + *dsmPtr = dsm; + if (dsm <= ONE) + return (TRUE); + + /* Test failed; increment counters, set nflag, and restore zn array */ + (*nefPtr)++; + netf++; + *nflagPtr = PREV_ERR_FAIL; + CVRestore(cv_mem, saved_t); + + /* At MXNEF failures or |h| = hmin, return with kflag = REP_ERR_FAIL */ + if ((ABS(h) <= hmin * ONEPSM) || (*nefPtr == MXNEF)) + { + *kflagPtr = REP_ERR_FAIL; + return (FALSE); + } + + /* Set etamax = 1 to prevent step size increase at end of this step */ + etamax = ONE; + + /* Set h ratio eta from dsm, rescale, and return for retry of step */ + if (*nefPtr <= MXNEF1) + { + eta = ONE / (RPowerR(BIAS2 * dsm, ONE / L) + ADDON); + eta = MAX(ETAMIN, MAX(eta, hmin / ABS(h))); + if (*nefPtr >= SMALL_NEF) + eta = MIN(eta, ETAMXF); + CVRescale(cv_mem); + return (FALSE); + } + + /* After MXNEF1 failures, force an order reduction and retry step */ + if (q > 1) + { + eta = MAX(ETAMIN, hmin / ABS(h)); + CVAdjustOrder(cv_mem, -1); + L = q; + q--; + qwait = L; + CVRescale(cv_mem); + return (FALSE); + } + + /* If already at order 1, restart: reload zn from scratch */ + eta = MAX(ETAMIN, hmin / ABS(h)); + h *= eta; + hscale = h; + qwait = LONG_WAIT; + nscon = 0; + f(N, tn, zn[0], tempv, f_data); + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("CVDoErrorTest"); + /*exit(8); */ + CVMEM error_msg("CVDoErrorTest", 1 /* STOP */ ); +#if !defined(R_SO) + exit(4); +#endif + } + nfe++; + N_VScale(h, tempv, zn[1]); + return (FALSE); +} + +/*************** CVCompleteStep ********************************** + + This routine performs various update operations when the solution + to the nonlinear system has passed the local error test. + We increment the step counter nst, record the values hu and qu, + update the tau array, and apply the corrections to the zn array. + The tau[i] are the last q values of h, with tau[1] the most recent. + The counter qwait is decremented, and if qwait == 1 (and q < qmax) + we save acor and tq[5] for a possible order increase. + +******************************************************************/ + +static void +CVCompleteStep(CVodeMem cv_mem) +{ + int i, j; + + nst++; + nscon++; + hu = h; + qu = q; + + for (i = q; i >= 2; i--) + tau[i] = tau[i - 1]; + if ((q == 1) && (nst > 1)) + tau[2] = tau[1]; + tau[1] = h; + + for (j = 0; j <= q; j++) + N_VLinearSum(l[j], acor, ONE, zn[j], zn[j]); + qwait--; + if ((qwait == 1) && (q != qmax)) + { + N_VScale(ONE, acor, zn[qmax]); + saved_tq5 = tq[5]; + } +} + +/************* CVPrepareNextStep ********************************** + + This routine handles the setting of stepsize and order for the + next step -- hprime and qprime. Along with hprime, it sets the + ratio eta = hprime/h. It also updates other state variables + related to a change of step size or order. + +******************************************************************/ + +static void +CVPrepareNextStep(CVodeMem cv_mem, realtype dsm) +{ + /* If etamax = 1, defer step size or order changes */ + if (etamax == ONE) + { + qwait = MAX(qwait, 2); + qprime = q; + hprime = h; + eta = ONE; + return; + } + + /* etaq is the ratio of new to old h at the current order */ + etaq = ONE / (RPowerR(BIAS2 * dsm, ONE / L) + ADDON); + + /* If no order change, adjust eta and acor in CVSetEta and return */ + if (qwait != 0) + { + eta = etaq; + qprime = q; + CVSetEta(cv_mem); + return; + } + + /* If qwait = 0, consider an order change. etaqm1 and etaqp1 are + the ratios of new to old h at orders q-1 and q+1, respectively. + CVChooseEta selects the largest; CVSetEta adjusts eta and acor */ + qwait = 2; + etaqm1 = CVComputeEtaqm1(cv_mem); + etaqp1 = CVComputeEtaqp1(cv_mem); + CVChooseEta(cv_mem); + CVSetEta(cv_mem); +} + +/***************** CVSetEta *************************************** + + This routine adjusts the value of eta according to the various + heuristic limits and the optional input hmax. It also resets + etamax to be the estimated local error vector. + +*******************************************************************/ + +static void +CVSetEta(CVodeMem cv_mem) +{ + + /* If eta below the threshhold THRESH, reject a change of step size */ + if (eta < THRESH) + { + eta = ONE; + hprime = h; + } + else + { + /* Limit eta by etamax and hmax, then set hprime */ + eta = MIN(eta, etamax); + eta /= MAX(ONE, ABS(h) * hmax_inv * eta); + hprime = h * eta; + if (qprime < q) + nscon = 0; + } + + /* Reset etamax for the next step size change, and scale acor */ +} + +/*************** CVComputeEtaqm1 ********************************** + + This routine computes and returns the value of etaqm1 for a + possible decrease in order by 1. + +******************************************************************/ + +static realtype +CVComputeEtaqm1(CVodeMem cv_mem) +{ + realtype ddn; + + etaqm1 = ZERO; + if (q > 1) + { + ddn = N_VWrmsNorm(zn[q], ewt) / tq[1]; + etaqm1 = ONE / (RPowerR(BIAS1 * ddn, ONE / q) + ADDON); + } + return (etaqm1); +} + +/*************** CVComputeEtaqp1 ********************************** + + This routine computes and returns the value of etaqp1 for a + possible increase in order by 1. + +******************************************************************/ + +static realtype +CVComputeEtaqp1(CVodeMem cv_mem) +{ + realtype dup, cquot; + + etaqp1 = ZERO; + if (q != qmax) + { + cquot = (tq[5] / saved_tq5) * RPowerI(h / tau[2], L); + N_VLinearSum(-cquot, zn[qmax], ONE, acor, tempv); + dup = N_VWrmsNorm(tempv, ewt) / tq[3]; + etaqp1 = ONE / (RPowerR(BIAS3 * dup, ONE / (L + 1)) + ADDON); + } + return (etaqp1); +} + +/******************* CVChooseEta ********************************** + + Given etaqm1, etaq, etaqp1 (the values of eta for qprime = + q - 1, q, or q + 1, respectively), this routine chooses the + maximum eta value, sets eta to that value, and sets qprime to the + corresponding value of q. If there is a tie, the preference + order is to (1) keep the same order, then (2) decrease the order, + and finally (3) increase the order. If the maximum eta value + is below the threshhold THRESH, the order is kept unchanged and + eta is set to 1. + +******************************************************************/ + +static void +CVChooseEta(CVodeMem cv_mem) +{ + realtype etam; + + etam = MAX(etaqm1, MAX(etaq, etaqp1)); + + if (etam < THRESH) + { + eta = ONE; + qprime = q; + return; + } + + if (etam == etaq) + { + eta = etaq; + qprime = q; + } + else if (etam == etaqm1) + { + eta = etaqm1; + qprime = q - 1; + } + else + { + eta = etaqp1; + qprime = q + 1; + if (lmm == BDF) + N_VScale(ONE, acor, zn[qmax]); + } +} + +/****************** CVHandleFailure ****************************** + + This routine prints error messages for all cases of failure by + CVStep. It returns to CVode the value that CVode is to return to + the user. + +*****************************************************************/ + +static int +CVHandleFailure(CVodeMem cv_mem, int kflag) +{ + /* Set vector of absolute weighted local errors */ + N_VProd(acor, ewt, tempv); + N_VAbs(tempv, tempv); + + /* Depending on kflag, print error message and return error flag */ + switch (kflag) + { + case REP_ERR_FAIL: + { + char * error_string = CVMEM sformatf(MSG_ERR_FAILS, (double) tn, (double) h); + CVMEM warning_msg(error_string); + } + return (ERR_FAILURE); + case REP_CONV_FAIL: + { + char * error_string = CVMEM sformatf(MSG_CONV_FAILS, (double) tn, (double) h); + CVMEM warning_msg(error_string); + } + + return (CONV_FAILURE); + case SETUP_FAILED: + { + char * error_string = CVMEM sformatf(MSG_SETUP_FAILED, (double) tn); + CVMEM warning_msg(error_string); + } + return (SETUP_FAILURE); + case SOLVE_FAILED: + { + char * error_string = CVMEM sformatf(MSG_SOLVE_FAILED, (double) tn); + CVMEM warning_msg(error_string); + } + return (SOLVE_FAILURE); + } + return (-99); +} + +/****************** CVBDFStab *********************************** + This routine handles the BDF Stability Limit Detection Algorithm + STALD. It is called if lmm = BDF and the SLDET option is on. + If the order is 3 or more, the required norm data is saved. + If a decision to reduce order has not already been made, and + enough data has been saved, CVsldet is called. If it signals + a stability limit violation, the order is reduced, and the step + size is reset accordingly. + +*****************************************************************/ +#ifdef ORIGINAL_CVBDFStab +void +CVBDFStab(CVodeMem cv_mem) +{ + int i, k, ldflag, factorial; + realtype sq, sqm1, sqm2; + + /* If order is 3 or greater, then save scaled derivative data, + push old data down in i, then add current values to top. */ + + if (q >= 3) + { + for (k = 1; k <= 3; k++) + { + for (i = 5; i >= 2; i--) + ssdat[i][k] = ssdat[i - 1][k]; + } + factorial = 1; + for (i = 1; i <= q - 1; i++) + factorial *= i; + sq = factorial * q * (q + 1) * acnrm / tq[5]; + sqm1 = factorial * q * N_VWrmsNorm(zn[q], ewt); + sqm2 = factorial * N_VWrmsNorm(zn[q - 1], ewt); + ssdat[1][1] = sqm2 * sqm2; + ssdat[1][2] = sqm1 * sqm1; + ssdat[1][3] = sq * sq; + } + + if (qprime >= q) + { + + /* If order is 3 or greater, and enough ssdat has been saved, + nscon >= q+5, then call stability limit detection routine. */ + + if ((q >= 3) && (nscon >= q + 5)) + { + ldflag = CVsldet(cv_mem); + if (ldflag > 3) + { + /* A stability limit violation is indicated by + a return flag of 4, 5, or 6. + Reduce new order. */ + qprime = q - 1; + eta = etaqm1; + eta = MIN(eta, etamax); + eta = eta / MAX(ONE, ABS(h) * hmax_inv * eta); + hprime = h * eta; + iopt[NOR] = iopt[NOR] + 1; + /* CVMEM warning_msg(CVMEM sformatf( + " Order reduced to %d by CVBDFStab at nst = %d,\n h = %e hnew = %e\n", + qprime,nst,h,h*eta)); */ + } + } + } + else + { + /* Otherwise, let order increase happen, and + reset stability limit counter, nscon. */ + nscon = 0; + } +} +#endif +void +CVBDFStab(CVodeMem cv_mem) +{ + // appt try... + if (q >= 3 && qprime >= q) + { + if (tq[5] < saved_tq5) + qprime = 1; + //else + //nscon = 0; + } + + //int i, k, ldflag, factorial; + //realtype sq, sqm1, sqm2; + + ///* If order is 3 or greater, then save scaled derivative data, + // push old data down in i, then add current values to top. */ + + //if (q >= 3) + //{ + // for (k = 1; k <= 3; k++) + // { + // for (i = 5; i >= 2; i--) + // ssdat[i][k] = ssdat[i - 1][k]; + // } + // factorial = 1; + // for (i = 1; i <= q - 1; i++) + // factorial *= i; + // sq = factorial * q * (q + 1) * acnrm / MAX(tq[5], TINY); + // sqm1 = factorial * q * N_VWrmsNorm(zn[q], ewt); + // sqm2 = factorial * N_VWrmsNorm(zn[q - 1], ewt); + // ssdat[1][1] = sqm2 * sqm2; + // ssdat[1][2] = sqm1 * sqm1; + // ssdat[1][3] = sq * sq; + //} + + //if (qprime >= q) + //{ + + // /* If order is 3 or greater, and enough ssdat has been saved, + // nscon >= q+5, then call stability limit detection routine. */ + + // if ((q >= 3) && (nscon >= q + 5)) + // { + // ldflag = CVsldet(cv_mem); + // //cv_mem->cv_machenv->phreeqc_ptr->set_forward_output_to_log(1); // appt + // qprime = 1; // appt try + // //CVMEM warning_msg(CVMEM sformatf( + // // "CVBDFStab: ldflag = %d, order(q) = %d, qprime = %d, nst = %d, h = %8.2e, time = %8.2e\n", + // // ldflag, q, qprime, nst, h, CVMEM cvode_last_good_time)); + + // if (ldflag > 3) + // { + // /* A stability limit violation is indicated by + // a return flag of 4, 5, or 6. + // Reduce new order. */ + // qprime = q - 1; + // eta = etaqm1; + // eta = MIN(eta, etamax); + // eta = eta / MAX(ONE, ABS(h) * hmax_inv * eta); + // hprime = h * eta; + // iopt[NOR] = iopt[NOR] + 1; + // //CVMEM warning_msg(CVMEM sformatf( + // // " Order reduced to %d by CVBDFStab at nst = %d,\n h = %e hnew = %e\n", + // // qprime,nst,h,h*eta)); + // } + // } + //} + //else + //{ + // /* Otherwise, let order increase happen, and + // reset stability limit counter, nscon. */ + // nscon = 0; + //} +} + +/********************* CVsldet ************************************ + This routine detects stability limitation using stored scaled + derivatives data. CVsldet returns the magnitude of the + dominate characteristic root, rr. The presents of a stability + limit is indicated by rr > "something a little less then 1.0", + and a positive kflag. This routine should only be called if + order is greater than or equal to 3, and data has been collected + for 5 time steps. + + Returned values: + kflag = 1 -> Found stable characteristic root, normal matrix case + kflag = 2 -> Found stable characteristic root, quartic solution + kflag = 3 -> Found stable characteristic root, quartic solution, + with Newton correction + kflag = 4 -> Found stability violation, normal matrix case + kflag = 5 -> Found stability violation, quartic solution + kflag = 6 -> Found stability violation, quartic solution, + with Newton correction + + kflag < 0 -> No stability limitation, + or could not compute limitation. + + kflag = -1 -> Min/max ratio of ssdat too small. + kflag = -2 -> For normal matrix case, vmax > vrrt2*vrrt2 + kflag = -3 -> For normal matrix case, The three ratios + are inconsistent. + kflag = -4 -> Small coefficient prevents elimination of quartics. + kflag = -5 -> R value from quartics not consistent. + kflag = -6 -> No corrected root passes test on qk values + kflag = -7 -> Trouble solving for sigsq. + kflag = -8 -> Trouble solving for B, or R via B. + kflag = -9 -> R via sigsq[k] disagrees with R from data. + +********************************************************************/ + +#ifdef ORIGINAL_CVBDFStab +static int +CVsldet(CVodeMem cv_mem) +{ + integertype i, k, j, it, kmin, kflag = 0; + realtype rat[5][4], rav[4], qkr[4], sigsq[4], smax[4], ssmax[4]; + realtype drr[4], rrc[4], sqmx[4], qjk[4][4], vrat[5], qc[6][4], qco[6][4]; + realtype rr, rrcut, vrrtol, vrrt2, sqtol, rrtol; + realtype smink, smaxk, sumrat, sumrsq, vmin, vmax, drrmax, adrr; + realtype /*small_cvode,*/ tem, sqmax, saqk, qp, s, sqmaxk, saqj, sqmin; + //realtype rsa, rsb, rsc, rsd, rse, rd1a, rd1b, rd1c, rd1d; + realtype rsa, rsb, rsc, rsd, rd1a, rd1b, rd1c; + //realtype rd2a, rd2b, rd2c, rd3a, rd3b, cest1, corr1; + realtype rd2a, rd2b, rd3a, cest1, corr1; + realtype ratp, ratm, qfac1, qfac2, bb, rrb; + + /* The following are cutoffs and tolerances used by this routine */ + + rrcut = 0.98; + vrrtol = 1.0e-4; + vrrt2 = 5.0e-4; + sqtol = 1.0e-3; + rrtol = 1.0e-2; + + rr = ZERO; + + /* Index k corresponds to the degree of the interpolating polynomial. */ + /* k = 1 -> q-1 */ + /* k = 2 -> q */ + /* k = 3 -> q+1 */ + + /* Index i is a backward-in-time index, i = 1 -> current time, */ + /* i = 2 -> previous step, etc */ + + /* get maxima, minima, and variances, and form quartic coefficients */ + + for (k = 1; k <= 3; k++) + { + smink = ssdat[1][k]; + smaxk = ZERO; + + for (i = 1; i <= 5; i++) + { + smink = MIN(smink, ssdat[i][k]); + smaxk = MAX(smaxk, ssdat[i][k]); + } + + if (smink < TINY * smaxk) + { + kflag = -1; + return (kflag); + } + smax[k] = smaxk; + ssmax[k] = smaxk * smaxk; + + sumrat = ZERO; + sumrsq = ZERO; + for (i = 1; i <= 4; i++) + { + rat[i][k] = ssdat[i][k] / ssdat[i + 1][k]; + sumrat = sumrat + rat[i][k]; + sumrsq = sumrsq + rat[i][k] * rat[i][k]; + } + rav[k] = FOURTH * sumrat; + vrat[k] = ABS(FOURTH * sumrsq - rav[k] * rav[k]); + + qc[5][k] = ssdat[1][k] * ssdat[3][k] - ssdat[2][k] * ssdat[2][k]; + qc[4][k] = ssdat[2][k] * ssdat[3][k] - ssdat[1][k] * ssdat[4][k]; + qc[3][k] = ZERO; + qc[2][k] = ssdat[2][k] * ssdat[5][k] - ssdat[3][k] * ssdat[4][k]; + qc[1][k] = ssdat[4][k] * ssdat[4][k] - ssdat[3][k] * ssdat[5][k]; + + for (i = 1; i <= 5; i++) + { + qco[i][k] = qc[i][k]; + } + } /* End of k loop */ + + /* Isolate normal or nearly-normal matrix case. Three quartic will + have common or nearly-common roots in this case. + Return a kflag = 1 if this procedure works. If three root + differ more than vrrt2, return error kflag = -3. */ + + vmin = MIN(vrat[1], MIN(vrat[2], vrat[3])); + vmax = MAX(vrat[1], MAX(vrat[2], vrat[3])); + + if (vmin < vrrtol * vrrtol) + { + if (vmax > vrrt2 * vrrt2) + { + kflag = -2; + return (kflag); + } + else + { + rr = (rav[1] + rav[2] + rav[3]) / THREE; + + drrmax = ZERO; + for (k = 1; k <= 3; k++) + { + adrr = ABS(rav[k] - rr); + drrmax = MAX(drrmax, adrr); + } + if (drrmax > vrrt2) + { + kflag = -3; + } + + kflag = 1; + + /* can compute charactistic root, drop to next section */ + + } + } + else + { + + /* use the quartics to get rr. */ + + if (ABS(qco[1][1]) < TINY * ssmax[1]) + { + //small_cvode = qco[1][1]; + kflag = -4; + return (kflag); + } + + tem = qco[1][2] / qco[1][1]; + for (i = 2; i <= 5; i++) + { + qco[i][2] = qco[i][2] - tem * qco[i][1]; + } + + qco[1][2] = ZERO; + tem = qco[1][3] / qco[1][1]; + for (i = 2; i <= 5; i++) + { + qco[i][3] = qco[i][3] - tem * qco[i][1]; + } + qco[1][3] = ZERO; + + if (ABS(qco[2][2]) < TINY * ssmax[2]) + { + //small_cvode = qco[2][2]; + kflag = -4; + return (kflag); + } + + tem = qco[2][3] / qco[2][2]; + for (i = 3; i <= 5; i++) + { + qco[i][3] = qco[i][3] - tem * qco[i][2]; + } + + if (ABS(qco[4][3]) < TINY * ssmax[3]) + { + //small_cvode = qco[4][3]; + kflag = -4; + return (kflag); + } + + rr = -qco[5][3] / qco[4][3]; + + if (rr < TINY || rr > HUN) + { + kflag = -5; + return (kflag); + } + + for (k = 1; k <= 3; k++) + { + qkr[k] = + qc[5][k] + rr * (qc[4][k] + + rr * rr * (qc[2][k] + rr * qc[1][k])); + } + + sqmax = ZERO; + for (k = 1; k <= 3; k++) + { + saqk = ABS(qkr[k]) / ssmax[k]; + if (saqk > sqmax) + sqmax = saqk; + } + sqmin = sqmax; + if (sqmax < sqtol) + { + kflag = 2; + + /* can compute charactistic root, drop to "given rr,etc" */ + + } + else + { + + /* do Newton corrections to improve rr. */ + + for (it = 1; it <= 3; it++) + { + for (k = 1; k <= 3; k++) + { + qp = qc[4][k] + rr * rr * (THREE * qc[2][k] + + rr * FOUR * qc[1][k]); + drr[k] = ZERO; + if (ABS(qp) > TINY * ssmax[k]) + drr[k] = -qkr[k] / qp; + rrc[k] = rr + drr[k]; + } + + for (k = 1; k <= 3; k++) + { + s = rrc[k]; + sqmaxk = ZERO; + for (j = 1; j <= 3; j++) + { + qjk[j][k] = qc[5][j] + s * (qc[4][j] + + s * s * (qc[2][j] + + s * qc[1][j])); + saqj = ABS(qjk[j][k]) / ssmax[j]; + if (saqj > sqmaxk) + sqmaxk = saqj; + } + sqmx[k] = sqmaxk; + } + + sqmin = sqmx[1]; + kmin = 1; + for (k = 2; k <= 3; k++) + { + if (sqmx[k] < sqmin) + { + kmin = k; + sqmin = sqmx[k]; + } + } + rr = rrc[kmin]; + + if (sqmin < sqtol) + { + kflag = 3; + /* can compute charactistic root */ + /* break out of Newton correction loop and drop to "given rr,etc" */ + break; + } + else + { + for (j = 1; j <= 3; j++) + { + qkr[j] = qjk[j][kmin]; + } + } + } /* end of Newton correction loop */ + + if (sqmin > sqtol) + { + kflag = -6; + return (kflag); + } + } /* end of if (sqmax < sqtol) else */ + } /* end of if(vmin < vrrtol*vrrtol) else, quartics to get rr. */ + + /* given rr, find sigsq[k] and verify rr. */ + /* All positive kflag drop to this section */ + + for (k = 1; k <= 3; k++) + { + rsa = ssdat[1][k]; + rsb = ssdat[2][k] * rr; + rsc = ssdat[3][k] * rr * rr; + rsd = ssdat[4][k] * rr * rr * rr; + //rse = ssdat[5][k] * rr * rr * rr * rr; + rd1a = rsa - rsb; + rd1b = rsb - rsc; + rd1c = rsc - rsd; + //rd1d = rsd - rse; + rd2a = rd1a - rd1b; + rd2b = rd1b - rd1c; + //rd2c = rd1c - rd1d; + rd3a = rd2a - rd2b; + //rd3b = rd2b - rd2c; + /* rd3b = rd3b; */ + + if (ABS(rd1b) < TINY * smax[k]) + { + kflag = -7; + return (kflag); + } + + cest1 = -rd3a / rd1b; + if (cest1 < TINY || cest1 > FOUR) + { + kflag = -7; + return (kflag); + } + corr1 = (rd2b / cest1) / (rr * rr); + sigsq[k] = ssdat[3][k] + corr1; + } + + if (sigsq[2] < TINY) + { + kflag = -8; + return (kflag); + } + + ratp = sigsq[3] / sigsq[2]; + ratm = sigsq[1] / sigsq[2]; + qfac1 = FOURTH * (q * q - ONE); + qfac2 = TWO / (q - ONE); + bb = ratp * ratm - ONE - qfac1 * ratp; + tem = ONE - qfac2 * bb; + + if (ABS(tem) < TINY) + { + kflag = -8; + return (kflag); + } + + rrb = ONE / tem; + + if (ABS(rrb - rr) > rrtol) + { + kflag = -9; + return (kflag); + } + + /* Check to see if rr is above cutoff rrcut */ + if (rr > rrcut) + { + if (kflag == 1) + kflag = 4; + if (kflag == 2) + kflag = 5; + if (kflag == 3) + kflag = 6; + } + + /* All positive kflag returned at this point */ + + return (kflag); + +} +#endif + +/*******************************************************************/ +/********* END Private Helper Functions Implementation *************/ +/*******************************************************************/ + + +/***************************************************************/ +/************** END CVODE Implementation ***********************/ +/***************************************************************/ diff --git a/phreeqcpp/cvode.h b/phreeqcpp/cvode.h new file mode 100644 index 00000000..57e72692 --- /dev/null +++ b/phreeqcpp/cvode.h @@ -0,0 +1,940 @@ +#ifndef _INC_CVODE_H +#define _INC_CVODE_H +/************************************************************************** + * * + * File : cvode.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, Radu Serban * + * and Dan Shumaker @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the interface file for the main CVODE integrator. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _cvode_h +#define _cvode_h + + +#include +#include "sundialstypes.h" +#include "nvector.h" + +#undef SS + +/****************************************************************** + * * + * CVODE is used to solve numerically the ordinary initial value * + * problem : * + * * + * y' = f(t,y), * + * y(t0) = y0, * + * * + * where t0, y0 in R^N, and f: R x R^N -> R^N are given. * + * * + ******************************************************************/ + + +/****************************************************************** + * * + * Enumerations for inputs to CVodeMalloc, CVReInit, and CVode. * + *----------------------------------------------------------------* + * Symbolic constants for the lmm, iter, and itol input * + * parameters to CVodeMalloc and CVReInit, as well as the input * + * parameter itask to CVode, are given below. * + * * + * lmm : The user of the CVODE package specifies whether to use * + * the ADAMS or BDF (backward differentiation formula) * + * linear multistep method. The BDF method is recommended * + * for stiff problems, and the ADAMS method is recommended * + * for nonstiff problems. * + * * + * iter : At each internal time step, a nonlinear equation must * + * be solved. The user can specify either FUNCTIONAL * + * iteration, which does not require linear algebra, or a * + * NEWTON iteration, which requires the solution of linear * + * systems. In the NEWTON case, the user also specifies a * + * CVODE linear solver. NEWTON is recommended in case of * + * stiff problems. * + * * + * itol : This parameter specifies the relative and absolute * + * tolerance types to be used. The SS tolerance type means * + * a scalar relative and absolute tolerance, while the SV * + * tolerance type means a scalar relative tolerance and a * + * vector absolute tolerance (a potentially different * + * absolute tolerance for each vector component). * + * * + * itask : The itask input parameter to CVode indicates the job * + * of the solver for the next user step. The NORMAL * + * itask is to have the solver take internal steps until * + * it has reached or just passed the user specified tout * + * parameter. The solver then interpolates in order to * + * return an approximate value of y(tout). The ONE_STEP * + * option tells the solver to just take one internal step * + * and return the solution at the point reached by that * + * step. * + * * + ******************************************************************/ + + enum + { ADAMS, BDF }; /* lmm */ + + enum + { FUNCTIONAL, NEWTON }; /* iter */ + + enum + { SS, SV }; /* itol */ + + enum + { NORMAL, ONE_STEP }; /* itask */ + + +/****************************************************************** + * * + * Type : RhsFn * + *----------------------------------------------------------------* + * The f function which defines the right hand side of the ODE * + * system y' = f(t,y) must have type RhsFn. * + * f takes as input the problem size N, the independent variable * + * value t, and the dependent variable vector y. It stores the * + * result of f(t,y) in the vector ydot. The y and ydot arguments * + * are of type N_Vector. * + * (Allocation of memory for ydot is handled within CVODE.) * + * The f_data parameter is the same as the f_data * + * parameter passed by the user to the CVodeMalloc routine. This * + * user-supplied pointer is passed to the user's f function * + * every time it is called. * + * A RhsFn f does not have a return value. * + * * + ******************************************************************/ + + typedef void (*RhsFn) (integertype N, realtype t, N_Vector y, + N_Vector ydot, void *f_data); + + +/****************************************************************** + * * + * Function : CVodeMalloc * + *----------------------------------------------------------------* + * CVodeMalloc allocates and initializes memory for a problem to * + * to be solved by CVODE. * + * * + * N is the number of equations in the ODE system. * + * * + * f is the right hand side function in y' = f(t,y). * + * * + * t0 is the initial value of t. * + * * + * y0 is the initial condition vector y(t0). * + * * + * lmm is the type of linear multistep method to be used. * + * The legal values are ADAMS and BDF (see previous * + * description). * + * * + * iter is the type of iteration used to solve the nonlinear * + * system that arises during each internal time step. * + * The legal values are FUNCTIONAL and NEWTON. * + * * + * itol is the type of tolerances to be used. * + * The legal values are: * + * SS (scalar relative and absolute tolerances), * + * SV (scalar relative tolerance and vector * + * absolute tolerance). * + * * + * reltol is a pointer to the relative tolerance scalar. * + * * + * abstol is a pointer to the absolute tolerance scalar or * + * an N_Vector of absolute tolerances. * + * * + * The parameters itol, reltol, and abstol define a vector of * + * error weights, ewt, with components * + * ewt[i] = 1/(reltol*abs(y[i]) + abstol) (if itol = SS), or * + * ewt[i] = 1/(reltol*abs(y[i]) + abstol[i]) (if itol = SV). * + * This vector is used in all error and convergence tests, which * + * use a weighted RMS norm on all error-like vectors v: * + * WRMSnorm(v) = sqrt( (1/N) sum(i=1..N) (v[i]*ewt[i])^2 ). * + * * + * f_data is a pointer to user data that will be passed to the * + * user's f function every time f is called. * + * * + * errfp is the file pointer for an error file where all CVODE * + * warning and error messages will be written. This * + * parameter can be stdout (standard output), stderr * + * (standard error), a file pointer (corresponding to * + * a user error file opened for writing) returned by * + * fopen, or NULL. If the user passes NULL, then all * + * messages will be written to standard output. * + * * + * optIn is a flag indicating whether there are any optional * + * inputs from the user in the arrays iopt and ropt. * + * Pass FALSE to indicate no optional inputs and TRUE * + * to indicate that optional inputs are present. * + * * + * iopt is the user-allocated array (of size OPT_SIZE given * + * later) that will hold optional integer inputs and * + * outputs. The user can pass NULL if he/she does not * + * wish to use optional integer inputs or outputs. * + * If optIn is TRUE, the user should preset to 0 those * + * locations for which default values are to be used. * + * * + * ropt is the user-allocated array (of size OPT_SIZE given * + * later) that will hold optional real inputs and * + * outputs. The user can pass NULL if he/she does not * + * wish to use optional real inputs or outputs. * + * If optIn is TRUE, the user should preset to 0.0 the * + * locations for which default values are to be used. * + * * + * machEnv is a pointer to machine environment-specific * + * information. * + * * + * Note: The tolerance values may be changed in between calls to * + * CVode for the same problem. These values refer to * + * (*reltol) and either (*abstol), for a scalar absolute * + * tolerance, or the components of abstol, for a vector * + * absolute tolerance. * + * * + * If successful, CVodeMalloc returns a pointer to initialized * + * problem memory. This pointer should be passed to CVode. If * + * an initialization error occurs, CVodeMalloc prints an error * + * message to the file specified by errfp and returns NULL. * + * * + ******************************************************************/ + + + void *CVodeMalloc(integertype N, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, realtype * reltol, + void *abstol, void *f_data, FILE * errfp, + booleantype optIn, long int iopt[], realtype ropt[], + M_Env machEnv); + + +/****************************************************************** + * * + * Function : CVReInit * + *----------------------------------------------------------------* + * CVReInit re-initializes CVode for the solution of a problem, * + * where a prior call to CVodeMalloc has been made with the same * + * problem size N. CVReInit performs the same input checking * + * and initializations that CVodeMalloc does (except for N). * + * But it does no memory allocation, assuming that the existing * + * internal memory is sufficient for the new problem. * + * * + * The use of CVReInit requires that the maximum method order, * + * maxord, is no larger for the new problem than for the problem * + * specified in the last call to CVodeMalloc. This condition is * + * automatically fulfilled if the multistep method parameter lmm * + * is unchanged (or changed from ADAMS to BDF) and the default * + * value for maxord is specified. * + * * + * If iter = NEWTON, then following the call to CVReInit, a call * + * to the linear solver specification routine is necessary if a * + * different linear solver is chosen, but may not be otherwise. * + * If the same linear solver is chosen, and there are no changes * + * in the input parameters to the specification routine, then no * + * call to that routine is needed. * + * If there are changes in parameters, but they do not increase * + * the linear solver memory size, then a call to the corresponding* + * CVReInit routine must made to communicate the new * + * parameters; in that case the linear solver memory is reused. * + * If the parameter changes do increase the linear solver memory * + * size, then the main linear solver specification routine must be* + * called. See the linear solver documentation for full details. * + * * + * The first argument to CVReInit is: * + * * + * cvode_mem = pointer to CVODE memory returned by CVodeMalloc. * + * * + * All the remaining arguments to CVReInit have names and * + * meanings identical to those of CVodeMalloc. Note that the * + * problem size N is not passed as an argument to CVReInit, * + * as that is assumed to be unchanged since the CVodeMalloc call. * + * * + * The return value of CVReInit is equal to SUCCESS = 0 if there * + * were no errors; otherwise it is a negative int equal to: * + * CVREI_NO_MEM indicating cvode_mem was NULL, or * + * CVREI_ILL_INPUT indicating an input argument was illegal * + * (including an attempt to increase maxord). * + * In case of an error return, an error message is also printed. * + * * + * Note: the reported workspace sizes iopt[LENRW] and iopt[LENIW] * + * are left unchanged from the values computed by CVodeMalloc, and* + * so may be larger than would be computed for the new problem. * + ******************************************************************/ + + int CVReInit(void *cvode_mem, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, realtype * reltol, + void *abstol, void *f_data, FILE * errfp, + booleantype optIn, long int iopt[], + realtype ropt[], M_Env machEnv); + + +/* CVReInit return values: */ + +/* SUCCESS = 0 (Defined under CVode return values, but listed + here also for completeness) */ + enum + { CVREI_NO_MEM = -1, CVREI_ILL_INPUT = -2 }; + + +/****************************************************************** + * * + * Function : CVode * + *----------------------------------------------------------------* + * CVode integrates the ODE over an interval in t. * + * If itask is NORMAL, then the solver integrates from its * + * current internal t value to a point at or beyond tout, then * + * interpolates to t = tout and returns y(tout) in the user- * + * allocated vector yout. If itask is ONE_STEP, then the solver * + * takes one internal time step and returns in yout the value of * + * y at the new internal time. In this case, tout is used only * + * during the first call to CVode to determine the direction of * + * integration and the rough scale of the problem. In either * + * case, the time reached by the solver is placed in (*t). The * + * user is responsible for allocating the memory for this value. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * tout is the next time at which a computed solution is desired * + * * + * yout is the computed solution vector. In NORMAL mode with no * + * errors, yout=y(tout). * + * * + * t is a pointer to a real location. CVode sets (*t) to the * + * time reached by the solver and returns yout=y(*t). * + * * + * itask is either NORMAL or ONE_STEP mode. These two modes have * + * been described above. * + * * + * The return values for CVode are defined later in this file. * + * Here is a brief description of each return value: * + * * + * SUCCESS : CVode succeeded. * + * * + * CVODE_NO_MEM : The cvode_mem argument was NULL. * + * * + * ILL_INPUT : One of the inputs to CVode is illegal. This * + * includes the situation when a component of the * + * error weight vectors becomes < 0 during * + * internal time-stepping. The ILL_INPUT flag * + * will also be returned if the linear solver * + * routine CV--- (called by the user after * + * calling CVodeMalloc) failed to set one of the * + * linear solver-related fields in cvode_mem or * + * if the linear solver's init routine failed. In * + * any case, the user should see the printed * + * error message for more details. * + * * + * TOO_MUCH_WORK : The solver took mxstep internal steps but * + * could not reach tout. The default value for * + * mxstep is MXSTEP_DEFAULT = 500. * + * * + * TOO_MUCH_ACC : The solver could not satisfy the accuracy * + * demanded by the user for some internal step. * + * * + * ERR_FAILURE : Error test failures occurred too many times * + * (= MXNEF = 7) during one internal time step or * + * occurred with |h| = hmin. * + * * + * CONV_FAILURE : Convergence test failures occurred too many * + * times (= MXNCF = 10) during one internal time * + * step or occurred with |h| = hmin. * + * * + * SETUP_FAILURE : The linear solver's setup routine failed in an * + * unrecoverable manner. * + * * + * SOLVE_FAILURE : The linear solver's solve routine failed in an * + * unrecoverable manner. * + * * + ******************************************************************/ + + + int CVode(void *cvode_mem, realtype tout, N_Vector yout, + realtype * t, int itask); + + +/* CVode return values */ + + enum + { SUCCESS = 0, CVODE_NO_MEM = -1, ILL_INPUT = -2, TOO_MUCH_WORK = -3, + TOO_MUCH_ACC = -4, ERR_FAILURE = -5, CONV_FAILURE = -6, + SETUP_FAILURE = -7, SOLVE_FAILURE = -8 + }; + + +/****************************************************************** + * * + * Function : CVodeDky * + *----------------------------------------------------------------* + * CVodeDky computes the kth derivative of the y function at * + * time t, where tn-hu <= t <= tn, tn denotes the current * + * internal time reached, and hu is the last internal step size * + * successfully used by the solver. The user may request * + * k=0, 1, ..., qu, where qu is the current order. The * + * derivative vector is returned in dky. This vector must be * + * allocated by the caller. It is only legal to call this * + * function after a successful return from CVode. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * t is the time at which the kth derivative of y is evaluated. * + * The legal range for t is [tn-hu,tn] as described above. * + * * + * k is the order of the derivative of y to be computed. The * + * legal range for k is [0,qu] as described above. * + * * + * dky is the output derivative vector [(D_k)y](t). * + * * + * The return values for CVodeDky are defined later in this file. * + * Here is a brief description of each return value: * + * * + * OKAY : CVodeDky succeeded. * + * * + * BAD_K : k is not in the range 0, 1, ..., qu. * + * * + * BAD_T : t is not in the interval [tn-hu,tn]. * + * * + * BAD_DKY : The dky argument was NULL. * + * * + * DKY_NO_MEM : The cvode_mem argument was NULL. * + * * + ******************************************************************/ + + + int CVodeDky(void *cvode_mem, realtype t, int k, N_Vector dky); + + +/* CVodeDky return values */ + + enum + { OKAY = 0, BAD_K = -1, BAD_T = -2, BAD_DKY = -3, DKY_NO_MEM = -4 }; + + +/****************************************************************** + * * + * Function : CVodeFree * + *----------------------------------------------------------------* + * CVodeFree frees the problem memory cvode_mem allocated by * + * CVodeMalloc. Its only argument is the pointer cvode_mem * + * returned by CVodeMalloc. * + * * + ******************************************************************/ + + void CVodeFree(void *cvode_mem); + + +/****************************************************************** + * * + * Optional Inputs and Outputs * + *----------------------------------------------------------------* + * The user should declare two arrays for optional input and * + * output, an iopt array for optional integer input and output * + * and an ropt array for optional real input and output. The * + * size of both these arrays should be OPT_SIZE. * + * So the user's declaration should look like: * + * * + * long int iopt[OPT_SIZE]; * + * realtype ropt[OPT_SIZE]; * + * * + * The enumerations below the OPT_SIZE definition * + * are indices into the iopt and ropt arrays. Here is a brief * + * description of the contents of these positions: * + * * + * iopt[MAXORD] : maximum lmm order to be used by the solver. * + * Optional input. (Default = 12 for ADAMS, 5 for * + * BDF). * + * * + * iopt[MXSTEP] : maximum number of internal steps to be taken by * + * the solver in its attempt to reach tout. * + * Optional input. (Default = 500). * + * * + * iopt[MXHNIL] : maximum number of warning messages issued by the* + * solver that t + h = t on the next internal step.* + * A value of -1 means no such messages are issued.* + * Optional input. (Default = 10). * + * * + * iopt[NST] : cumulative number of internal steps taken by * + * the solver (total so far). Optional output. * + * * + * iopt[NFE] : number of calls to the user's f function. * + * Optional output. * + * * + * iopt[NSETUPS] : number of calls made to the linear solver's * + * setup routine. Optional output. * + * * + * iopt[NNI] : number of NEWTON iterations performed. * + * Optional output. * + * * + * iopt[NCFN] : number of nonlinear convergence failures * + * that have occurred. Optional output. * + * * + * iopt[NETF] : number of local error test failures that * + * have occurred. Optional output. * + * * + * iopt[QU] : order used during the last internal step. * + * Optional output. * + * * + * iopt[QCUR] : order to be used on the next internal step. * + * Optional output. * + * * + * iopt[LENRW] : size of required CVODE internal real work * + * space, in realtype words. Optional output. * + * * + * iopt[LENIW] : size of required CVODE internal integer work * + * space, in integertype words. Optional output. * + * * + * iopt[SLDET] : Flag to turn on/off stability limit detection * + * (1 = on, 0 = off). When BDF is used and order * + * is 3 or greater, CVsldet is call to detect * + * stability limit. If limit is detected, the * + * order is reduced. Optional input. * + * * + * iopt[NOR] : Number of order reductions due to * + * stability limit detection. * + * Optional output. * + * * + * ropt[H0] : initial step size. Optional input. * + * * + * ropt[HMAX] : maximum absolute value of step size allowed. * + * Optional input. (Default is infinity). * + * Note: If optIn = TRUE, the value of ropt[HMAX] * + * is examined on every call to CVode, and so can * + * be changed between calls. * + * * + * ropt[HMIN] : minimum absolute value of step size allowed. * + * Optional input. (Default is 0.0). * + * * + * ropt[HU] : step size for the last internal step. * + * Optional output. * + * * + * ropt[HCUR] : step size to be attempted on the next internal * + * step. Optional output. * + * * + * ropt[TCUR] : current internal time reached by the solver. * + * Optional output. * + * * + * ropt[TOLSF] : a suggested factor by which the user's * + * tolerances should be scaled when too much * + * accuracy has been requested for some internal * + * step. Optional output. * + * * + ******************************************************************/ + +/* iopt, ropt array sizes */ + +#define OPT_SIZE 40 + + +/* iopt and ropt offsets * + * The constants CVODE_IOPT_SIZE and CVODE_ROPT_SIZE are equal to * + * the number of integer and real optional inputs and outputs * + * actually accessed in cvode.c. The locations beyond these * + * values are used by the linear solvers. */ + +#define CVODE_IOPT_SIZE 15 +#define CVODE_ROPT_SIZE 7 + +/* iopt indices */ + enum + { MAXORD, MXSTEP, MXHNIL, + NST, NFE, NSETUPS, NNI, NCFN, NETF, QU, QCUR, + LENRW, LENIW, SLDET, NOR + }; + +/* ropt indices */ + + enum + { H0, HMAX, HMIN, + HU, HCUR, TCUR, TOLSF + }; + + +/* Basic CVODE constants */ + +#define ADAMS_Q_MAX 12 /* max value of q for lmm == ADAMS */ +#define BDF_Q_MAX 5 /* max value of q for lmm == BDF */ +#define Q_MAX ADAMS_Q_MAX /* max value of q for either lmm */ +#define L_MAX (Q_MAX+1) /* max value of L for either lmm */ +#define NUM_TESTS 5 /* number of error test quantities */ + + +/****************************************************************** + * * + * Types : struct CVodeMemRec, CVodeMem * + *----------------------------------------------------------------* + * The type CVodeMem is type pointer to struct CVodeMemRec. This * + * structure contains fields to keep track of problem state. * + * * + ******************************************************************/ + + typedef struct CVodeMemRec + { + + realtype cv_uround; /* machine unit roundoff */ + + /* Problem Specification Data */ + + integertype cv_N; /* ODE system size */ + RhsFn cv_f; /* y' = f(t,y(t)) */ + void *cv_f_data; /* user pointer passed to f */ + int cv_lmm; /* lmm = ADAMS or BDF */ + int cv_iter; /* iter = FUNCTIONAL or NEWTON */ + int cv_itol; /* itol = SS or SV */ + realtype *cv_reltol; /* ptr to relative tolerance */ + void *cv_abstol; /* ptr to absolute tolerance */ + + /* Nordsieck History Array */ + + N_Vector cv_zn[L_MAX]; /* Nordsieck array, of size N x (q+1). */ + /* zn[j] is a vector of length N (j=0,...,q) */ + /* zn[j] = [1/factorial(j)] * h^j * (jth */ + /* derivative of the interpolating polynomial */ + + /* Vectors of length N */ + + N_Vector cv_ewt; /* error weight vector */ + N_Vector cv_y; /* y is used as temporary storage by the solver */ + /* The memory is provided by the user to CVode */ + /* where the vector is named yout. */ + N_Vector cv_acor; /* In the context of the solution of the */ + /* nonlinear equation, acor = y_n(m) - y_n(0). */ + /* On return, this vector is scaled to give */ + /* the estimated local error in y. */ + N_Vector cv_tempv; /* temporary storage vector */ + N_Vector cv_ftemp; /* temporary storage vector */ + + /* Step Data */ + + int cv_q; /* current order */ + int cv_qprime; /* order to be used on the next step */ + /* = q-1, q, or q+1 */ + int cv_qwait; /* number of internal steps to wait before */ + /* considering a change in q */ + int cv_L; /* L = q + 1 */ + + realtype cv_h; /* current step size */ + realtype cv_hprime; /* step size to be used on the next step */ + realtype cv_eta; /* eta = hprime / h */ + realtype cv_hscale; /* value of h used in zn */ + realtype cv_tn; /* current internal value of t */ + + realtype cv_tau[L_MAX + 1]; /* array of previous q+1 successful step */ + /* sizes indexed from 1 to q+1 */ + realtype cv_tq[NUM_TESTS + 1]; /* array of test quantities indexed from */ + /* 1 to NUM_TESTS(=5) */ + realtype cv_l[L_MAX]; /* coefficients of l(x) (degree q poly) */ + + realtype cv_rl1; /* 1 / l[1] */ + realtype cv_gamma; /* gamma = h * rl1 */ + realtype cv_gammap; /* gamma at the last setup call */ + realtype cv_gamrat; /* gamma / gammap */ + + realtype cv_crate; /* estimated corrector convergence rate */ + realtype cv_acnrm; /* | acor | wrms */ + int cv_mnewt; /* Newton iteration counter */ + + /* Limits */ + + int cv_qmax; /* q <= qmax */ + int cv_mxstep; /* maximum number of internal steps for one user call */ + int cv_maxcor; /* maximum number of corrector iterations for the */ + /* solution of the nonlinear equation */ + int cv_mxhnil; /* maximum number of warning messages issued to the */ + /* user that t + h == t for the next internal step */ + + realtype cv_hmin; /* |h| >= hmin */ + realtype cv_hmax_inv; /* |h| <= 1/hmax_inv */ + realtype cv_etamax; /* eta <= etamax */ + + /* Counters */ + + long int cv_nst; /* number of internal steps taken */ + long int cv_nfe; /* number of f calls */ + long int cv_ncfn; /* number of corrector convergence failures */ + long int cv_netf; /* number of error test failures */ + long int cv_nni; /* number of Newton iterations performed */ + long int cv_nsetups; /* number of setup calls */ + int cv_nhnil; /* number of messages issued to the user that */ + /* t + h == t for the next iternal step */ + long int cv_lrw; /* number of realtype words in CVODE work vectors */ + long int cv_liw; /* no. of integertype words in CVODE work vectors */ + long int cv_nscon; /* counter for STALD method */ + + realtype cv_etaqm1; /* ratio of new to old h for order q-1 */ + realtype cv_etaq; /* ratio of new to old h for order q */ + realtype cv_etaqp1; /* ratio of new to old h for order q+1 */ + realtype cv_ssdat[6][4]; /* scaled data array for STALD */ + + /* Linear Solver Data */ + + /* Linear Solver functions to be called */ + + int (*cv_linit) (struct CVodeMemRec * cv_mem); + + int (*cv_lsetup) (struct CVodeMemRec * cv_mem, int convfail, + N_Vector ypred, N_Vector fpred, + booleantype * jcurPtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + int (*cv_lsolve) (struct CVodeMemRec * cv_mem, N_Vector b, + N_Vector ycur, N_Vector fcur); + + void (*cv_lfree) (struct CVodeMemRec * cv_mem); + + /* Linear Solver specific memory */ + + void *cv_lmem; + + /* Saved Values */ + + int cv_qu; /* last successful q value used */ + long int cv_nstlp; /* step number of last setup call */ + realtype cv_hu; /* last successful h value used */ + realtype cv_saved_tq5; /* saved value of tq[5] */ + booleantype cv_jcur; /* Is the Jacobian info used by */ + /* linear solver current? */ + realtype cv_tolsf; /* tolerance scale factor */ + booleantype cv_setupNonNull; /* Does setup do something? */ + + /* Arrays for Optional Input and Optional Output */ + + booleantype cv_optIn; /* boolean input optIn */ + long int *cv_iopt; /* long int optional input, output */ + realtype *cv_ropt; /* real optional input, output */ + + /* Error File */ + +#if !defined(R_SO) + FILE *cv_errfp; /* CVODE error messages are sent to errfp */ +#endif + + /* Pointer to Machine Environment-Specific Information */ + + M_Env cv_machenv; + + /* Stability Limit Detection control flag */ + + booleantype cv_sldeton; /* Is Stability Limit Detection on */ + + } *CVodeMem; + + +/****************************************************************** + * * + * Communication between user and a CVODE Linear Solver * + *----------------------------------------------------------------* + * Return values of the linear solver specification routine. * + * The values of these are given in the enum statement below. * + * SUCCESS : The routine was successful. * + * * + * LMEM_FAIL : A memory allocation failed. * + * * + * LIN_ILL_INPUT: Some input was illegal (see message). * + * * + ******************************************************************/ + +/* SUCCESS = 0 (Defined under CVode return values, but listed + here also for completeness) */ + enum + { LMEM_FAIL = -1, LIN_ILL_INPUT = -2 }; + + +/****************************************************************** + * * + * Communication between cvode.c and a CVODE Linear Solver * + *----------------------------------------------------------------* + * (1) cv_linit return values * + * * + * LINIT_OK : The cv_linit routine succeeded. * + * * + * LINIT_ERR : The cv_linit routine failed. Each linear solver * + * init routine should print an appropriate error * + * message to (cv_mem->errfp). * + * * + * (2) convfail (input to cv_lsetup) * + * * + * NO_FAILURES : Either this is the first cv_setup call for this * + * step, or the local error test failed on the * + * previous attempt at this step (but the Newton * + * iteration converged). * + * * + * FAIL_BAD_J : This value is passed to cv_lsetup if * + * * + * (1) The previous Newton corrector iteration * + * did not converge and the linear solver's * + * setup routine indicated that its Jacobian- * + * related data is not current. * + * or * + * (2) During the previous Newton corrector * + * iteration, the linear solver's solve routine * + * failed in a recoverable manner and the * + * linear solver's setup routine indicated that * + * its Jacobian-related data is not current. * + * * + * FAIL_OTHER : During the current internal step try, the * + * previous Newton iteration failed to converge * + * even though the linear solver was using current * + * Jacobian-related data. * + * * + * (3) Parameter documentation, as well as a brief description * + * of purpose, for each CVODE linear solver routine to be * + * called in cvode.c is given below the constant declarations * + * that follow. * + * * + ******************************************************************/ + +/* cv_linit return values */ + +#define LINIT_OK 0 +#define LINIT_ERR -1 + +/* Constants for convfail (input to cv_lsetup) */ + +#define NO_FAILURES 0 +#define FAIL_BAD_J 1 +#define FAIL_OTHER 2 + + +/******************************************************************* + * * + * int (*cv_linit)(CVodeMem cv_mem); * + *-----------------------------------------------------------------* + * The purpose of cv_linit is to complete initializations for * + * specific linear solver, such as counters and statistics. * + * An LInitFn should return LINIT_OK (= 0) if it has successfully * + * initialized the CVODE linear solver and LINIT_ERR (= -1) * + * otherwise. These constants are defined above. If an error does * + * occur, an appropriate message should be sent to (cv_mem->errfp).* + * * + *******************************************************************/ + +/******************************************************************* + * * + * int (*cv_lsetup)(CVodeMem cv_mem, int convfail, N_Vector ypred, * + * N_Vector fpred, booleantype *jcurPtr, * + * N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); * + *-----------------------------------------------------------------* + * The job of cv_lsetup is to prepare the linear solver for * + * subsequent calls to cv_lsolve. It may re-compute Jacobian- * + * related data is it deems necessary. Its parameters are as * + * follows: * + * * + * cv_mem - problem memory pointer of type CVodeMem. See the big * + * typedef earlier in this file. * + * * + * convfail - a flag to indicate any problem that occurred during * + * the solution of the nonlinear equation on the * + * current time step for which the linear solver is * + * being used. This flag can be used to help decide * + * whether the Jacobian data kept by a CVODE linear * + * solver needs to be updated or not. * + * Its possible values have been documented above. * + * * + * ypred - the predicted y vector for the current CVODE internal * + * step. * + * * + * fpred - f(tn, ypred). * + * * + * jcurPtr - a pointer to a boolean to be filled in by cv_lsetup. * + * The function should set *jcurPtr=TRUE if its Jacobian * + * data is current after the call and should set * + * *jcurPtr=FALSE if its Jacobian data is not current. * + * Note: If cv_lsetup calls for re-evaluation of * + * Jacobian data (based on convfail and CVODE state * + * data), it should return *jcurPtr=TRUE unconditionally;* + * otherwise an infinite loop can result. * + * * + * vtemp1 - temporary N_Vector provided for use by cv_lsetup. * + * * + * vtemp3 - temporary N_Vector provided for use by cv_lsetup. * + * * + * vtemp3 - temporary N_Vector provided for use by cv_lsetup. * + * * + * The cv_lsetup routine should return 0 if successful, * + * a positive value for a recoverable error, and a negative value * + * for an unrecoverable error. * + * * + *******************************************************************/ + +/******************************************************************* + * * + * int (*cv_lsolve)(CVodeMem cv_mem, N_Vector b, N_Vector ycur, * + * N_Vector fcur); * + *-----------------------------------------------------------------* + * cv_lsolve must solve the linear equation P x = b, where * + * P is some approximation to (I - gamma J), J = (df/dy)(tn,ycur) * + * and the RHS vector b is input. The N-vector ycur contains * + * the solver's current approximation to y(tn) and the vector * + * fcur contains the N-vector f(tn,ycur). The solution is to be * + * returned in the vector b. cv_lsolve returns a positive value * + * for a recoverable error and a negative value for an * + * unrecoverable error. Success is indicated by a 0 return value. * + * * + *******************************************************************/ + +/******************************************************************* + * * + * void (*cv_lfree)(CVodeMem cv_mem); * + *-----------------------------------------------------------------* + * cv_lfree should free up any memory allocated by the linear * + * solver. This routine is called once a problem has been * + * completed and the linear solver is no longer needed. * + * * + *******************************************************************/ + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_CVODE_H */ diff --git a/phreeqcpp/cxxKinetics.cxx b/phreeqcpp/cxxKinetics.cxx new file mode 100644 index 00000000..6da1a9f6 --- /dev/null +++ b/phreeqcpp/cxxKinetics.cxx @@ -0,0 +1,617 @@ +// cxxKinetics.cxx: implementation of the cxxKinetics class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" +#include "Phreeqc.h" +#include "cxxKinetics.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "PHRQ_io.h" +#include "Dictionary.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxKinetics::cxxKinetics(PHRQ_io *io) + // + // default constructor for cxxKinetics + // +: cxxNumKeyword(io) +{ + step_divide = 1.0; + rk = 3; + bad_step_max = 500; + use_cvode = false; + cvode_steps = 100; + cvode_order = 5; + totals.type = cxxNameDouble::ND_ELT_MOLES; + equalIncrements = false; + count = 0; +} +cxxKinetics::cxxKinetics(const std::map < int, cxxKinetics > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + step_divide = 1.0; + rk = 3; + bad_step_max = 500; + use_cvode = false; + cvode_steps = 100; + cvode_order = 5; + totals.type = cxxNameDouble::ND_ELT_MOLES; + equalIncrements = false; + count = 0; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxKinetics *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } +} + +cxxKinetics::~cxxKinetics() +{ +} + +void +cxxKinetics::dump_raw(std::ostream & s_oss, unsigned int indent, int * n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "KINETICS_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# KINETICS_MODIFY candidate identifiers #\n"; + + s_oss << indent1; + s_oss << "-step_divide " << this->step_divide << "\n"; + + s_oss << indent1; + s_oss << "-rk " << this->rk << "\n"; + + s_oss << indent1; + s_oss << "-bad_step_max " << this->bad_step_max << "\n"; + + s_oss << indent1; + s_oss << "-use_cvode " << this->use_cvode << "\n"; + + s_oss << indent1; + s_oss << "-cvode_steps " << this->cvode_steps << "\n"; + + s_oss << indent1; + s_oss << "-cvode_order " << this->cvode_order << "\n"; + + // kineticsComps structures + for (size_t k = 0; k < this->kinetics_comps.size(); k++) + { + s_oss << indent1; + s_oss << "-component " << this->kinetics_comps[k].Get_rate_name() << "\n"; + this->kinetics_comps[k].dump_raw(s_oss, indent + 2); + } + + // equal_steps + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // equal_steps + s_oss << indent1; + s_oss << "-count " << this->count << "\n"; + + // steps + s_oss << indent1; + s_oss << "-steps " << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->steps.begin(); + it != this->steps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1 << "# KINETICS workspace variables #\n"; + // totals + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + return; +} +void +cxxKinetics::read_raw(CParser & parser, bool check) +{ + + LDBLE d; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + std::vector < LDBLE > temp_steps; + + // Read kinetics number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool step_divide_defined(false); + bool rk_defined(false); + bool bad_step_max_defined(false); + bool use_cvode_defined(false); + bool cvode_steps_defined(false); + bool cvode_order_defined(false); + bool steps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in KINETICS_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // step_divide + if (!(parser.get_iss() >> this->step_divide)) + { + this->step_divide = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for step_divide.", + PHRQ_io::OT_CONTINUE); + } + step_divide_defined = true; + break; + + case 1: // rk + if (!(parser.get_iss() >> this->rk)) + { + this->rk = 3; + parser.incr_input_error(); + parser.error_msg("Expected integer value for rk.", + PHRQ_io::OT_CONTINUE); + } + rk_defined = true; + break; + + case 2: // bad_step_max + if (!(parser.get_iss() >> this->bad_step_max)) + { + this->bad_step_max = 500; + parser.incr_input_error(); + parser.error_msg("Expected integer value for bad_step_max.", + PHRQ_io::OT_CONTINUE); + } + bad_step_max_defined = true; + break; + + case 3: // use_cvode + if (!(parser.get_iss() >> this->use_cvode)) + { + this->use_cvode = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for use_cvode.", + PHRQ_io::OT_CONTINUE); + } + use_cvode_defined = true; + break; + + case 4: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxKineticsComp temp_comp(this->io); + temp_comp.Set_rate_name(str.c_str()); + cxxKineticsComp *comp_ptr = this->Find(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->kinetics_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->kinetics_comps[j].Get_rate_name().c_str(), str.c_str()) == 0) + { + this->kinetics_comps[j] = temp_comp; + } + } + } + else + { + this->kinetics_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + + case 5: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for KineticsComp totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 5; + break; + + case 6: // steps + while (parser.copy_token(token, next_char) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for steps.", + PHRQ_io::OT_CONTINUE); + } + else + { + temp_steps.push_back(d); + steps_defined = true; + } + } + opt_save = 6; + break; + + case 7: // cvode_steps + if (!(parser.get_iss() >> this->cvode_steps)) + { + this->cvode_steps = 100; + parser.incr_input_error(); + parser.error_msg("Expected integer value for cvode_steps.", + PHRQ_io::OT_CONTINUE); + } + cvode_steps_defined = true; + break; + + case 8: // cvode_order + if (!(parser.get_iss() >> this->cvode_order)) + { + this->cvode_order = 5; + parser.incr_input_error(); + parser.error_msg("Expected integer value for cvode_order.", + PHRQ_io::OT_CONTINUE); + } + cvode_order_defined = true; + + break; + case 9: // equalIncrements + case 11: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->use_cvode = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // count + if (!(parser.get_iss() >> this->count)) + { + this->count = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for count.", + PHRQ_io::OT_CONTINUE); + } + break; + + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (steps_defined) + { + this->steps = temp_steps; + } + if (check) + { + // members that must be defined + if (step_divide_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Step_divide not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (rk_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Rk not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (bad_step_max_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Bad_step_max not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (use_cvode_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Use_cvode not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cvode_steps_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Cvode_steps not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cvode_order_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Cvode_order not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +void +cxxKinetics::add(const cxxKinetics & addee, LDBLE extensive) + // + // Add to existing ppassemblage to "this" ppassemblage + // +{ + if (extensive == 0.0) + return; + for (size_t i_add = 0; i_add < addee.kinetics_comps.size(); i_add++) + { + bool found(false); + size_t i; + for (i = 0; i < this->kinetics_comps.size(); i++) + { + if (this->kinetics_comps[i].Get_rate_name() == addee.kinetics_comps[i_add].Get_rate_name()) + { + found = true; + break; + } + } + if (found) + { + this->kinetics_comps[i].add(addee.kinetics_comps[i_add], extensive); + } + else + { + cxxKineticsComp entity = addee.kinetics_comps[i_add]; + entity.multiply(extensive); + this->kinetics_comps.push_back(entity); + } + } + this->steps = addee.steps; + this->step_divide = addee.step_divide; + this->rk = addee.rk; + this->bad_step_max = addee.bad_step_max; + this->use_cvode = addee.use_cvode; + this->cvode_steps = addee.cvode_steps; + this->cvode_order = addee.cvode_order; + this->equalIncrements = addee.equalIncrements; + this->count = addee.count; +} +cxxKineticsComp * cxxKinetics:: +Find(const std::string &s) +{ + for (size_t i = 0; i < this->kinetics_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->kinetics_comps[i].Get_rate_name().c_str(), s.c_str()) == 0) + { + return & (this->kinetics_comps[i]); + } + } + return NULL; +} +int cxxKinetics:: +Get_reaction_steps(void) const +{ + if(equalIncrements) + { + return count; + } + return ((int) steps.size()); +} +LDBLE cxxKinetics:: +Current_step(bool incremental_reactions, int reaction_step) const +{ + if (this->steps.size() == 0) + return 1; + LDBLE kin_time = 1; + if (!incremental_reactions) + { + if (!this->equalIncrements) + { + if (reaction_step > (int) this->steps.size()) + { + kin_time = this->steps[this->steps.size() - 1]; + } + else + { + kin_time = this->steps[(size_t)reaction_step - 1]; + } + } + else + { + if (reaction_step > this->count) + { + kin_time = this->steps[0]; + } + else + { + kin_time = (LDBLE) reaction_step * this->steps[0] / ((LDBLE) (this->count)); + } + } + } + else + { + /* incremental reactions */ + if (!this->equalIncrements) + { + if (reaction_step > (int) this->steps.size()) + { + kin_time = this->steps[this->steps.size() - 1]; + } + else + { + kin_time = this->steps[(size_t)reaction_step - 1]; + } + } + else + { + if (reaction_step > this->count) + { + kin_time = 0; + } + else + { + kin_time = this->steps[0] / ((LDBLE) (this->count)); + } + } + } + return kin_time; +} +void +cxxKinetics::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + ints.push_back((int) this->kinetics_comps.size()); + for (size_t i = 0; i < this->kinetics_comps.size(); i++) + { + this->kinetics_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back((int) this->steps.size()); + for (size_t i = 0; i < this->steps.size(); i++) + { + doubles.push_back(this->steps[i]); + } + ints.push_back(this->count); + ints.push_back(this->equalIncrements ? 1 : 0); + doubles.push_back(this->step_divide); + ints.push_back(this->rk); + ints.push_back(this->bad_step_max); + ints.push_back(this->use_cvode ? 1 : 0); + ints.push_back(this->cvode_steps); + ints.push_back(this->cvode_order); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxKinetics::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + int n = ints[ii++]; + this->kinetics_comps.clear(); + for (int i = 0; i < n; i++) + { + cxxKineticsComp kc(this->io); + kc.Deserialize(dictionary, ints, doubles, ii, dd); + this->kinetics_comps.push_back(kc); + } + n = ints[ii++]; + this->steps.clear(); + for (int i = 0; i < n; i++) + { + this->steps.push_back(doubles[dd++]); + } + this->count = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); + this->step_divide = doubles[dd++]; + this->rk = ints[ii++]; + this->bad_step_max = ints[ii++]; + this->use_cvode = (ints[ii++] != 0); + this->cvode_steps = ints[ii++]; + this->cvode_order = ints[ii++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("step_divide"), // 0 + std::vector< std::string >::value_type("rk"), // 1 + std::vector< std::string >::value_type("bad_step_max"), // 2 + std::vector< std::string >::value_type("use_cvode"), // 3 + std::vector< std::string >::value_type("component"), // 4 + std::vector< std::string >::value_type("totals"), // 5 + std::vector< std::string >::value_type("steps"), // 6 + std::vector< std::string >::value_type("cvode_steps"), // 7 + std::vector< std::string >::value_type("cvode_order"), // 8 + std::vector< std::string >::value_type("equalincrements"), // 9 + std::vector< std::string >::value_type("count"), // 10 + std::vector< std::string >::value_type("equal_increments") // 11 +}; +const std::vector< std::string > cxxKinetics::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/cxxKinetics.h b/phreeqcpp/cxxKinetics.h new file mode 100644 index 00000000..eff885ae --- /dev/null +++ b/phreeqcpp/cxxKinetics.h @@ -0,0 +1,78 @@ +#if !defined(CXXKINETICS_H_INCLUDED) +#define CXXKINETICS_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NumKeyword.h" +#include "KineticsComp.h" +#include "PHRQ_base.h" +class cxxMix; + +class cxxKinetics:public cxxNumKeyword +{ + + public: + cxxKinetics(PHRQ_io *io=NULL); + cxxKinetics(const std::map < int, cxxKinetics > &entity_map, cxxMix & mx, + int n_user, PHRQ_io *io=NULL); + ~cxxKinetics(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int * n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + std::vector < LDBLE > &Get_steps(void) {return steps;} + const std::vector < LDBLE > &Get_steps(void)const {return steps;} + LDBLE Get_step_divide(void) const {return step_divide;} + void Set_step_divide(LDBLE t) {step_divide = t;} + int Get_rk(void) const {return rk;}; + void Set_rk(int t) {rk = t;} + int Get_bad_step_max(void) const {return bad_step_max;} + void Set_bad_step_max(int t) {bad_step_max = t;} + bool Get_use_cvode(void) const {return use_cvode;} + void Set_use_cvode(bool tf) {use_cvode = tf;} + int Get_cvode_steps(void) const {return cvode_steps;} + void Set_cvode_steps(int t) {cvode_steps = t;} + int Get_cvode_order(void) const {return cvode_order;} + void Set_cvode_order(int t) {cvode_order = t;} + std::vector < cxxKineticsComp > &Get_kinetics_comps(void) {return kinetics_comps;} + const std::vector < cxxKineticsComp > &Get_kinetics_comps(void)const {return kinetics_comps;} + cxxNameDouble & Get_totals(void) {return this->totals;} + void Set_totals(const cxxNameDouble &nd) {totals = nd;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + int Get_count(void) const {return count;} + void Set_count(int i) {count = i;} + int Get_reaction_steps(void) const; + cxxKineticsComp * Find(const std::string &str); + LDBLE Current_step(const bool incremental_reactions, const int reaction_step) const; + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + void add(const cxxKinetics & addee, LDBLE extensive); + + protected: + // KINETICS_MODIFY candidates + std::vector < cxxKineticsComp > kinetics_comps; + std::vector < LDBLE >steps; + int count; + bool equalIncrements; + LDBLE step_divide; + int rk; + int bad_step_max; + bool use_cvode; + int cvode_steps; + int cvode_order; + // internal variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(CXXKINETICS_H_INCLUDED) diff --git a/phreeqcpp/cxxMix.cxx b/phreeqcpp/cxxMix.cxx new file mode 100644 index 00000000..8cd8e0be --- /dev/null +++ b/phreeqcpp/cxxMix.cxx @@ -0,0 +1,154 @@ +// Mix.cxx: implementation of the cxxMix class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Parser.h" +#include "cxxMix.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxMix::cxxMix(PHRQ_io *io) + // + // default constructor for cxxMix + // +: cxxNumKeyword(io) +{ +} + +cxxMix::~cxxMix() +{ +} + +void +cxxMix::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Mix element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "MIX_RAW " << n_user_local << " " << this->description << "\n"; + + for (std::map < int, LDBLE >::const_iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + s_oss << indent1 << it->first << " " << it->second << "\n"; + } +} + +void +cxxMix::read_raw(CParser & parser) +{ + + int i; + LDBLE d; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read mix number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_DEFAULT; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in MIX_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case CParser::OPT_DEFAULT: // solution number, mix fraction + if (parser.copy_token(token, next_char) != CParser::TT_EMPTY) + { + std::istringstream iss(token); + if (!(iss >> i)) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected integer value for solution number.", + PHRQ_io::OT_CONTINUE); + break; + } + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected numeric value for solution fraction.", + PHRQ_io::OT_CONTINUE); + break; + } + this->mixComps[i] = d; + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} +void cxxMix::Vectorize(std::vector &n, std::vector &f) +{ + n.clear(); + f.clear(); + for (std::map < int, LDBLE >::const_iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + n.push_back(it->first); + f.push_back(it->second); + } +} +const std::vector< std::string > cxxMix::vopts; diff --git a/phreeqcpp/cxxMix.h b/phreeqcpp/cxxMix.h new file mode 100644 index 00000000..6d27a2fd --- /dev/null +++ b/phreeqcpp/cxxMix.h @@ -0,0 +1,58 @@ +#if !defined(CXXMIX_H_INCLUDED) +#define CXXMIX_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "NumKeyword.h" +#include "PHRQ_base.h" +#include "phrqtype.h" + +class cxxMix:public cxxNumKeyword +{ + + public: + cxxMix(PHRQ_io *io=NULL); + ~cxxMix(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser); + + void Add(int n, LDBLE f) + { + if (this->mixComps.find(n) != this->mixComps.end()) + { + mixComps[n] += f; + } + else + { + mixComps[n] = f; + } + } + void Multiply(LDBLE f) + { + for (std::map < int, LDBLE >::iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + it->second *= f; + } + } + + const std::map < int, LDBLE > & Get_mixComps() const + { + return mixComps; + } + void Vectorize(std::vector &n, std::vector &f); + protected: + friend class cxxStorageBin; + std::map < int, LDBLE >mixComps; + const static std::vector < std::string > vopts; + +}; + +#endif // !defined(CXXMIX_H_INCLUDED) diff --git a/phreeqcpp/dense.cpp b/phreeqcpp/dense.cpp new file mode 100644 index 00000000..5c25e8fe --- /dev/null +++ b/phreeqcpp/dense.cpp @@ -0,0 +1,175 @@ +/************************************************************************** + * * + * File : dense.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic DENSE linear * + * solver package. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include +#include +#include "sundialstypes.h" +#include "sundialsmath.h" +#include "dense.h" +#include "smalldense.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) + + +/* Implementation */ + +DenseMat +DenseAllocMat(integertype N) +{ + DenseMat A; + + if (N <= 0) + return (NULL); + + A = (DenseMat) malloc(sizeof *A); + if (A == NULL) + return (NULL); + + A->data = denalloc(N); + if (A->data == NULL) + { + free(A); + return (NULL); + } + + A->size = N; + + return (A); +} + + +integertype * +DenseAllocPiv(integertype N) +{ + if (N <= 0) + return (NULL); + + return ((integertype *) malloc(N * sizeof(integertype))); +} + + +integertype +DenseFactor(DenseMat A, integertype * p) +{ + return (gefa(A->data, A->size, p)); +} + + +void +DenseBacksolve(DenseMat A, integertype * p, realtype * b) +{ + gesl(A->data, A->size, p, b); +} + + +void +DenseZero(DenseMat A) +{ + denzero(A->data, A->size); +} + +void +DenseCopy(DenseMat A, DenseMat B) +{ + dencopy(A->data, B->data, A->size); +} + +void +DenseScale(realtype c, DenseMat A) +{ + denscale(c, A->data, A->size); +} + +void +DenseAddI(DenseMat A) +{ + denaddI(A->data, A->size); +} + +void +DenseFreeMat(DenseMat A) +{ + denfree(A->data); + free(A); +} + +void +DenseFreePiv(integertype * p) +{ + free(p); +} + +void +DensePrint(DenseMat A) +{ + denprint(A->data, A->size); +} diff --git a/phreeqcpp/dense.h b/phreeqcpp/dense.h new file mode 100644 index 00000000..2e23263f --- /dev/null +++ b/phreeqcpp/dense.h @@ -0,0 +1,341 @@ +#ifndef _INC_DENSE_H +#define _INC_DENSE_H +/************************************************************************** + * * + * File : dense.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a generic DENSE linear solver * + * package. The routines listed in this file all use type * + * DenseMat, defined below, for matrices. These routines in turn * + * call routines in the smalldense.h/smalldense.c module, which * + * use the type realtype** for matrices. This separation allows * + * for possible modifications in which matrices of type DenseMat * + * may not be stored contiguously, while small matrices can still * + * be treated with the routines in smalldense. * + * * + * Routines that work with the type DenseMat begin with "Dense". * + * The DenseAllocMat function allocates a dense matrix for use in * + * the other DenseMat routines listed in this file. Matrix * + * storage details are given in the documentation for the type * + * DenseMat. The DenseAllocPiv function allocates memory for * + * pivot information. The storage allocated by DenseAllocMat and * + * DenseAllocPiv is deallocated by the routines DenseFreeMat and * + * DenseFreePiv, respectively. The DenseFactor and DenseBacksolve * + * routines perform the actual solution of a dense linear system. * + * * + * Routines that work with realtype** begin with "den" (except for * + * the factor and solve routines which are called gefa and gesl, * + * respectively). The underlying matrix storage is described in * + * the documentation for denalloc in smalldense.h * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _dense_h +#define _dense_h + + +#include "sundialstypes.h" +#include "smalldense.h" + + +/****************************************************************** + * * + * Type: DenseMat * + *----------------------------------------------------------------* + * The type DenseMat is defined to be a pointer to a structure * + * with a size and a data field. The size field indicates the * + * number of columns (== number of rows) of a dense matrix, while * + * the data field is a two dimensional array used for component * + * storage. The elements of a dense matrix are stored columnwise * + * (i.e columns are stored one on top of the other in memory). If * + * A is of type DenseMat, then the (i,j)th element of A (with * + * 0 <= i,j <= size-1) is given by the expression (A->data)[j][i] * + * or by the expression (A->data)[0][j*n+i]. The macros below * + * allow a user to access efficiently individual matrix * + * elements without writing out explicit data structure * + * references and without knowing too much about the underlying * + * element storage. The only storage assumption needed is that * + * elements are stored columnwise and that a pointer to the jth * + * column of elements can be obtained via the DENSE_COL macro. * + * Users should use these macros whenever possible. * + * * + ******************************************************************/ + + typedef struct _DenseMat + { + integertype size; + realtype **data; + } *DenseMat; + + +/* DenseMat accessor macros */ + + +/****************************************************************** + * * + * Macro : DENSE_ELEM * + * Usage : DENSE_ELEM(A,i,j) = a_ij; OR * + * a_ij = DENSE_ELEM(A,i,j); * + *----------------------------------------------------------------* + * DENSE_ELEM(A,i,j) references the (i,j)th element of the N by N * + * DenseMat A, 0 <= i,j <= N-1. * + * * + ******************************************************************/ + +#define DENSE_ELEM(A,i,j) ((A->data)[j][i]) + + +/****************************************************************** + * * + * Macro : DENSE_COL * + * Usage : col_j = DENSE_COL(A,j); * + *----------------------------------------------------------------* + * DENSE_COL(A,j) references the jth column of the N by N * + * DenseMat A, 0 <= j <= N-1. The type of the expression * + * DENSE_COL(A,j) is realtype *. After the assignment in the usage* + * above, col_j may be treated as an array indexed from 0 to N-1. * + * The (i,j)th element of A is referenced by col_j[i]. * + * * + ******************************************************************/ + +#define DENSE_COL(A,j) ((A->data)[j]) + + +/* Functions that use the DenseMat representation for a dense matrix */ + + +/****************************************************************** + * * + * Function : DenseAllocMat * + * Usage : A = DenseAllocMat(N); * + * if (A == NULL) ... memory request failed * + *----------------------------------------------------------------* + * DenseAllocMat allocates memory for an N by N dense matrix and * + * returns the storage allocated (type DenseMat). DenseAllocMat * + * returns NULL if the request for matrix storage cannot be * + * satisfied. See the above documentation for the type DenseMat * + * for matrix storage details. * + * * + ******************************************************************/ + + DenseMat DenseAllocMat(integertype N); + + +/****************************************************************** + * * + * Function : DenseAllocPiv * + * Usage : p = DenseAllocPiv(N); * + * if (p == NULL) ... memory request failed * + *----------------------------------------------------------------* + * DenseAllocPiv allocates memory for pivot information to be * + * filled in by the DenseFactor routine during the factorization * + * of an N by N dense matrix. The underlying type for pivot * + * information is an array of N integers and this routine returns * + * the pointer to the memory it allocates. If the request for * + * pivot storage cannot be satisfied, DenseAllocPiv returns NULL. * + * * + ******************************************************************/ + + integertype *DenseAllocPiv(integertype N); + + +/****************************************************************** + * * + * Function : DenseFactor * + * Usage : ier = DenseFactor(A, p); * + * if (ier != 0) ... A is singular * + *----------------------------------------------------------------* + * DenseFactor performs the LU factorization of the N by N dense * + * matrix A. This is done using standard Gaussian elimination * + * with partial pivoting. * + * * + * A successful LU factorization leaves the matrix A and the * + * pivot array p with the following information: * + * * + * (1) p[k] contains the row number of the pivot element chosen * + * at the beginning of elimination step k, k=0, 1, ..., N-1. * + * * + * (2) If the unique LU factorization of A is given by PA = LU, * + * where P is a permutation matrix, L is a lower triangular * + * matrix with all 1's on the diagonal, and U is an upper * + * triangular matrix, then the upper triangular part of A * + * (including its diagonal) contains U and the strictly lower * + * triangular part of A contains the multipliers, I-L. * + * * + * DenseFactor returns 0 if successful. Otherwise it encountered * + * a zero diagonal element during the factorization. In this case * + * it returns the column index (numbered from one) at which * + * it encountered the zero. * + * * + ******************************************************************/ + + integertype DenseFactor(DenseMat A, integertype * p); + + +/****************************************************************** + * * + * Function : DenseBacksolve * + * Usage : DenseBacksolve(A, p, b); * + *----------------------------------------------------------------* + * DenseBacksolve solves the N-dimensional system A x = b using * + * the LU factorization in A and the pivot information in p * + * computed in DenseFactor. The solution x is returned in b. This * + * routine cannot fail if the corresponding call to DenseFactor * + * did not fail. * + * * + ******************************************************************/ + + void DenseBacksolve(DenseMat A, integertype * p, realtype * b); + + +/****************************************************************** + * * + * Function : DenseZero * + * Usage : DenseZero(A); * + *----------------------------------------------------------------* + * DenseZero sets all the elements of the N by N matrix A to 0.0. * + * * + ******************************************************************/ + + void DenseZero(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseCopy * + * Usage : DenseCopy(A, B); * + *----------------------------------------------------------------* + * DenseCopy copies the contents of the N by N matrix A into the * + * N by N matrix B. * + * * + ******************************************************************/ + + void DenseCopy(DenseMat A, DenseMat B); + + +/****************************************************************** + * * + * Function: DenseScale * + * Usage : DenseScale(c, A); * + *----------------------------------------------------------------* + * DenseScale scales the elements of the N by N matrix A by the * + * constant c and stores the result back in A. * + * * + ******************************************************************/ + + void DenseScale(realtype c, DenseMat A); + + +/****************************************************************** + * * + * Function : DenseAddI * + * Usage : DenseAddI(A); * + *----------------------------------------------------------------* + * DenseAddI adds the identity matrix to A and stores the result * + * back in A. * + * * + ******************************************************************/ + + void DenseAddI(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseFreeMat * + * Usage : DenseFreeMat(A); * + *----------------------------------------------------------------* + * DenseFreeMat frees the memory allocated by DenseAllocMat for * + * the N by N matrix A. * + * * + ******************************************************************/ + + void DenseFreeMat(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseFreePiv * + * Usage : DenseFreePiv(p); * + *----------------------------------------------------------------* + * DenseFreePiv frees the memory allocated by DenseAllocPiv for * + * the pivot information array p. * + * * + ******************************************************************/ + + void DenseFreePiv(integertype * p); + + +/****************************************************************** + * * + * Function : DensePrint * + * Usage : DensePrint(A); * + *----------------------------------------------------------------* + * This routine prints the N by N dense matrix A to standard * + * output as it would normally appear on paper. It is intended * + * as a debugging tool with small values of N. The elements are * + * printed using the %g option. A blank line is printed before * + * and after the matrix. * + * * + ******************************************************************/ + + void DensePrint(DenseMat A); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_DENSE_H */ diff --git a/phreeqcpp/dumper.cpp b/phreeqcpp/dumper.cpp new file mode 100644 index 00000000..edc897e6 --- /dev/null +++ b/phreeqcpp/dumper.cpp @@ -0,0 +1,277 @@ +#include // std::replace + +#include "dumper.h" +#include "Parser.h" +#include "PHRQ_io.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +dumper::dumper(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->file_name = "dump.out"; + this->append = false; + this->on = false; +} +dumper::dumper(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->file_name = "dump.out"; + this->append = false; + this->Read(parser); +} + +dumper::~dumper(void) +{ +} + +void dumper::SetAll(bool tf) +{ + this->binList.SetAll(tf); + +} +bool dumper::Read(CParser & parser) +{ + bool return_value(true); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_DEFAULT; + bool cleared_once = false; + this->on = true; + + for (;;) + { + int opt; + StorageBinListItem cells; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + if (opt > 1 && !cleared_once) + { + binList.SetAll(false); + cleared_once = true; + } + // Select StorageBinListItem + StorageBinListItem *item = NULL; + switch (opt) + { + case 3: // cell + case 4: // cells + item = &cells; + break; + case 5: + case 6: + item = &(this->binList.Get_solution()); + break; + case 7: + case 8: + case 9: + case 10: + item = &(this->binList.Get_pp_assemblage()); + break; + case 11: + item = &(this->binList.Get_exchange()); + break; + case 12: + item = &(this->binList.Get_surface()); + break; + case 13: + case 14: + case 15: + item = &(this->binList.Get_ss_assemblage()); + break; + case 16: + case 17: + item = &(this->binList.Get_gas_phase()); + break; + case 18: + item = &(this->binList.Get_kinetics()); + break; + + case 19: // mix + item = &(this->binList.Get_mix()); + break; + case 20: // reaction + case 21: // reactions + item = &(this->binList.Get_reaction()); + break; + case 22: // temperature + case 23: // reaction_temperature + case 24: // reaction_temperatures + item = &(this->binList.Get_temperature()); + break; + case 25: // pressure + case 26: // reaction_pressure + case 27: // reaction_pressures + item = &(this->binList.Get_pressure()); + break; + default: + break; + } + + // Read dump entity list of numbers or number ranges for line, store in item + if ((opt > 2)) + { + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (item && j == CParser::TT_DIGIT) + { + item->Augment(token); + } + else if (item && j == CParser::TT_EMPTY) + { + item->Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + } + } + } + + if (opt == 3 || opt == 4) + { + this->binList.TransferAll(cells); + } + // Process other identifiers + std::set < int >::iterator it; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + break; + case 0: //file + std::getline(parser.get_iss(), this->file_name); + trim(this->file_name); + if (this->file_name.size() == 0) + { + this->file_name = "dump.out"; + } + + break; + case 1: //append + { + parser.copy_token(token, next_char); + //if (!(parser.get_iss() >> this->append)) + this->append = true; + if (token.c_str()[0] == 'f' || token.c_str()[0] == 'F') + { + this->append = false; + } + } + break; + case 2: //all + this->SetAll(true); + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading DUMP definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + return(return_value); +} + +bool dumper::Get_bool_any(void) +{ + return ( + Get_bool_solution() || + Get_bool_pp_assemblage() || + Get_bool_exchange() || + Get_bool_surface() || + Get_bool_ss_assemblage() || + Get_bool_gas_phase() || + Get_bool_kinetics() || + Get_bool_mix() || + Get_bool_reaction() || + Get_bool_temperature() || + Get_bool_pressure() + ); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("file"), // 0 + std::vector< std::string >::value_type("append"), // 1 + std::vector< std::string >::value_type("all"), // 2 + std::vector< std::string >::value_type("cell"), // 3 + std::vector< std::string >::value_type("cells"), // 4 + std::vector< std::string >::value_type("solution"), // 5 + std::vector< std::string >::value_type("solutions"), // 6 + std::vector< std::string >::value_type("pp_assemblage"), // 7 + std::vector< std::string >::value_type("pp_assemblages"), // 8 + std::vector< std::string >::value_type("equilibrium_phase"), // 9 + std::vector< std::string >::value_type("equilibrium_phases"), // 10 + std::vector< std::string >::value_type("exchange"), // 11 + std::vector< std::string >::value_type("surface"), // 12 + std::vector< std::string >::value_type("ss_assemblage"), // 13 + std::vector< std::string >::value_type("solid_solution"), // 14 + std::vector< std::string >::value_type("solid_solutions"), // 15 + std::vector< std::string >::value_type("gas_phase"), // 16 + std::vector< std::string >::value_type("gas_phases"), // 17 + std::vector< std::string >::value_type("kinetics"), // 18 + std::vector< std::string >::value_type("mix"), // 19 + std::vector< std::string >::value_type("reaction"), // 20 + std::vector< std::string >::value_type("reactions"), // 21 + std::vector< std::string >::value_type("temperature"), // 22 + std::vector< std::string >::value_type("reaction_temperature"), // 23 + std::vector< std::string >::value_type("reaction_temperatures"), // 24 + std::vector< std::string >::value_type("pressure"), // 25 + std::vector< std::string >::value_type("reaction_pressure"), // 26 + std::vector< std::string >::value_type("reaction_pressures") // 27 +}; +const std::vector< std::string > dumper::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/dumper.h b/phreeqcpp/dumper.h new file mode 100644 index 00000000..7ff0aac0 --- /dev/null +++ b/phreeqcpp/dumper.h @@ -0,0 +1,60 @@ +#if !defined(DUMPER_H_INCLUDED) +#define DUMPER_H_INCLUDED +#include // std::set +#include // std::string +#include // std::list +#include // std::vector +#include "StorageBinList.h" +class CParser; + +class dumper: public PHRQ_base +{ +public: + dumper(PHRQ_io *io=NULL); + dumper(CParser & parser, PHRQ_io *io=NULL); + virtual ~dumper(void); + bool Read(CParser & parser); + void SetAll(bool tf); + std::string Get_file_name(void) { return(this->file_name); }; + void Set_file_name(std::string fn) { this->file_name = fn; }; + bool Get_append(void)const { return(this->append); }; + void Set_append(bool app) { this->append = app; }; + bool Get_bool_solution(void) { return(this->binList.Get_solution().Get_defined()); }; + bool Get_bool_pp_assemblage(void) { return(this->binList.Get_pp_assemblage().Get_defined()); }; + bool Get_bool_exchange(void) { return(this->binList.Get_exchange().Get_defined()); }; + bool Get_bool_surface(void) { return(this->binList.Get_surface().Get_defined()); }; + bool Get_bool_ss_assemblage(void) { return(this->binList.Get_ss_assemblage().Get_defined()); }; + bool Get_bool_gas_phase(void) { return(this->binList.Get_gas_phase().Get_defined()); }; + bool Get_bool_kinetics(void) { return(this->binList.Get_kinetics().Get_defined()); }; + bool Get_bool_mix(void) { return(this->binList.Get_mix().Get_defined()); }; + bool Get_bool_reaction(void) { return(this->binList.Get_reaction().Get_defined()); }; + bool Get_bool_temperature(void) { return(this->binList.Get_temperature().Get_defined()); }; + bool Get_bool_pressure(void) { return(this->binList.Get_pressure().Get_defined()); }; + bool Get_bool_any(void); + + std::set < int > & Get_solution(void) { return(this->binList.Get_solution().Get_numbers()); }; + std::set < int > & Get_pp_assemblage(void) { return(this->binList.Get_pp_assemblage().Get_numbers()); }; + std::set < int > & Get_exchange(void) { return(this->binList.Get_exchange().Get_numbers()); }; + std::set < int > & Get_surface(void) { return(this->binList.Get_surface().Get_numbers()); }; + std::set < int > & Get_ss_assemblage(void) { return(this->binList.Get_ss_assemblage().Get_numbers()); }; + std::set < int > & Get_gas_phase(void) { return(this->binList.Get_gas_phase().Get_numbers()); }; + std::set < int > & Get_kinetics(void) { return(this->binList.Get_kinetics().Get_numbers()); }; + std::set < int > & Get_mix(void) { return(this->binList.Get_mix().Get_numbers()); }; + std::set < int > & Get_reaction(void) { return(this->binList.Get_reaction().Get_numbers()); }; + std::set < int > & Get_temperature(void) { return(this->binList.Get_temperature().Get_numbers()); }; + std::set < int > & Get_pressure(void) { return(this->binList.Get_pressure().Get_numbers()); }; + bool Get_on(void) { return this->on; }; + void Set_on(bool tf) { this->on = tf; }; + + StorageBinList & Get_StorageBinList(void) { return this->binList; }; + const StorageBinList & Get_StorageBinList(void)const { return this->binList; }; + void Set_StorageBinList(StorageBinList sbl) { this->binList = sbl; }; +protected: + std::string file_name; + bool append; + bool on; + StorageBinList binList; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(DUMPER_H_INCLUDED) diff --git a/phreeqcpp/gases.cpp b/phreeqcpp/gases.cpp new file mode 100644 index 00000000..d64d2c97 --- /dev/null +++ b/phreeqcpp/gases.cpp @@ -0,0 +1,697 @@ +#include "Phreeqc.h" +#include "GasPhase.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_fixed_volume_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for gas phase unknown (sum of partial pressures) + * in unknown structure + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); +/* + * One for each gas component + */ + gas_unknowns.clear(); + gas_unknown = NULL; + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_phase_name().c_str(), &j, FALSE); + x[count_unknowns]->type = GAS_MOLES; + x[count_unknowns]->description = phase_ptr->name; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->moles = comp_ptr->Get_moles(); + if (x[count_unknowns]->moles <= 0) + { + x[count_unknowns]->moles = MIN_TOTAL; + } + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + gas_unknowns.push_back(x[count_unknowns]); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + x[count_unknowns]->moles); + x[count_unknowns]->phase->moles_x = x[count_unknowns]->moles; + count_unknowns++; + } + if (gas_unknowns.size() > 0) + { + gas_unknown = gas_unknowns[0]; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_fixed_volume_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * sum of partial pressures equation and + * mass balance equations for elements contained in gases + */ + size_t row, col; + class master *master_ptr; + class rxn_token *rxn_ptr; + class unknown *unknown_ptr; + LDBLE coef, coef_elt; + + if (gas_unknown == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_phase_name().c_str(), &j, FALSE); +/* + * Determine elements in gas component + */ + count_elts = 0; + paren_count = 0; + if (phase_ptr->rxn_x.token.size() == 0) + continue; + add_elt_list(phase_ptr->next_elt, 1.0); +#define COMBINE +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif +/* + * Build mass balance sums for each element in gas + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations %s.\n", + phase_ptr->name)); + } + + /* All elements in gas */ + for (j = 0; j < (int) count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr != NULL) + { + coef = elt_list[j].coef; + store_mb(&(gas_unknowns[i]->moles), &(unknown_ptr->f), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + unknown_ptr->description, (double) coef)); + } + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* Total pressure of gases */ + store_mb(&(phase_ptr->p_soln_x), &(gas_unknown->f), + 1.0); + } +/* + * Build jacobian sums for mass balance equations + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tJacobian summations %s.\n\n", + phase_ptr->name)); + } + for (j = 0; j < (int) count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr == NULL) + { + continue; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\t%s.\n", + unknown_ptr->description)); + } + row = unknown_ptr->number * (count_unknowns + 1); + coef_elt = elt_list[j].coef; + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + master_ptr->s->la = -999.0; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", + master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + col = master_ptr->unknown->number; + coef = coef_elt * rxn_ptr->coef; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + store_jacob(&(gas_unknowns[i]->moles), + &(my_array[(size_t)row + (size_t)col]), coef); + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* derivative wrt total moles of gas */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + "gas moles", (double) elt_list[j].coef, + row / (count_unknowns + 1), + gas_unknown->number)); + } + store_jacob(&(phase_ptr->fraction_x), + &(my_array[(size_t)row + (size_t)gas_unknown->number]), coef_elt); + } + } +/* + * Build jacobian sums for sum of partial pressures equation + */ + if (gas_phase_ptr->Get_type() != cxxGasPhase::GP_PRESSURE) + continue; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tPartial pressure eqn %s.\n\n", + phase_ptr->name)); + } + unknown_ptr = gas_unknown; + row = unknown_ptr->number * (count_unknowns + 1); + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s != s_eminus && rxn_ptr->s->in == FALSE) + { + error_string = sformatf( + "Element in species, %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + warning_msg(error_string); + } + else + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + if (master_ptr && master_ptr->s) + { + master_ptr->s->la = -999.0; + } + } + + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species for %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + assert(false); + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + warning_msg(error_string); + } + col = master_ptr->unknown->number; + coef = rxn_ptr->coef; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + store_jacob(&(phase_ptr->p_soln_x), &(my_array[(size_t)row + (size_t)col]), coef); + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_PR(void) +/* ---------------------------------------------------------------------- */ +/* Calculate fugacity and fugacity coefficient for gas pressures if critical T and P + are defined. + 1) Solve molar volume V_m or total pressure P from Peng-Robinson's EOS: + P = R * T / (V_m - b) - a * aa / (V_m^2 + 2 * b * V_m - b^2) + a = 0.457235 * (R * T_c)^2 / P_c + b = 0.077796 * R * T_c / P_c + aa = (1 + kk * (1 - T_r^0.5))^2 + kk = 0.37464 + 1.54226 * omega - 0.26992 * omega^2 + T_r = T / T_c + multicomponent gas phase: + use: b_sum = Sum(x_i * b), x_i is mole-fraction + a_aa_sum = Sum_i( Sum_j(x_i * x_j * (a_i * aa_i * a_j * aa_j)^0.5) ) + 2) Find the fugacity coefficient phi for gas i: + log(phi_i) = B_ratio * (z - 1) - log(z - B) + A / (2.8284 * B) * (B_ratio - 2 / a_aa_sum * a_aa_sum2) *\ + log((z + 2.4142 * B) / (z - 0.4142 * B)) + B_ratio = b_i / b_sum + A = a_aa_sum * P / R_TK^2 + B = b_sum * P / R_TK + a_aa_sum2 = Sum_j(x_j * (a_aa_i * a_aa_j)^0.5 + 3) correct the solubility of gas i with: + pr_si_f = log10(phi_i) - Delta_V_i * (P - 1) / (2.303 * R * TK); +*/ +{ + LDBLE T_c, P_c; + LDBLE A, B, B_r, /*b2,*/ kk, oo, a_aa, T_r; + LDBLE m_sum, /*b_sum, a_aa_sum,*/ a_aa_sum2; + LDBLE phi; + LDBLE /*R_TK,*/ R = R_LITER_ATM; /* L atm / (K mol) */ + LDBLE r3[4], r3_12, rp, rp3, rq, rz, ri, ri1, one_3 = 0.33333333333333333; + LDBLE disct, vinit, v1, ddp, dp_dv, dp_dv2; + int it; + class phase *phase_ptr; + LDBLE V_m = 0, P = 0; + + LDBLE TK = tk_x; + bool halved; + R_TK = R * TK; + m_sum = b_sum = a_aa_sum = 0.0; + size_t i; + + if (gas_unknowns.size() == 0) + { + error_msg("No gas unknowns.", STOP); + } + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + + for (i = 0; i < gas_unknowns.size(); i++) + { + m_sum += gas_unknowns[i]->moles; + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + error_msg("Cannot calculate a mixture of ideal and Peng_Robinson gases,\n please define Tc and Pc for the active gases in PHASES.", STOP); + //continue; + if (!phase_ptr->pr_a) + { + T_c = phase_ptr->t_c; + P_c = phase_ptr->p_c; + phase_ptr->pr_a = 0.457235 * R * R * T_c * T_c / P_c; + phase_ptr->pr_b = 0.077796 * R * T_c / P_c; + T_r = TK / T_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; + phase_ptr->pr_in = true; + } + if (phase_ptr->pr_tk != TK) + { + T_r = TK / phase_ptr->t_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; + phase_ptr->pr_in = true; + } + } + if (m_sum == 0) + return (OK); + gas_phase_ptr->Set_v_m(gas_phase_ptr->Get_volume() / m_sum); + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + phase_ptr->fraction_x = gas_unknowns[i]->moles / m_sum; // phase_ptr->fraction_x updated + } + + for (i = 0; i < gas_unknowns.size(); i++) + { + a_aa_sum2 = 0.0; + phase_ptr = gas_unknowns[i]->phase; + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + // continue; + b_sum += phase_ptr->fraction_x * phase_ptr->pr_b; + size_t i1; + class phase *phase_ptr1; + for (i1 = 0; i1 < gas_unknowns.size(); i1++) + { + phase_ptr1 = gas_unknowns[i1]->phase; + //if (phase_ptr1->t_c == 0.0 || phase_ptr1->p_c == 0.0) + // continue; + if (phase_ptr1->fraction_x == 0) + continue; + a_aa = sqrt(phase_ptr->pr_a * phase_ptr->pr_alpha * + phase_ptr1->pr_a * phase_ptr1->pr_alpha); + if (!strcmp(phase_ptr->name, "H2O(g)")) + { + if (!strcmp(phase_ptr1->name, "CO2(g)")) + a_aa *= 0.81; // Soreide and Whitson, 1992, FPE 77, 217 + else if (!strcmp(phase_ptr1->name, "H2S(g)") || !strcmp(phase_ptr1->name, "H2Sg(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr1->name, "CH4(g)") || !strcmp(phase_ptr1->name, "Mtg(g)") || !strcmp(phase_ptr1->name, "Methane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "N2(g)") || !strcmp(phase_ptr1->name, "Ntg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "Ethane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "Propane(g)")) + a_aa *= 0.45; + } + if (!strcmp(phase_ptr1->name, "H2O(g)")) + { + if (!strcmp(phase_ptr->name, "CO2(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "H2S(g)") || !strcmp(phase_ptr->name, "H2Sg(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "CH4(g)") || !strcmp(phase_ptr->name, "Mtg(g)") || !strcmp(phase_ptr->name, "Methane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "N2(g)") || !strcmp(phase_ptr->name, "Ntg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Ethane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Propane(g)")) + a_aa *= 0.45; + } + a_aa_sum += phase_ptr->fraction_x * phase_ptr1->fraction_x * a_aa; + a_aa_sum2 += phase_ptr1->fraction_x * a_aa; + } + phase_ptr->pr_aa_sum2 = a_aa_sum2; + } + b2 = b_sum * b_sum; + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + V_m = gas_phase_ptr->Get_volume() / m_sum; + P = 0.0; + while (P <= 0) + { + P = R_TK / (V_m - b_sum) - a_aa_sum / (V_m * (V_m + 2 * b_sum) - b2); + if (P <= 0.0) + { + V_m *= 2.0; + //a_aa_sum /= 2.0; + } + } + if (iterations > 0 && P < 150 && V_m < 1.01) + { + // check for 3-roots... + r3[1] = b_sum - R_TK / P; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // the discriminant of the cubic eqn... + disct = 18. * r3[1] * r3[2] * r3[3] - + 4. * pow(r3[1], 3) * r3[3] + + r3[1] * r3[1] * r3[2] * r3[2] - + 4. * pow(r3[2], 3) - + 27. * r3[3] * r3[3]; + if (disct > 0) + { + // 3-roots, find the largest P... + it = 0; + halved = false; + ddp = 1e-9; + v1 = vinit = 0.729; + dp_dv = f_Vm(v1, this); + while (fabs(dp_dv) > 1e-11 && it < 40) + { + it +=1; + dp_dv2 = f_Vm(v1 - ddp, this); + v1 -= (dp_dv * ddp / (dp_dv - dp_dv2)); + if (!halved && (v1 > vinit || v1 < 0.03)) + { + if (vinit > 0.329) + vinit -= 0.1; + else + vinit -=0.05; + if (vinit < 0.03) + { + vinit = halve(f_Vm, 0.03, 1.0, 1e-3); + if (f_Vm(vinit - 2e-3, this) < 0) + vinit = halve(f_Vm, vinit + 2e-3, 1.0, 1e-3); + halved = true; + } + v1 = vinit; + } + dp_dv = f_Vm(v1, this); + if (fabs(dp_dv) < 1e-11) + { + if (f_Vm(v1 - 1e-4, this) < 0) + { + v1 = halve(f_Vm, v1 + 1e-4, 1.0, 1e-3); + dp_dv = f_Vm(v1, this); + } + } + } + if (it == 40) + { +// accept a (possible) whobble in the curve... +// error_msg("No convergence when calculating P in Peng-Robinson.", STOP); + } + if (V_m < v1 && it < 40) + P = R_TK / (v1 - b_sum) - a_aa_sum / (v1 * (v1 + 2 * b_sum) - b2); + } + } + if (P <= 0) // iterations = -1 + P = 1.; + gas_phase_ptr->Set_total_p(P); // phase_ptr->total_p updated + gas_phase_ptr->Set_v_m(V_m); + } + else + { + assert(false); + P = gas_phase_ptr->Get_total_p(); + r3[1] = b_sum - R_TK / P; + r3_12 = r3[1] * r3[1]; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // solve t^3 + rp*t + rq = 0. + // molar volume V_m = t - r3[1] / 3... + rp = r3[2] - r3_12 / 3; + rp3 = rp * rp * rp; + rq = (2.0 * r3_12 * r3[1] - 9.0 * r3[1] * r3[2]) / 27 + r3[3]; + rz = rq * rq / 4 + rp3 / 27; + if (rz >= 0) // Cardono's method... + { + ri = sqrt(rz); + if (ri + rq / 2 <= 0) + { + V_m = pow(ri - rq / 2, one_3) + pow(- ri - rq / 2, one_3) - r3[1] / 3; + } else + { + ri = - pow(ri + rq / 2, one_3); + V_m = ri - rp / (3.0 * ri) - r3[1] / 3; + } + } else // use complex plane... + { + ri = sqrt(- rp3 / 27); // rp < 0 + ri1 = acos(- rq / 2 / ri); + V_m = 2.0 * pow(ri, one_3) * cos(ri1 / 3) - r3[1] / 3; + } + gas_phase_ptr->Set_v_m(V_m); // phase_ptr->fraction_x updated + } + // calculate the fugacity coefficients... + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->fraction_x == 0.0) + { + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1; + phase_ptr->pr_si_f = 0.0; + //phase_ptr->logk[vm0] = 0.0; // *** + continue; + } + phase_ptr->pr_p = phase_ptr->fraction_x * P; + + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + //{ + // phase_ptr->pr_phi = 1; + // continue; + //} + rz = P * V_m / R_TK; + A = a_aa_sum * P / (R_TK * R_TK); + B = b_sum * P / R_TK; + B_r = phase_ptr->pr_b / b_sum; + if (rz > B) + { + phi = B_r * (rz - 1) - log(rz - B) + A / (2.828427 * B) * (B_r - 2.0 * phase_ptr->pr_aa_sum2 / a_aa_sum) * + log((rz + 2.41421356 * B) / (rz - 0.41421356 * B)); + //phi = (phi > 4.44 ? 4.44 : (phi < -3 ? -3 : phi)); + } + else + phi = -3.0; // fugacity coefficient = 0.05 + phase_ptr->pr_phi = exp(phi); + phase_ptr->pr_si_f = phi / LOG_10; // pr_si_f updated + // **** + //phase_ptr->logk[vm0] = V_m * 1e3 * phase_ptr->fraction_x; + phase_ptr->pr_in = true; + } + return (V_m); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_fixed_volume_gas_pressures(void) +/* ---------------------------------------------------------------------- */ +{ + int n_g = 0; + LDBLE lp; + class rxn_token *rxn_ptr; + class phase *phase_ptr; + bool PR = false, pr_done = false; + size_t i; +/* + * moles and partial pressures for gases + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + gas_phase_ptr->Set_total_moles(0); + + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->in == TRUE) + { + if (!PR && phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + n_g++; + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gas_unknowns[i]->moles); + } + if (PR && gas_phase_ptr->Get_total_moles() > 0) + { + calc_PR(); + pr_done = true; + gas_phase_ptr->Set_total_moles(0); + } else + { + gas_phase_ptr->Set_total_p(0); + gas_phase_ptr->Set_total_moles(0); + } + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + //lp = -k_calc(phase_ptr->rxn_x.logk, tk_x, use.Get_gas_phase_ptr()->total_p * PASCAL_PER_ATM); + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(LOG_10 * (lp - phase_ptr->pr_si_f)); + + if (pr_done) + { + lp = phase_ptr->p_soln_x / gas_phase_ptr->Get_total_p() * + gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_v_m(); + phase_ptr->moles_x = lp; + } + else + { + phase_ptr->moles_x = phase_ptr->p_soln_x * gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } + } + + return (OK); +} diff --git a/phreeqcpp/global_structures.h b/phreeqcpp/global_structures.h new file mode 100644 index 00000000..9d07f324 --- /dev/null +++ b/phreeqcpp/global_structures.h @@ -0,0 +1,1653 @@ +#ifndef _INC_GLOBAL_STRUCTURES_H +#define _INC_GLOBAL_STRUCTURES_H +#include "Surface.h" +#include "GasPhase.h" +/* ---------------------------------------------------------------------- + * #define DEFINITIONS + * ---------------------------------------------------------------------- */ +#ifndef NAN +# define NAN -99999999 +#endif +#define MISSING -9999.999 +#include "NA.h" /* NA = not available */ + +#define F_C_MOL 96493.5 /* C/mol or joule/volt-eq */ +#define F_KJ_V_EQ 96.4935 /* kJ/volt-eq */ +#define F_KCAL_V_EQ 23.0623 /* kcal/volt-eq */ +#define R_LITER_ATM 0.0820597 /* L-atm/deg-mol */ +#define R_KCAL_DEG_MOL 0.00198726 /* kcal/deg-mol */ +#define R_KJ_DEG_MOL 0.00831470 /* kJ/deg-mol */ +#define EPSILON 78.5 /* dialectric constant, dimensionless. Is calculated as eps_r(P, T) in calc_dielectrics. Update the code?? */ +#define EPSILON_ZERO 8.854e-12 /* permittivity of free space, C/V-m = C**2/m-J */ +#define JOULES_PER_CALORIE 4.1840 +#define PASCAL_PER_ATM 1.01325E5 /* conversion from atm to Pa */ +#define AVOGADRO 6.02252e23 /* atoms / mole */ +#define pi 3.14159265358979 +#define AH2O_FACTOR 0.017 + +#define TRUE 1 +#define FALSE 0 +#define OK 1 +#define ERROR 0 +#define STOP 1 +#define CONTINUE 0 + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPT_1 -5 + +#define DISP 2 +#define STAG 3 +#define NOMIX 4 +#define MIX_BS 5 // mix boundary solutions in electromigration + +#define CONVERGED 2 +#define MASS_BALANCE 3 + +#define REWRITE 2 +#define INIT -1 + +/* check_line values, plus EMPTY, EOF, OK */ +#define KEYWORD 3 + +/* copy_token values */ +#define EMPTY 2 +#define UPPER 4 +#define LOWER 5 +#define DIGIT 6 +#define UNKNOWN 7 +#define OPTION 8 + +/* species types */ +#define AQ 0 +#define HPLUS 1 +#define H2O 2 +#define EMINUS 3 +#define SOLID 4 +#define EX 5 +#define SURF 6 +#define SURF_PSI 7 +#define SURF_PSI1 8 +#define SURF_PSI2 9 + +/* unknown types */ +#define MB 10 +#define ALK 11 +#define CB 12 +#define SOLUTION_PHASE_BOUNDARY 13 +#define MU 14 +#define AH2O 15 +#define MH 16 +#define MH2O 17 +#define PP 18 +#define EXCH 19 +#define SURFACE 20 +#define SURFACE_CB 21 +#define SURFACE_CB1 22 +#define SURFACE_CB2 23 +#define GAS_MOLES 24 +#define SS_MOLES 25 +#define PITZER_GAMMA 26 +#define SLACK 28 +/* state */ +#define INITIALIZE 0 +#define INITIAL_SOLUTION 1 +#define INITIAL_EXCHANGE 2 +#define INITIAL_SURFACE 3 +#define INITIAL_GAS_PHASE 4 +#define REACTION 5 +#define INVERSE 6 +#define ADVECTION 7 +#define TRANSPORT 8 +#define PHAST 9 + +/* constaints in mass balance */ +#define EITHER 0 +#define DISSOLVE 1 +#define PRECIPITATE -1 + +/* gas phase type */ +#define PRESSURE 1 +#define VOLUME 2 + +#define MAX_PP_ASSEMBLAGE 10 /* default estimate of the number of phase assemblages */ +#define MAX_ADD_EQUATIONS 20 /* maximum number of equations added together to reduce eqn to + master species */ +#define MAX_ELEMENTS 50 /* default estimate of the number of elements */ +#define MAX_LENGTH 256 /* maximum number of characters component name */ +#define MAX_LINE 4096 /* estimate of maximum line length */ +#define MAX_MASS_BALANCE 10 /* initial guess of number mass balance equations for a solution */ +#define MAX_MASTER 50 /* default estimate of the number of master species */ +#define MAX_ELTS 15 /* default estimate for maximum number of times elements occur in + an equation */ +#define MAX_PHASES 500 /* initial guess of number of phases defined */ +#define MAX_S 500 /* default estimate for maximum number of species in aqueous model */ +#define MAX_SUM_JACOB0 50 /* list used to calculate jacobian */ +#define MAX_SUM_JACOB1 500 /* list used to calculate jacobian */ +#define MAX_SUM_JACOB2 500 /* list used to calculate jacobian */ +#define MAX_SUM_MB 500 /* list used to calculate mass balance sums */ +#define MAX_TRXN 16 /* default estimate for maximum number of components in an eqn */ +#define MAX_UNKNOWNS 15 /* default estimate for maximum number of unknowns in model */ +#define TOL 1e-9 /* tolerance for comparisons of double numbers */ +#define MAX_LM 3.0 /* maximum log molality allowed in intermediate iterations */ +#define MAX_M 1000.0 +#ifdef USE_DECIMAL128 +// #define MIN_LM -80.0 /* minimum log molality allowed before molality set to zero */ +// #define LOG_ZERO_MOLALITY -80 /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ +// #define MIN_TOTAL 1e-60 +// #define MIN_TOTAL_SS MIN_TOTAL/100 +// #define MIN_RELATED_SURFACE MIN_TOTAL*100 +// #define MIN_RELATED_LOG_ACTIVITY -60 +#else +// #define MIN_LM -30.0 /* minimum log molality allowed before molality set to zero */ +// #define LOG_ZERO_MOLALITY -30 /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ +// #define MIN_TOTAL 1e-25 +// #define MIN_TOTAL_SS MIN_TOTAL/100 +// #define MIN_RELATED_SURFACE MIN_TOTAL*100 +// #define MIN_RELATED_LOG_ACTIVITY -30 +#endif +#define REF_PRES_PASCAL 1.01325E5 /* Reference pressure: 1 atm */ +#define MAX_P_NONLLNL 1500.0 + +// +// Typedefs and structure definitions +// +typedef enum { kcal, cal, kjoules, joules } DELTA_H_UNIT; +typedef enum { cm3_per_mol, dm3_per_mol, m3_per_mol } DELTA_V_UNIT; +enum entity_type +{ Solution, Reaction, Exchange, Surface, Gas_phase, Pure_phase, Ss_phase, + Kinetics, Mix, Temperature, Pressure, UnKnown +}; + +typedef enum { + logK_T0, + delta_h, + T_A1, + T_A2, + T_A3, + T_A4, + T_A5, + T_A6, + delta_v, /* set in calc_delta_v: calculated molar volume-change of the reaction */ + vm_tc, /* set in calc_vm: calculated molal volume of the species at tc */ + vm0, /* read: molar volume of a phase */ + vma1, vma2, vma3, vma4, /* read: a1..a4 from supcrt, see calc_vm */ + wref, /* from supcrt */ + b_Av, /* b in z^2 * A_v * log(1 + b * I^0.5) / (2 * b) */ + vmi1, vmi2, vmi3, vmi4, /* ionic strength terms: (i1 + i2/(TK - 228) + i3 * (TK - 228) ) * I^i4 */ + MAX_LOG_K_INDICES /* Keep this definition at the end of the enum */ +} LOG_K_INDICES; + +typedef struct PHRQMemHeader +{ + struct PHRQMemHeader *pNext; /* memory allocated just after this one */ + struct PHRQMemHeader *pPrev; /* memory allocated just prior to this one */ + size_t size; /* memory request + sizeof(PHRQMemHeader) */ +#if !defined(NDEBUG) + char *szFileName; /* file name */ + int nLine; /* line number */ + int dummy; /* alignment */ +#endif +} PHRQMemHeader; + +struct Change_Surf +{ + const char *comp_name; + LDBLE fraction; + const char *new_comp_name; + LDBLE new_Dw; + int cell_no; + int next; +}; +/*---------------------------------------------------------------------- + * CReaction + *---------------------------------------------------------------------- */ +class CReaction +{ +public: + CReaction(void); + CReaction(size_t ntoken); + ~CReaction(void) {} + double* Get_logk(void) { return this->logk; } + void Set_logk(double* d); + double* Get_dz(void) { return this->dz; } + void Set_dz(double* d); + size_t size() { return token.size(); } + std::vector& Get_tokens(void) { return this->token; } + void Set_tokens(const std::vector& t) { this->token = t; } + +public: + double logk[MAX_LOG_K_INDICES]; + double dz[3]; + std::vector token; +}; +class rxn_token +{ +public: + ~rxn_token() {}; + rxn_token() + { + s = NULL; + coef = 0.0; + name = NULL; + } + class species* s; + LDBLE coef; + const char* name; +}; +class save +{ +public: + ~save() {}; + save() + { + solution = 0; + n_solution_user = 0; + n_solution_user_end = 0; + mix = 0; + n_mix_user = 0; + n_mix_user_end = 0; + reaction = 0; + n_reaction_user = 0; + n_reaction_user_end = 0; + pp_assemblage = 0; + n_pp_assemblage_user = 0; + n_pp_assemblage_user_end = 0; + exchange = 0; + n_exchange_user = 0; + n_exchange_user_end = 0; + kinetics = 0; + n_kinetics_user = 0; + n_kinetics_user_end = 0; + surface = 0; + n_surface_user = 0; + n_surface_user_end = 0; + gas_phase = 0; + n_gas_phase_user = 0; + n_gas_phase_user_end = 0; + ss_assemblage = 0; + n_ss_assemblage_user = 0; + n_ss_assemblage_user_end = 0; + } + int solution; + int n_solution_user; + int n_solution_user_end; + int mix; + int n_mix_user; + int n_mix_user_end; + int reaction; + int n_reaction_user; + int n_reaction_user_end; + int pp_assemblage; + int n_pp_assemblage_user; + int n_pp_assemblage_user_end; + int exchange; + int n_exchange_user; + int n_exchange_user_end; + int kinetics; + int n_kinetics_user; + int n_kinetics_user_end; + int surface; + int n_surface_user; + int n_surface_user_end; + int gas_phase; + int n_gas_phase_user; + int n_gas_phase_user_end; + int ss_assemblage; + int n_ss_assemblage_user; + int n_ss_assemblage_user_end; +}; + +/*---------------------------------------------------------------------- + * Copy + *---------------------------------------------------------------------- */ +class copier +{ +public: + ~copier() {}; + copier() + {} + std::vector n_user; + std::vector start; + std::vector end; +}; +/*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ +class inverse +{ +public: + ~inverse() {}; + inverse() + { + n_user = -1; + description = NULL; + new_def = FALSE; + minimal = FALSE; + range = FALSE; + mp = FALSE; + mp_censor = 1e-20; + range_max = 1000.0; + tolerance = 1e-10; + mp_tolerance = 1e-12; + //uncertainties.clear(); + //ph_uncertainties.clear(); + water_uncertainty = 0.0; + mineral_water = TRUE; + carbon = TRUE; + //dalk_dph.clear(); + //dalk_dc.clear(); + count_solns = 0; + //solns.clear(); + //force_solns.clear(); + //elts.clear(); + //phases.clear(); + count_redox_rxns = 0; + //isotopes.clear(); + //i_u.clear(); + //isotope_unknowns.clear(); + netpath = NULL; + pat = NULL; + } + int n_user; + char* description; + int new_def; + int minimal; + int range; + int mp; + LDBLE mp_censor; + LDBLE range_max; + LDBLE tolerance; + LDBLE mp_tolerance; + std::vector uncertainties; + std::vector ph_uncertainties; + LDBLE water_uncertainty; + int mineral_water; + int carbon; + std::vector dalk_dph; + std::vector dalk_dc; + size_t count_solns; + std::vector solns; + std::vector force_solns; + std::vector elts; + std::vector phases; + size_t count_redox_rxns; + std::vector isotopes; + std::vector i_u; + std::vector isotope_unknowns; + const char* netpath; + const char* pat; +}; +class inv_elts +{ +public: + ~inv_elts() {}; + inv_elts() + { + name = NULL; + master = NULL; + row = 0; + //uncertainties.clear(); + } + const char* name; + class master* master; + size_t row; + std::vector uncertainties; +}; +class inv_isotope +{ +public: + ~inv_isotope() {}; + inv_isotope() + { + isotope_name = NULL; + isotope_number = 0; + elt_name = NULL; + //uncertainties.clear(); + } + const char* isotope_name; + LDBLE isotope_number; + const char* elt_name; + std::vector uncertainties; +}; +class inv_phases +{ +public: + ~inv_phases() {}; + inv_phases() + { + name = NULL; + phase = NULL; + column = 0; + constraint = EITHER; + force = FALSE; + //isotopes.clear(); + } + const char* name; + class phase* phase; + int column; + int constraint; + int force; + std::vector isotopes; +}; +/*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ +class Model +{ +public: + Model() + { + force_prep = true; + gas_phase_type = cxxGasPhase::GP_UNKNOWN; + numerical_fixed_volume = false; + dl_type = cxxSurface::NO_DL; + surface_type = cxxSurface::UNKNOWN_DL; + }; + ~Model() + { + }; + bool force_prep; + bool numerical_fixed_volume; + cxxGasPhase::GP_TYPE gas_phase_type; + std::vector gas_phase; + std::vector ss_assemblage; + std::vector pp_assemblage; + std::vector si; + std::vector add_formula; + cxxSurface::DIFFUSE_LAYER_TYPE dl_type; + cxxSurface::SURFACE_TYPE surface_type; + std::vector surface_comp; + std::vector surface_charge; +}; +class name_coef +{ +public: + ~name_coef() {}; + name_coef() + { + name = NULL; + coef = 0; + } + const char* name; + LDBLE coef; +}; +/*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ +class species_list +{ +public: + ~species_list() {}; + species_list() + { + master_s = NULL; + s = NULL; + coef = 0; + } + class species* master_s; + class species* s; + LDBLE coef; +}; +/*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ +class list0 +{ +public: + ~list0() {}; + list0() + { + target = NULL; + coef = 0; + } + LDBLE* target; + LDBLE coef; +}; +class list1 +{ +public: + ~list1() {}; + list1() + { + source = NULL; + target = NULL; + } + LDBLE* source; + LDBLE* target; +}; +class list2 +{ +public: + ~list2() {}; + list2() + { + source = NULL; + target = NULL; + coef = 0; + } + LDBLE* source; + LDBLE* target; + LDBLE coef; +}; +class isotope +{ +public: + ~isotope() {}; + isotope() + { + isotope_number = 0; + elt_name = NULL; + isotope_name = NULL; + total = 0; + ratio = 0; + ratio_uncertainty = 0; + x_ratio_uncertainty = 0; + master = NULL; + primary = NULL; + coef = 0; /* coefficient of element in phase */ + } + LDBLE isotope_number; + const char* elt_name; + const char* isotope_name; + LDBLE total; + LDBLE ratio; + LDBLE ratio_uncertainty; + LDBLE x_ratio_uncertainty; + class master* master; + class master* primary; + LDBLE coef; +}; +class iso +{ +public: + ~iso() {}; + iso() + { + name = NULL; + value = 0; + uncertainty = 0.05; + } + const char* name; + LDBLE value; + LDBLE uncertainty; +}; +/*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ +class stag_data +{ +public: + ~stag_data() {}; + stag_data() + { + count_stag = 0; + exch_f = 0; + th_m = 0; + th_im = 0; + } + int count_stag; + LDBLE exch_f; + LDBLE th_m; + LDBLE th_im; +}; +class cell_data +{ +public: + ~cell_data() {}; + cell_data() + { + length = 1; + mid_cell_x = 1.; + disp = 1.0; + temp = 25.; + // free (uncharged) porewater porosities + por = 0.1; + // interlayer water porosities + por_il = 0.01; + // potential (V) + potV = 0; + punch = FALSE; + print = FALSE; + same_model = FALSE; + } + LDBLE length; + LDBLE mid_cell_x; + LDBLE disp; + LDBLE temp; + LDBLE por; + LDBLE por_il; + LDBLE potV; + int punch; + int print; + int same_model; +}; +/*---------------------------------------------------------------------- + * Keywords + *---------------------------------------------------------------------- */ +//class key +//{ +//public: +// ~key() {}; +// key() +// { +// name = NULL; +// keycount = 0; +// } +// char* name; +// int keycount = 0; +// }; +//class const_key +//{ +//public: +// ~const_key() {}; +// const_key() +// { +// name = NULL; +// keycount = 0; +// } +// const char* name; +// int keycount; +//}; +/*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ +class element +{ +public: + ~element() {}; + element() + { + // element name + name = NULL; + /* int in; */ + master = NULL; + primary = NULL; + gfw = 0; + } + const char* name; + class master* master; + class master* primary; + LDBLE gfw; +}; +/*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ +class elt_list +{ +public: + ~elt_list() {}; + elt_list() + { /* list of name and number of elements in an equation */ + elt = NULL; /* pointer to element structure */ + coef = 0.0; /* number of element e's in eqn */ + } + class element* elt; + LDBLE coef; +}; +/*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ +class species +{ +public: + ~species() {}; + species() + { /* all data pertinent to an aqueous species */ + name = NULL; // name of species + mole_balance = NULL; // formula for mole balance + in = FALSE; // set internally if species in model + number = 0; + // points to master species list, NULL if not primary master + primary = NULL; + // points to master species list, NULL if not secondary master + secondary = NULL; + gfw = 0; // gram formula wt of species + z = 0; // charge of species + // tracer diffusion coefficient in water at 25oC, m2/s + dw = 0; + // correct Dw for temperature: Dw(TK) = Dw(298.15) * exp(dw_t / TK - dw_t / 298.15) + dw_t = 0; + // parms for calc'ng SC = SC0 * exp(-dw_a * z * mu^0.5 / (1 + DH_B * dw_a2 * mu^0.5)) + dw_a = 0; + dw_a2 = 0; + dw_a_visc = 0; // viscosity correction of SC + dw_t_SC = 0; // contribution to SC, for calc'ng transport number with BASIC + dw_corr = 0; // dw corrected for TK and mu + erm_ddl = 0; // enrichment factor in DDL + equiv = 0; // equivalents in exchange species + alk = 0; // alkalinity of species, used for cec in exchange + carbon = 0; // stoichiometric coefficient of carbon in species + co2 = 0; // stoichiometric coefficient of C(4) in species + h = 0; // stoichiometric coefficient of H in species + // stoichiometric coefficient of O in species + o = 0; + // WATEQ Debye Huckel a and b-dot; active_fraction coef for exchange species + dha = 0, dhb = 0, a_f = 0; + lk = 0; // log10 k at working temperature + // log kt0, delh, 6 coefficients analytical expression + volume terms + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) logk[i] = 0; + // 7 coefficients analytical expression for B, D, anion terms and pressure in Jones_Dole viscosity eqn + for (size_t i = 0; i < 10; i++) Jones_Dole[i] = 0; + // regression coefficients to calculate temperature dependent phi_0and b_v of Millero density model + for (size_t i = 0; i < 7; i++) millero[i] = 0; + original_units = kjoules; // enum with original delta H units + //add_logk.clear(); + lg = 0; // log10 activity coefficient, gamma + lg_pitzer = 0; // log10 activity coefficient, from pitzer calculation + lm = 0; // log10 molality + la = 0; // log10 activity + dg = 0; // gamma term for jacobian + dg_total_g = 0; + moles = 0; // moles in solution; moles/mass_water = molality + type = 0; // flag indicating presence in model and types of equations + gflag = 0; // flag for preferred activity coef eqn + exch_gflag = 0; // flag for preferred activity coef eqn + // vector of elements + //next_elt.clear(); + //next_secondary.clear(); + //next_sys_total.clear(); + // switch to check equation for charge and element balance + check_equation = TRUE; + //CReaction rxn; // data base reaction + //CReaction rxn_s; // reaction converted to secondary and primary master species + //CReaction rxn_x; // reaction to be used in model + // (1 + sum(g)) * moles + tot_g_moles = 0; + // sum(moles*g*Ws/Waq) + tot_dh2o_moles = 0; + for (size_t i = 0; i < 5; i++) cd_music[i] = 0; + for (size_t i = 0; i < 3; i++) dz[i] = 0; + original_deltav_units = cm3_per_mol; + } + const char* name; + const char* mole_balance; + int in; + int number; + class master* primary; + class master* secondary; + LDBLE gfw; + LDBLE z; + LDBLE dw; + LDBLE dw_t; + LDBLE dw_a; + LDBLE dw_a2; + LDBLE dw_a_visc; + LDBLE dw_t_SC; + LDBLE dw_corr; + LDBLE erm_ddl; + LDBLE equiv; + LDBLE alk; + LDBLE carbon; + LDBLE co2; + LDBLE h; + LDBLE o; + LDBLE dha, dhb, a_f; + LDBLE lk; + LDBLE logk[MAX_LOG_K_INDICES]; + LDBLE Jones_Dole[10]; + LDBLE millero[7]; + DELTA_H_UNIT original_units; + std::vector add_logk; + LDBLE lg; + LDBLE lg_pitzer; + LDBLE lm; + LDBLE la; + LDBLE dg; + LDBLE dg_total_g; + LDBLE moles; + int type; + int gflag; + int exch_gflag; + std::vector next_elt; + std::vector next_secondary; + std::vector next_sys_total; + int check_equation; + CReaction rxn; + CReaction rxn_s; + CReaction rxn_x; + LDBLE tot_g_moles; + LDBLE tot_dh2o_moles; + LDBLE cd_music[5]; + LDBLE dz[3]; + DELTA_V_UNIT original_deltav_units; +}; +class logk +{ +public: + ~logk() {}; + logk() + { /* Named log K's */ + name = NULL; // name of species + lk = 0.0; // log10 k at working temperature + // log kt0, delh, 6 coefficients analalytical expression + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) log_k[i] = 0; + // enum with original delta H units + original_units = kjoules; + done = FALSE; + //add_logk.clear(); + // log kt0, delh, 5 coefficients analalytical expression + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) log_k_original[i] = 0; + original_deltav_units = cm3_per_mol; + } + const char* name; + LDBLE lk; + LDBLE log_k[MAX_LOG_K_INDICES]; + DELTA_H_UNIT original_units; + int done; + std::vector add_logk; + LDBLE log_k_original[MAX_LOG_K_INDICES]; + DELTA_V_UNIT original_deltav_units; +}; +/*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ +class phase +{ +public: + ~phase() {}; + phase() + { /* all data pertinent to a pure solid phase */ + name = NULL; //name of species + formula = NULL; // chemical formula + in = FALSE; // species used in model if TRUE + lk = 0; // log10 k at working temperature + // log kt0, delh, 6 coefficients analalytical expression + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) logk[i] = 0; + // enum with original delta H units + original_units = kjoules; + original_deltav_units = cm3_per_mol; + //add_logk.clear(); + moles_x = 0; + delta_max = 0; + p_soln_x = 0; + fraction_x = 0; + log10_lambda = 0; + log10_fraction_x = 0; + dn = 0, dnb = 0, dnc = 0; + gn = 0, gntot = 0; + gn_n = 0, gntot_n = 0; + // gas: critical TK, critical P(atm), Pitzer acentric coeff + t_c = 0, p_c = 0, omega = 0; + // Peng-Robinson parm's + pr_a = 0, pr_b = 0, pr_alpha = 0; + // Temperature (K), Pressure (atm) + pr_tk = 0, pr_p = 0; + // fugacity coefficient (-) + pr_phi = 0; + // for calculating multicomponent phi + pr_aa_sum2 = 0; + // delta_v[0] = [1] + [2]*T + [3]/T + [4]*log10(T) + [5]/T^2 + [6]*T^2 + [7]*P + for (size_t i = 0; i < 9; i++) delta_v[i] = 0; + // si adapter: log10(phi) - delta_v[0] * (P - 1) /RT + pr_si_f = 0; + // Peng-Robinson in the calc's, or not + pr_in = false; + // flag indicating presence in model and types of equations + type = SOLID; + // list of elements in phase + //next_elt.clear(); + //next_sys_total.clear(); + // switch to check equation for charge and element balance + check_equation = TRUE; + // data base reaction + //CReaction rxn; + // reaction converted to secondary and primary master species + //CReaction rxn_s; + // reaction to be used in model + //CReaction rxn_x; + // equation contains solids or gases + replaced = FALSE; + in_system = FALSE; + } + const char* name; + const char* formula; + int in; + LDBLE lk; + LDBLE logk[MAX_LOG_K_INDICES]; + DELTA_H_UNIT original_units; + DELTA_V_UNIT original_deltav_units; + std::vector add_logk; + LDBLE moles_x; + LDBLE delta_max; + LDBLE p_soln_x; + LDBLE fraction_x; + LDBLE log10_lambda; + LDBLE log10_fraction_x; + LDBLE dn, dnb, dnc; + LDBLE gn, gntot; + LDBLE gn_n, gntot_n; + LDBLE t_c, p_c, omega; + LDBLE pr_a, pr_b, pr_alpha; + LDBLE pr_tk, pr_p; + LDBLE pr_phi; + LDBLE pr_aa_sum2; + LDBLE delta_v[9]; + LDBLE pr_si_f; + bool pr_in; + int type; + std::vector next_elt; + std::vector next_sys_total; + int check_equation; + CReaction rxn; + CReaction rxn_s; + CReaction rxn_x; + int replaced; + int in_system; +}; +/*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ +class master +{ +public: + ~master() {}; + master() + { + // TRUE if in model, FALSE if out, REWRITE if other mb eq + in = 0; + // sequence number in list of masters + number = 0; + // saved to determine if model has changed + last_model = FALSE; + // AQ or EX + type = 0; + // TRUE if master species is primary + primary = FALSE; + // coefficient of element in master species + coef = 0; + // total concentration for element or valence state + total = 0; + isotope_ratio = 0; + isotope_ratio_uncertainty = 0; + isotope = 0; + total_primary = 0; + // element structure + elt = NULL; + // alkalinity of species + alk = 0; + // default gfw for species + gfw = 1; + // formula from which to calcuate gfw + gfw_formula = NULL; + // pointer to unknown structure + unknown = NULL; + // pointer to species structure + s = NULL; + // reaction writes master species in terms of primary master species + //CReaction rxn_primary; + // reaction writes master species in terms of secondary master species + //CReaction rxn_secondary; + pe_rxn = NULL; + minor_isotope = FALSE; + } + int in; + size_t number; + int last_model; + int type; + int primary; + LDBLE coef; + LDBLE total; + LDBLE isotope_ratio; + LDBLE isotope_ratio_uncertainty; + int isotope; + LDBLE total_primary; + class element* elt; + LDBLE alk; + LDBLE gfw; + const char* gfw_formula; + class unknown* unknown; + class species* s; + CReaction rxn_primary; + CReaction rxn_secondary; + const char* pe_rxn; + int minor_isotope; +}; +/*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ +class unknown +{ +public: + ~unknown() {}; + unknown() + { + type = 0; + moles = 0; + ln_moles = 0; + f = 0; + sum = 0; + delta = 0; + la = 0; + number = 0; + description = NULL; + //master.clear(); + phase = NULL; + si = 0; + n_gas_phase_user = 0; + s = NULL; + exch_comp = NULL; + pp_assemblage_comp_name = NULL; + pp_assemblage_comp_ptr = NULL; + ss_name = NULL; + ss_ptr = NULL; + ss_comp_name = NULL; + ss_comp_ptr = NULL; + ss_comp_number = 0; + ss_in = FALSE; + surface_comp = NULL; + surface_charge = NULL; + related_moles = 0; + potential_unknown = NULL; + potential_unknown1 = NULL; + potential_unknown2 = NULL; + // list for CD_MUSIC of comps that contribute to 0 plane mass-balance term + //comp_unknowns.clear(); + phase_unknown = NULL; + mass_water = 1; + dissolve_only = FALSE; + inert_moles = 0; + V_m = 0; + pressure = 1; + mb_number = 0; + iteration = 0; + } + int type; + LDBLE moles; + LDBLE ln_moles; + LDBLE f; + LDBLE sum; + LDBLE delta; + LDBLE la; + size_t number; + const char* description; + std::vector master; + class phase* phase; + LDBLE si; + int n_gas_phase_user; + class species* s; + const char* exch_comp; + const char* pp_assemblage_comp_name; + void* pp_assemblage_comp_ptr; + const char* ss_name; + void* ss_ptr; + const char* ss_comp_name; + void* ss_comp_ptr; + int ss_comp_number; + int ss_in; + const char* surface_comp; + const char* surface_charge; + LDBLE related_moles; + class unknown* potential_unknown; + class unknown* potential_unknown1; + class unknown* potential_unknown2; + std::vector comp_unknowns; + class unknown* phase_unknown; + LDBLE mass_water; + int dissolve_only; + LDBLE inert_moles; + LDBLE V_m; + LDBLE pressure; + int mb_number; + int iteration; +}; +/*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ +class reaction_temp +{ +public: + ~reaction_temp() {}; + reaction_temp() + { + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) logk[i] = 0; + for (size_t i = 0; i < 3; i++) dz[i] = 0; + //token.clear(); + } + LDBLE logk[MAX_LOG_K_INDICES]; + LDBLE dz[3]; + std::vector token; +}; +class rxn_token_temp +{ +public: + ~rxn_token_temp() {}; + rxn_token_temp() + { // data for equations, aq. species or minerals + name = NULL; // pointer to a species name (formula) + z = 0; // charge on species + s = NULL; + unknown = NULL; + coef = 0; // coefficient of species name + } + const char* name; + LDBLE z; + class species* s; + class unknown* unknown; + LDBLE coef; +}; +class unknown_list +{ +public: + ~unknown_list() {}; + unknown_list() + { + unknown = NULL; + source = NULL; + gamma_source = NULL; + coef = 0; + } + class unknown* unknown; + LDBLE* source; + LDBLE* gamma_source; + LDBLE coef; +}; +/* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ +class prints +{ +public: + ~prints() {}; + prints() + { + all = 0; + initial_solutions = 0; + initial_exchangers = 0; + reactions = 0; + gas_phase = 0; + ss_assemblage = 0; + pp_assemblage = 0; + surface = 0; + exchange = 0; + kinetics = 0; + totals = 0; + eh = 0; + species = 0; + saturation_indices = 0; + irrev = 0; + mix = 0; + reaction = 0; + use = 0; + logfile = 0; + punch = 0; + status = 0; + inverse = 0; + dump = 0; + user_print = 0; + headings = 0; + user_graph = 0; + echo_input = 0; + warnings = 0; + initial_isotopes = 0; + isotope_ratios = 0; + isotope_alphas = 0; + hdf = 0; + alkalinity = 0; + } + int all; + int initial_solutions; + int initial_exchangers; + int reactions; + int gas_phase; + int ss_assemblage; + int pp_assemblage; + int surface; + int exchange; + int kinetics; + int totals; + int eh; + int species; + int saturation_indices; + int irrev; + int mix; + int reaction; + int use; + int logfile; + int punch; + int status; + int inverse; + int dump; + int user_print; + int headings; + int user_graph; + int echo_input; + int warnings; + int initial_isotopes; + int isotope_ratios; + int isotope_alphas; + int hdf; + int alkalinity; +}; +/* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ +class rate +{ +public: + ~rate() {}; + rate() + { + name = NULL; + //std::string commands; + new_def = 0; + linebase = NULL; + varbase = NULL; + loopbase = NULL; + } + const char* name; + std::string commands; + int new_def; + void* linebase; + void* varbase; + void* loopbase; +}; +/* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ +class spread_row +{ +public: + ~spread_row() {}; + spread_row() + { + count = 0; + empty = 0, string = 0, number = 0; + //char_vector.clear(); + //d_vector.clear(); + //type_vector.clear(); + } + size_t count; + size_t empty, string, number; + std::vector str_vector; + std::vector type_vector; +}; +class defaults +{ +public: + ~defaults() {}; + defaults() + { + temp = 25; + density = 1; + calc_density = false; + units = NULL; + redox = NULL; + ph = 7; + pe = 4; + water = 1; + //iso.clear(); + pressure = 1; /* pressure in atm */ + } + LDBLE temp; + LDBLE density; + bool calc_density; + const char* units; + const char* redox; + LDBLE ph; + LDBLE pe; + LDBLE water; + std::vector iso; + LDBLE pressure; +}; +class spread_sheet +{ +public: + ~spread_sheet() {}; + spread_sheet() + { + heading = NULL; + units = NULL; + //class defaults defaults; + } + class spread_row* heading; + class spread_row* units; + std::vector rows; + class defaults defaults; +}; +/* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ +class master_isotope +{ +public: + ~master_isotope() {}; + master_isotope() + { + name = NULL; + master = NULL; + elt = NULL; + units = NULL; + standard = 0; + ratio = 0; + moles = 0; + total_is_major = 0; + minor_isotope = 0; + } + const char* name; + class master* master; + class element* elt; + const char* units; + LDBLE standard; + LDBLE ratio; + LDBLE moles; + int total_is_major; + int minor_isotope; +}; +class calculate_value +{ +public: + ~calculate_value() {}; + calculate_value() + { + name = NULL; + value = 0; + //commands.clear(); + new_def = 0; + calculated = 0; + linebase = NULL; + varbase = NULL; + loopbase = NULL; + } + const char* name; + LDBLE value; + std::string commands; + int new_def; + int calculated; + void* linebase; + void* varbase; + void* loopbase; +}; +class isotope_ratio +{ +public: + isotope_ratio() + { + name = NULL; + isotope_name = NULL; + ratio = 0; + converted_ratio = 0; + } + ~isotope_ratio() {}; + + const char* name; + const char* isotope_name; + LDBLE ratio; + LDBLE converted_ratio; +}; +class isotope_alpha +{ +public: + isotope_alpha() + { + name = NULL; + named_logk = NULL; + value = 0; + } + ~isotope_alpha() {}; + const char* name; + const char* named_logk; + LDBLE value; +}; +class system_species +{ +public: + ~system_species() {}; + system_species() + { + name = NULL; + type = NULL; + moles = 0; + } + char* name; + char* type; + LDBLE moles; +}; +/* tally.c ------------------------------- */ +class tally_buffer +{ +public: + ~tally_buffer() {}; + tally_buffer() + { + name = NULL; + master = NULL; + moles = 0; + gfw = 0; + } + const char* name; + class master* master; + LDBLE moles; + LDBLE gfw; +}; +class tally +{ +public: + ~tally() {}; + tally() + { + name = NULL; + type = UnKnown; + add_formula = NULL; + moles = 0; + //formula.clear(); + /* + * first total is initial + * second total is final + * third total is difference (final - initial) + */ + for(size_t i = 0; i < 3; i++) total[i]= NULL; + } + const char* name; + enum entity_type type; + const char* add_formula; + LDBLE moles; + std::vector formula; + /* + * first total is initial + * second total is final + * third total is difference (final - initial) + */ + class tally_buffer* total[3]; +}; +/* transport.c ------------------------------- */ +class spec +{ +public: + ~spec() {}; + spec() + { + // name of species + name = NULL; + // name of aqueous species in EX species + aq_name = NULL; + // type: AQ or EX + type = 0; + // activity + a = 0; + // log(concentration) + lm = 0; + // log(gamma) + lg = 0; + // concentration for AQ, equivalent fraction for EX + c = 0; + // charge number + z = 0; + // temperature corrected free water diffusion coefficient, m2/s + Dwt = 0; + // temperature factor for Dw + dw_t = 0; + // enrichment factor in ddl + erm_ddl = 0; + } + const char* name; + const char* aq_name; + int type; + LDBLE a; + LDBLE lm; + LDBLE lg; + LDBLE c; + LDBLE z; + LDBLE Dwt; + LDBLE dw_t; + LDBLE erm_ddl; +}; + +class sol_D +{ +public: + ~sol_D() {}; + sol_D() + { + // number of aqueous + exchange species + count_spec = 0; + // number of exchange species + count_exch_spec = 0; + // total moles of X-, max X- in transport step in sol_D[1], tk + exch_total = 0, x_max = 0, tk_x = 0; + // (tk_x * viscos_0_25) / (298 * viscos) + viscos_f = 0; + spec = NULL; + spec_size = 0; + } + int count_spec; + int count_exch_spec; + LDBLE exch_total, x_max, tk_x; + LDBLE viscos_f; + class spec* spec; + int spec_size; +}; +class J_ij +{ +public: + ~J_ij() {}; + J_ij() + { + name = NULL; + // species change in cells i and j + tot1 = 0; + tot2 = 0; + tot_stag = 0; + charge = 0; + } + const char* name; + LDBLE tot1, tot2, tot_stag, charge; +}; +class J_ij_save +{ +public: + ~J_ij_save() {}; + J_ij_save() + { + // species change in cells i and j + flux_t = 0; + flux_c = 0; + } + double flux_t, flux_c; +}; +class M_S +{ +public: + ~M_S() {}; + M_S() + { + name = NULL; + // master species transport in cells i and j + tot1 = 0; + tot2 = 0; + tot_stag = 0; + charge = 0; + } + const char* name; + LDBLE tot1, tot2, tot_stag, charge; +}; +// Pitzer definitions +typedef enum +{ TYPE_B0, TYPE_B1, TYPE_B2, TYPE_C0, TYPE_THETA, TYPE_LAMDA, TYPE_ZETA, + TYPE_PSI, TYPE_ETHETA, TYPE_ALPHAS, TYPE_MU, TYPE_ETA, TYPE_Other, + TYPE_SIT_EPSILON, TYPE_SIT_EPSILON_MU, TYPE_APHI +} pitz_param_type; +class pitz_param +{ +public: + ~pitz_param() {}; + pitz_param() + { + for(size_t i = 0; i < 3; i++) species[i] = NULL; + for (size_t i = 0; i < 3; i++) ispec[i] = -1; + type = TYPE_Other; + p = 0; + U.b0 = 0; + for (size_t i = 0; i < 6; i++) a[i] = 0; + alpha = 0; + os_coef = 0; + for (size_t i = 0; i < 3; i++) ln_coef[i] = 0; + thetas = NULL; + } + const char* species[3]; + int ispec[3]; + pitz_param_type type; + LDBLE p; + union + { + LDBLE b0; + LDBLE b1; + LDBLE b2; + LDBLE c0; + LDBLE theta; + LDBLE lamda; + LDBLE zeta; + LDBLE psi; + LDBLE alphas; + LDBLE mu; + LDBLE eta; + LDBLE eps; + LDBLE eps1; + LDBLE aphi; + } U; + LDBLE a[6]; + LDBLE alpha; + LDBLE os_coef; + LDBLE ln_coef[3]; + class theta_param* thetas; +}; +class theta_param +{ +public: + ~theta_param() {}; + theta_param() + { + zj = 0; + zk = 0; + etheta = 0; + ethetap = 0; + } + LDBLE zj; + LDBLE zk; + LDBLE etheta; + LDBLE ethetap; +}; +class const_iso +{ +public: + ~const_iso() {}; + const_iso() + { + name = NULL; + value = 0; + uncertainty = 0; + } + const_iso(const char *n, LDBLE v, LDBLE u) + { + name = n; + value = v; + uncertainty = u; + } + const char* name; + LDBLE value; + LDBLE uncertainty; +}; + +#endif /* _INC_GLOBAL_STRUCTURES_H */ diff --git a/phreeqcpp/input.cpp b/phreeqcpp/input.cpp new file mode 100644 index 00000000..18339645 --- /dev/null +++ b/phreeqcpp/input.cpp @@ -0,0 +1,130 @@ +#include +#include "Utils.h" +#include "Phreeqc.h" +#include +#include +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_reading_database(int reading_database) +/* ---------------------------------------------------------------------- */ +{ + reading_db = reading_database; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reading_database(void) +/* ---------------------------------------------------------------------- */ +{ + return reading_db; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_line(const char *string, int allow_empty, int allow_eof, + int allow_keyword, int print) +/* ---------------------------------------------------------------------- */ +{ + if (reading_database()) + print = FALSE; + return check_line_impl(string, allow_empty, allow_eof, allow_keyword, + print); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_line_impl(const char *string, int allow_empty, int allow_eof, + int allow_keyword, int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function gets a new line and checks for empty, eof, and keywords. + * + * Arguments: + * string Input, character string used in printing error message + * allow_empty Input, True or false, if a blank line is accepable + * if false, another line is read + * allow_eof Input, True or false, if EOF is acceptable + * allow_keyword Input, True or false, if a keyword is acceptable + * + * Returns: + * EMPTY if empty line read and allow_empty == true + * KEYWORD if line begins with keyword + * EOF if eof and allow_eof == true + * OK otherwise + * OPTION if line begins with -[alpha] + * + * Terminates if EOF and allow_eof == false. + */ + int i; + + +/* Get line */ + do + { + i = get_line(); + if ((print == TRUE && i != EOF) || i == KEYWORD) + { + echo_msg(sformatf( "\t%s\n", line_save)); + } + } + while (i == EMPTY && allow_empty == FALSE); +/* Check eof */ + if (i == EOF && allow_eof == FALSE) + { + error_string = sformatf( + "Unexpected eof while reading %s\nExecution terminated.\n", + string); + error_msg(error_string, STOP); + } +/* Check keyword */ + if (i == KEYWORD && allow_keyword == FALSE) + { + error_string = sformatf( + "Expected data for %s, but got a keyword ending data block.", + string); + error_msg(error_string, CONTINUE); + input_error++; + } + check_line_return = i; + return (i); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_line(void) +/* ---------------------------------------------------------------------- */ +{ + PHRQ_io::LINE_TYPE j = phrq_io->get_line(); + // check_key sets next_keyword + next_keyword = phrq_io->Get_m_next_keyword(); + + // copy parser line to line and line_save + // make sure there is enough space + size_t l1 = strlen(phrq_io->Get_m_line().c_str()) + 1; + size_t l2 = strlen(phrq_io->Get_m_line_save().c_str()) + 1; + size_t l = (l1 > l2) ? l1 : l2; + if (l >= (size_t) max_line) + { + max_line = (int) l * 2; + line_save = (char *) PHRQ_realloc(line_save, + (size_t) max_line * sizeof(char)); + if (line_save == NULL) + malloc_error(); + line = (char *) PHRQ_realloc(line, (size_t) max_line * sizeof(char)); + if (line == NULL) + malloc_error(); + } + strcpy(line, phrq_io->Get_m_line().c_str()); + strcpy(line_save, phrq_io->Get_m_line_save().c_str()); + return j; +} diff --git a/phreeqcpp/integrate.cpp b/phreeqcpp/integrate.cpp new file mode 100644 index 00000000..1cfab335 --- /dev/null +++ b/phreeqcpp/integrate.cpp @@ -0,0 +1,1087 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Utils.h" +#include "Solution.h" + +#define MAX_QUAD 20 +#define K_POLY 5 + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_all_g(void) +/* ---------------------------------------------------------------------- */ +{ + int converge, converge1; + LDBLE new_g, xd1; + LDBLE epsilon; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * calculate g for each surface + */ + epsilon = convergence_tolerance; + if (convergence_tolerance >= 1e-8) + { + G_TOL = 1e-9; + } + else + { + G_TOL = 1e-10; + } + + converge = TRUE; + + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( "Calc_all_g, X[%d]\n", j)); + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + std::map temp_g_map; + cxxSurfDL temp_g; + charge_ptr->Get_g_map()[0] = temp_g; + temp_g_map[0] = temp_g; + xd_global = exp(-2 * x[j]->master[0]->s->la * LOG_10); + /* alpha = 0.02935 @ 25; (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + /* 1000 J/kJ and 1000 L/m**3 */ + //alpha_global = sqrt(EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * 1000.0 * + // tk_x * 0.5); + alpha_global = sqrt(eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * 1000.0 * + tk_x * 0.5); +/* + * calculate g for given surface for each species + */ + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > HPLUS) + continue; + if (temp_g_map.find(s_x[i]->z) != temp_g_map.end()) + continue; + z_global = s_x[i]->z; + if (charge_ptr->Get_grams() > 0.0) + { + + if ((use.Get_surface_ptr()->Get_only_counter_ions() == false) || + (((x[j]->master[0]->s->la > 0) && (z_global < 0)) + || ((x[j]->master[0]->s->la < 0) && (z_global > 0)))) + { + if (xd_global > 0.1) + { + new_g = qromb_midpnt(charge_ptr, 1.0, xd_global); + } + else if (xd_global > 0.01) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, xd_global); + } + else if (xd_global > 0.001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, xd_global); + } + else if (xd_global > 0.0001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, xd_global); + } + else if (xd_global > 0.00001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, xd_global); + } + else if (xd_global > 0.000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, xd_global); + } + else if (xd_global > 0.0000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, xd_global); + } + else if (xd_global > 0.00000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, .0000001); + new_g += qromb_midpnt(charge_ptr, 0.0000001, xd_global); + } + else + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, .0000001); + new_g += qromb_midpnt(charge_ptr, 0.0000001, .00000001); + new_g += qromb_midpnt(charge_ptr, 0.00000001, xd_global); + } + } + else + { + new_g = 0; + } + } + else + { + new_g = 0.0; + } + if ((use.Get_surface_ptr()->Get_only_counter_ions()) && new_g < 0) + new_g = 0; + converge1 = TRUE; + if (fabs(new_g) >= 1.) + { + if (fabs((new_g - charge_ptr->Get_g_map()[z_global].Get_g()) / new_g) > epsilon) + { + converge1 = FALSE; + } + } + else + { + if (fabs(new_g - charge_ptr->Get_g_map()[z_global].Get_g()) > epsilon) + { + converge1 = FALSE; + } + } + if (converge1 == FALSE) + { + converge = FALSE; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\t%12f\t%12.4e\t%12.4e\t%12.4e\n", + (double) z_global, + (double) charge_ptr->Get_g_map()[z_global].Get_g(), + (double) new_g, + (double) (new_g - charge_ptr->Get_g_map()[z_global].Get_g()))); + } + } + charge_ptr->Get_g_map()[z_global].Set_g(new_g); + if (new_g == 0) + { + charge_ptr->Get_g_map()[z_global].Set_dg(0.); + } + else + { + if (charge_ptr->Get_grams() > 0.0) + { + LDBLE dg = charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() * alpha_global * + g_function(xd_global) / F_C_MOL; + dg *= + -2. / (exp(x[j]->master[0]->s->la * LOG_10) * + exp(x[j]->master[0]->s->la * LOG_10)); + if ((xd_global - 1) < 0.0) + { + dg *= -1.0; + } + + if (fabs(dg) < 1e-8) + { + xd1 = exp(-2 * 1e-3 * LOG_10); + new_g = qromb_midpnt(charge_ptr, 1.0, xd1); + dg = new_g / .001; + } + charge_ptr->Get_g_map()[z_global].Set_dg(dg); + } + else + { + charge_ptr->Get_g_map()[z_global].Set_dg(0.0); + } + } + temp_g_map[z_global] = charge_ptr->Get_g_map()[z_global]; + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf("\nSurface component %d: charge,\tg,\tdg/dlny,\txd\n", + (int) charge_ptr->Get_g_map().size())); + std::map::iterator it; + for (it = charge_ptr->Get_g_map().begin(); it != charge_ptr->Get_g_map().end(); it++) + { + output_msg(sformatf( + "\t%12f\t%12.4e\t%12.4e\t%12.4e\n", + (double) it->first, + (double) it->second.Get_g(), + (double) it->second.Get_dg(), + (double) xd_global)); + } + } + } + return (converge); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +g_function(LDBLE x_value) +/* ---------------------------------------------------------------------- */ +{ + LDBLE sum, return_value, sum1; + int i; + LDBLE ln_x_value; + + if (equal(x_value, 1.0, G_TOL * 100) == TRUE) + return (0.0); + sum = 0.0; + ln_x_value = log(x_value); + + cxxSurfaceCharge *charge_ptr = &(use.Get_surface_ptr()->Get_surface_charges()[0]); + std::map::iterator it = charge_ptr->Get_g_map().begin(); + for ( ; it != charge_ptr->Get_g_map().end(); it++) + { + it->second.Set_psi_to_z(exp(ln_x_value * it->first) - 1.0); + } + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type < H2O && s_x[i]->z != 0.0) + { + sum += s_x[i]->moles * charge_ptr->Get_g_map()[s_x[i]->z].Get_psi_to_z(); + } + } + if (sum < 0.0) + { + sum = 0.0; + sum1 = 0.0; + output_msg(sformatf( + "Species\tmoles\tX**z-1\tsum\tsum charge\n")); + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type < H2O && s_x[i]->z != 0.0) + { + sum += s_x[i]->moles * (pow(x_value, s_x[i]->z) - 1.0); + sum1 += s_x[i]->moles * s_x[i]->z; + output_msg(sformatf( "%s\t%e\t%e\t%e\t%e\n", + s_x[i]->name, (double) s_x[i]->moles, + (double) (pow((LDBLE) x_value, (LDBLE) s_x[i]->z) - + 1.0), (double) sum, (double) sum1)); + } + } + error_string = sformatf( "Negative sum in g_function, %e\t%e.", + (double) sum, (double) x_value); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "Solutions must be charge balanced, charge imbalance is %e\n", + (double) sum1); + error_msg(error_string, STOP); + } + + return_value = + (exp(ln_x_value * z_global) - + 1) / sqrt((x_value * x_value * mass_water_aq_x * sum)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +polint(LDBLE * xa, LDBLE * ya, int n, LDBLE xv, LDBLE * yv, LDBLE * dy) +/* ---------------------------------------------------------------------- */ +{ + int i, m, ns; + LDBLE den, dif, dift, ho, hp, w; + + ns = 1; + dif = fabs(xv - xa[1]); +/* + * Malloc work space + */ + std::vector c, d; + c.resize((size_t)n + 1); + d.resize((size_t)n + 1); + + for (i = 1; i <= n; i++) + { + dift = fabs(xv - xa[i]); + if (dift < dif) + { + ns = i; + dif = dift; + } + c[i] = ya[i]; + d[i] = ya[i]; + } + + *yv = ya[ns--]; + for (m = 1; m < n; m++) + { + for (size_t i = 1; i <= n - m; i++) + { + ho = xa[i] - xv; + hp = xa[i + m] - xv; + w = c[i + 1] - d[i]; + if ((den = ho - hp) == 0.0) + { + error_msg("In subroutine polint.", STOP); + } + den = w / den; + d[i] = hp * den; + c[i] = ho * den; + } + if (2 * ns < (n - m)) + { + *dy = c[(size_t)ns + 1]; + } + else + { + *dy = d[ns--]; + } + *yv += *dy; + +/* *yv += (*dy = (2 * ns < (n-m) ? c[ns+1] : d[ns--])); */ + } + return; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +midpnt(LDBLE x1, LDBLE x2, int n) +/* ---------------------------------------------------------------------- */ +{ + LDBLE xv, tnm, sum, del, ddel; + int it, j; + + if (n == 1) + { + midpoint_sv = (x2 - x1) * g_function(0.5 * (x1 + x2)); + return (midpoint_sv); + } + else + { + for (it = 1, j = 1; j < n - 1; j++) + it *= 3; + tnm = (LDBLE) it; + del = (x2 - x1) / (3 * tnm); + ddel = del + del; + xv = x1 + 0.5 * del; + sum = 0.0; + for (j = 1; j <= it; j++) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + sum += g_function(xv); + xv += ddel; + sum += g_function(xv); + xv += del; + } + midpoint_sv = (midpoint_sv + (x2 - x1) * sum / tnm) / 3.0; + return midpoint_sv; + } +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +qromb_midpnt(cxxSurfaceCharge *charge_ptr, LDBLE x1, LDBLE x2) +/* ---------------------------------------------------------------------- */ +{ + LDBLE ss, dss; + LDBLE sv[MAX_QUAD + 2], h[MAX_QUAD + 2]; + int j; + + h[0] = 1.0; + sv[0] = midpnt(x1, x2, 1); + for (j = 1; j < MAX_QUAD; j++) + { + sv[j] = midpnt(x1, x2, j + 1); + h[j] = h[j - 1] / 9.0; + + if (fabs(sv[j] - sv[j - 1]) <= G_TOL * fabs(sv[j])) + { + sv[j] *= charge_ptr->Get_grams() * charge_ptr->Get_specific_area() * alpha_global / F_C_MOL; /* (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + if ((x2 - 1) < 0.0) + sv[j] *= -1.0; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "Iterations in qromb_midpnt: %d\n", j)); + } + return (sv[j]); + } + + if (j >= K_POLY - 1) + { + polint(&h[j - K_POLY], &sv[j - K_POLY], K_POLY, 0.0, &ss, &dss); + if (fabs(dss) <= G_TOL * fabs(ss) || fabs(dss) < G_TOL) + { + ss *= charge_ptr->Get_grams() * charge_ptr->Get_specific_area() * alpha_global / F_C_MOL; /* (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + if ((x2 - 1) < 0.0) + ss *= -1.0; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "Iterations in qromb_midpnt: %d\n", j)); + } + return (ss); + } + } + + } + error_string = sformatf( + "\nToo many iterations integrating diffuse layer.\n"); + error_msg(error_string, STOP); + return (-999.9); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_init_g(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * calculate g for each surface + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + xd_global = exp(-2 * x[j]->master[0]->s->la * LOG_10); + /* alpha = 0.02935 @ 25; (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + /* second 1000 is liters/m**3 */ + //alpha_global = sqrt(EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // 1000.0 * tk_x * 0.5); + alpha_global = sqrt(eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + 1000.0 * tk_x * 0.5); + + if (charge_ptr->Get_g_map().size() == 0) + { + cxxSurfDL temp_g; + charge_ptr->Get_g_map()[0.0] = temp_g; + } +/* + * calculate g for given surface for each species + */ + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > HPLUS) + continue; + + if (charge_ptr->Get_g_map().find(s_x[i]->z) == charge_ptr->Get_g_map().end()) + { + cxxSurfDL temp_g; + /* save g for charge */ + if (charge_ptr->Get_grams() > 0.0) + { + temp_g.Set_g(2 * alpha_global * sqrt(mu_x) * (pow(xd_global, s_x[i]->z / 2.0) - 1) * + charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() / F_C_MOL); + temp_g.Set_dg(-s_x[i]->z); + if (use.Get_surface_ptr()->Get_only_counter_ions() && + temp_g.Get_g() < 0) + { + temp_g.Set_g(0); + temp_g.Set_dg(0); + } + } + else + { + temp_g.Set_g(0); + temp_g.Set_dg(-s_x[i]->z); + } + charge_ptr->Get_g_map()[s_x[i]->z] = temp_g; + } + + { + int is = s_x[i]->number; + assert (is < (int) s_diff_layer.size()); + // species found in diff_layer + s_diff_layer[is][charge_ptr->Get_name()].Set_g_moles(0); + s_diff_layer[is][charge_ptr->Get_name()].Set_dg_g_moles(0); + } + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\nSurface component %d: charge,\tg,\tdg\n", + (int) charge_ptr->Get_g_map().size())); + std::map::iterator it; + for (it = charge_ptr->Get_g_map().begin(); it != charge_ptr->Get_g_map().end(); it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) it->first, + (double) it->second.Get_g(), + (double) it->second.Get_dg())); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_surface_water(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * In initial surface calculation, need to calculate + * mass of water in diffuse layer. + * diffuse layer water + aqueous solution water = bulk water. + * Ionic strength is fixed, so diffuse-layer water will not change + */ + LDBLE debye_length, b, r, rd, ddl_limit, rd_limit, fraction, sum_surfs, l_s; + LDBLE damp_aq; +/* + * Debye length = 1/k = sqrt[eta*eta_zero*R*T/(2*F**2*mu_x*1000)], Dzombak and Morel, p 36 + * + * 1000 converts kJ to J; 1000 converts Liters to meter**3; debye_length is in meters. + */ + //debye_length = (EPSILON * EPSILON_ZERO * R_KJ_DEG_MOL * 1000.0 * tk_x) + // / (2. * F_C_MOL * F_C_MOL * mu_x * 1000.); + debye_length = (eps_r * EPSILON_ZERO * R_KJ_DEG_MOL * 1000.0 * tk_x) + / (2. * F_C_MOL * F_C_MOL * mu_x * 1000.); + debye_length = sqrt(debye_length); + + /* ddl is at most the fraction ddl_limit of bulk water */ + ddl_limit = use.Get_surface_ptr()->Get_DDL_limit(); + +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + { + sum_surfs = 0.0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + sum_surfs += + charge_ptr->Get_specific_area() * + charge_ptr->Get_grams(); + } + rd = debye_length * use.Get_surface_ptr()->Get_debye_lengths(); + use.Get_surface_ptr()->Set_thickness(rd); + + if (!sum_surfs) + { + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + charge_ptr->Set_mass_water(0); + } + } + else if (state == INITIAL_SURFACE) + { + /* distribute water over DDL (rd) and free pore (r - rd) */ + /* find r: free pore (m3) = pi * (r - rd)^2 * L, where L = A / (2*pi*r), + A = sum_surfs = sum of the surface areas */ + b = -2 * (rd + use.Get_solution_ptr()->Get_mass_water() / (1000 * sum_surfs)); + r = 0.5 * (-b + sqrt(b * b - 4 * rd * rd)); + /* DDL (m3) = pi * (r^2 - (r - rd)^2) * L */ + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + /* rd should be smaller than r and the ddl limit */ + if (rd > rd_limit) + { + mass_water_surfaces_x = + use.Get_solution_ptr()->Get_mass_water() * ddl_limit / (1 - ddl_limit); + r = 0.002 * (mass_water_surfaces_x + + use.Get_solution_ptr()->Get_mass_water()) / sum_surfs; + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + rd = rd_limit; + use.Get_surface_ptr()->Set_thickness(rd); + } + else + mass_water_surfaces_x = + (r * r / pow(r - rd, 2) - 1) * use.Get_solution_ptr()->Get_mass_water(); + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + l_s = charge_ptr->Get_specific_area() * charge_ptr->Get_grams(); + charge_ptr->Set_mass_water(mass_water_surfaces_x * l_s / sum_surfs); + } + } + else + { + r = 0.002 * mass_water_bulk_x / sum_surfs; + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + if (rd > rd_limit) + { + rd = rd_limit; + use.Get_surface_ptr()->Set_thickness(rd); + fraction = ddl_limit; + } + else + fraction = 1 - pow(r - rd, 2) / (r * r); + damp_aq = 1.0; + if (g_iterations > 10) + damp_aq = 0.2; + else if (g_iterations > 5) + damp_aq = 0.5; + mass_water_surfaces_x = damp_aq * fraction * mass_water_bulk_x + + (1 - damp_aq) * mass_water_surfaces_x; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + l_s = charge_ptr->Get_specific_area() * + charge_ptr->Get_grams(); + charge_ptr->Set_mass_water(mass_water_surfaces_x * l_s / sum_surfs); + } + } + } + else + { + /* take constant thickness of, default 1e-8 m (100 Angstroms) */ + mass_water_surfaces_x = 0.0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + charge_ptr->Set_mass_water(charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() * use.Get_surface_ptr()->Get_thickness() * + 1000); + mass_water_surfaces_x += charge_ptr->Get_mass_water(); + } + } + + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + else + { + /* for variable distribution of water over DDL and bulk... */ + if (state > INITIAL_SURFACE) + mass_water_aq_x = mass_water_bulk_x - mass_water_surfaces_x; + else + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sum_diffuse_layer(cxxSurfaceCharge *charge_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE mass_water_surface; + LDBLE molality, moles_excess, moles_surface; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Find position of component in list of components + */ + +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + count_elts = 0; + paren_count = 0; + mass_water_surface = charge_ptr->Get_mass_water(); + for (int j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + LDBLE g = charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + if (s_x[j]->erm_ddl != 1) + { + LDBLE ratio_aq = mass_water_surface / mass_water_aq_x; + LDBLE g2 = g / ratio_aq + 1; + g = ratio_aq * (g2 * s_x[j]->erm_ddl - 1); + } + moles_excess = mass_water_aq_x * molality * g; + moles_surface = mass_water_surface * molality + moles_excess; +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + add_elt_list(s_h2o->next_elt, mass_water_surface / gfw_water); + elt_list_combine(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_all_donnan(void) +/* ---------------------------------------------------------------------- */ +{ + bool converge; + int cd_m; + LDBLE new_g, f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq, ratio_aq_tot; + LDBLE new_g2, f_psi2, surf_chrg_eq2, psi_avg2, dif, var1; + + if (use.Get_surface_ptr() == NULL) + return (OK); + //f_sinh = sqrt(8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // tk_x * mu_x); + f_sinh = sqrt(8000.0 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + tk_x * mu_x); +/* + * calculate g for each surface... + */ + if (!calculating_deriv || use.Get_surface_ptr()->Get_debye_lengths()) // DL_pitz + initial_surface_water(); + converge = TRUE; + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( "Calc_all_g, X[%d]\n", j)); +/* + * sum eq of each charge number in solution... + */ + std::map::iterator it; + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + it->second = 0.0; + } + charge_group_map.clear(); + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > HPLUS) + continue; + charge_group_map[s_x[i]->z] += s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + /* find surface charge from potential... */ + A_surf = charge_ptr->Get_specific_area() * charge_ptr->Get_grams(); + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + f_psi = x[(size_t)j + 2]->master[0]->s->la * LOG_10; /* -FPsi/RT */ + f_psi = f_psi / 2; + cd_m = 1; + } else + { + f_psi = x[j]->master[0]->s->la * LOG_10; + cd_m = -1; + } + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + if (fabs(surf_chrg_eq) > 5e3) + { + surf_chrg_eq = (surf_chrg_eq < 0 ? -5e3 : 5e3); + var1 = surf_chrg_eq / (A_surf * f_sinh / F_C_MOL); + var1 = (var1 + sqrt(var1 * var1 + 1)); + f_psi = (var1 > 1e-8 ? log(var1) : -18.4); + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + x[j]->master[0]->s->la = f_psi / LOG_10; + } + /* also for the derivative... */ + dif = 1e-5; + f_psi2 = f_psi + dif; + surf_chrg_eq2 = A_surf * f_sinh * sinh(f_psi2) / F_C_MOL; + + /* find psi_avg that matches surface charge... */ + psi_avg = calc_psi_avg(charge_ptr, surf_chrg_eq); + psi_avg2 = calc_psi_avg(charge_ptr, surf_chrg_eq2); + + /*output_msg(sformatf( "psi's %e %e %e\n", f_psi, psi_avg, surf_chrg_eq)); */ + + /* fill in g's */ + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + ratio_aq_tot = charge_ptr->Get_mass_water() / mass_water_bulk_x; + + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + LDBLE z = it->first; + if (!ratio_aq) + { + charge_ptr->Get_g_map()[z].Set_g(0); + charge_ptr->Get_g_map()[z].Set_dg(0); + charge_ptr->Get_z_gMCD_map()[z] = 0; + converge = true; + continue; + } + new_g = ratio_aq * (exp(cd_m * z * psi_avg) - 1); + if (use.Get_surface_ptr()->Get_only_counter_ions() && surf_chrg_eq * z > 0) + //((surf_chrg_eq < 0 && z < 0) + // || (surf_chrg_eq > 0 && z > 0))) + new_g = -ratio_aq; + if (new_g <= -ratio_aq) + new_g = -ratio_aq + G_TOL * 1e-3; + new_g2 = ratio_aq * (exp(cd_m * z * psi_avg2) - 1); + if (use.Get_surface_ptr()->Get_only_counter_ions() && surf_chrg_eq * z > 0) + //((surf_chrg_eq < 0 && z < 0) + // || (surf_chrg_eq > 0 && z > 0))) + new_g2 = -ratio_aq; + if (new_g2 <= -ratio_aq) + new_g2 = -ratio_aq + G_TOL * 1e-3; + if (fabs(new_g) >= 1) + { + if (fabs((new_g - charge_ptr->Get_g_map()[z].Get_g()) / new_g) > convergence_tolerance) + { + converge = FALSE; + } + } + else + { + if (fabs(new_g - charge_ptr->Get_g_map()[z].Get_g()) > convergence_tolerance) + { + converge = FALSE; + } + } + charge_ptr->Get_g_map()[z].Set_g(new_g); + if (new_g != 0) + { + charge_ptr->Get_g_map()[z].Set_dg((new_g2 - new_g) / dif); + } + else + { + charge_ptr->Get_g_map()[z].Set_dg(-z); + } + /* save Boltzmann factor * water fraction for MCD calc's in transport */ + if (converge) + { + if (use.Get_surface_ptr()->Get_only_counter_ions()) + { + if (surf_chrg_eq * z > 0) // co-ions are not in the DL + charge_ptr->Get_z_gMCD_map()[z] = 0; + else // assume that counter-ions have the free water conc for diffusion + charge_ptr->Get_z_gMCD_map()[z] = ratio_aq_tot; + } + else + charge_ptr->Get_z_gMCD_map()[z] = (new_g / ratio_aq + 1) * ratio_aq_tot; + } + } + if (debug_diffuse_layer == TRUE) + { + std::string name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf( + "\nDonnan all on %s (%d): charge, \tg, \tdg, Psi_surface = %8f V. \n", + name.c_str(), (int) charge_ptr->Get_g_map().size(), + x[j]->master[0]->s->la * 2 * LOG_10 * R_KJ_DEG_MOL * + tk_x / F_KJ_V_EQ)); + for (std::map::iterator i_it = charge_ptr->Get_g_map().begin(); + i_it != charge_ptr->Get_g_map().end(); i_it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) i_it->first, + (double) i_it->second.Get_g(), + (double) i_it->second.Get_dg())); + } + } + } + return (converge); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_init_donnan(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq; + + if (use.Get_surface_ptr() == NULL) + return (OK); + f_sinh = + //sqrt(8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // tk_x * mu_x); + sqrt(8000.0 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + tk_x * mu_x); + if (convergence_tolerance >= 1e-8) + { + G_TOL = 1e-9; + } + else + { + G_TOL = 1e-13; + } +/* + * sum eq of each charge number in solution... + */ + charge_group_map.clear(); + charge_group_map[0.0] = 0.0; + + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > HPLUS) + continue; + if (charge_group_map.find(s_x[i]->z) != charge_group_map.end()) + { + charge_group_map.find(s_x[i]->z)->second += s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + else + { + charge_group_map[s_x[i]->z] = s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + } +/* + * calculate g for each surface... + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + charge_ptr->Get_g_map().clear(); + + /* find surface charge from potential... */ + A_surf = charge_ptr->Get_specific_area() * charge_ptr->Get_grams(); + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + f_psi = x[(size_t)j + 2]->master[0]->s->la * LOG_10; /* -FPsi/RT */ + f_psi = f_psi / 2; + } else + f_psi = x[j]->master[0]->s->la * LOG_10; + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + + /* find psi_avg that matches surface charge... */ +/* psi_avg = calc_psi_avg (0); + appt 7/9/8... may have to change above one */ + psi_avg = calc_psi_avg(charge_ptr, 0 * surf_chrg_eq); + + /* fill in g's */ + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + + std::map::iterator kit; + for (kit = charge_group_map.begin(); kit != charge_group_map.end(); kit++) + { + LDBLE z = kit->first; + LDBLE eq = kit->second; + + charge_ptr->Get_g_map()[z].Set_g(ratio_aq * (exp(-z * psi_avg) - 1)); + + if (use.Get_surface_ptr()->Get_only_counter_ions() + && ((surf_chrg_eq < 0 && z < 0) + || (surf_chrg_eq > 0 && z > 0))) + charge_ptr->Get_g_map()[z].Set_g(-ratio_aq); + + if (charge_ptr->Get_g_map()[z].Get_g() != 0) + { + charge_ptr->Get_g_map()[z].Set_dg(-A_surf * f_sinh * cosh(f_psi) / + (eq * F_C_MOL)); + } + else + { + charge_ptr->Get_g_map()[z].Set_dg(-z); + } + /* save g for species */ + for (int i = 0; i < (int)this->s_x.size(); i++) + { + int is = s_x[i]->number; + assert (is < (int) s_diff_layer.size()); + + s_diff_layer[is][charge_ptr->Get_name()].Set_g_moles(0.0); + s_diff_layer[is][charge_ptr->Get_name()].Set_dg_g_moles(0.0); + } + } + if (debug_diffuse_layer == TRUE) + { + std::string name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf( + "\nDonnan init on %s : charge, \tg, \tdg, Psi_surface = %8f V. \n", + name.c_str(), + x[j]->master[0]->s->la * 2 * LOG_10 * R_KJ_DEG_MOL * + tk_x / F_KJ_V_EQ)); + for (std::map::iterator i_it = charge_ptr->Get_g_map().begin(); + i_it != charge_ptr->Get_g_map().end(); i_it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) i_it->first, + (double) i_it->second.Get_g(), + (double) i_it->second.Get_dg())); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_psi_avg(cxxSurfaceCharge *charge_ptr, LDBLE surf_chrg_eq) +/* ---------------------------------------------------------------------- */ +{ +/* + * calculate the average (F * Psi / RT) that lets the DL charge counter the surface charge + */ + LDBLE fd, fd1, p, temp, ratio_aq; + + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + p = 0; + if (surf_chrg_eq == 0 || ratio_aq == 0) + return (0.0); + else if (surf_chrg_eq < 0) + p = -0.5 * log(-surf_chrg_eq * ratio_aq / mu_x + 1); + else if (surf_chrg_eq > 0) + p = 0.5 * log(surf_chrg_eq * ratio_aq / mu_x + 1); +/* + * Optimize p in SS{s_x[i]->moles * z_i * g(p)} = -surf_chrg_eq + * g(p) = exp(-p * z_i) * ratio_aq + * Elsewhere in PHREEQC, g is the excess, after subtraction of conc's for p = 0: + * g(p) = (exp(-p *z_i) - 1) * ratio_aq + */ + int l_iter = 0; + do + { + fd = surf_chrg_eq; + fd1 = 0.0; + std::map::iterator it; + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + LDBLE z = it->first; + if (!z || (use.Get_surface_ptr()->Get_only_counter_ions() && surf_chrg_eq * z > 0)) + continue; + LDBLE eq = it->second; + /* multiply with ratio_aq for multiplier options cp and cm + in calc_all_donnan (not used now)... */ + temp = exp(-z * p) * ratio_aq; + + fd += eq * temp; + fd1 -= z * eq * temp; + } + fd /= -fd1; + p += (fd > 1) ? 1 : ((fd < -1) ? -1 : fd); + if (fabs(p) < G_TOL) + p = 0.0; + l_iter++; + if (l_iter > 50) + { + pr.all = TRUE; + pr.exchange = TRUE; + pr.headings = TRUE; + pr.pp_assemblage = TRUE; + pr.species = TRUE; + pr.surface = TRUE; + pr.totals = TRUE; + print_all(); + error_string = sformatf( + "\nToo many iterations in subroutine calc_psi_avg; surface charge = %12.4e; surface water = %12.4e.\n", + (double)surf_chrg_eq, (double)charge_ptr->Get_mass_water()); + error_msg(error_string, STOP); + } + } + while (fabs(fd) > 1e-12 && p != 0.0); + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( + "iter in calc_psi_avg = %d. g(+1) = %8f. surface charge = %12.4e.\n", + l_iter, (double) (exp(-p) - 1), (double) surf_chrg_eq)); + + return (p); +} diff --git a/phreeqcpp/inverse.cpp b/phreeqcpp/inverse.cpp new file mode 100644 index 00000000..81a33781 --- /dev/null +++ b/phreeqcpp/inverse.cpp @@ -0,0 +1,5153 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Utils.h" +#include "Solution.h" +#include "SolutionIsotope.h" + + +#define MAX_MODELS 20 +#define MIN_TOTAL_INVERSE 1e-14 + +/* variables local to module */ +#define SCALE_EPSILON .0009765625 +#define SCALE_WATER 1. +#define SCALE_ALL 1. + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_models(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of inverse models, make calculations + * for any marked "new". + */ + int n/*, print1*/; + char string[MAX_LENGTH]; + if (count_inverse <= 0) return OK; + // Revert to previous headings after inverse modeling + std::vector old_headings; + + state = INVERSE; + dl_type_x = cxxSurface::NO_DL; + + for (n = 0; n < count_inverse; n++) + { + if (inverse[n].new_def == TRUE) + { +/* + * dump .lon file + */ + if (inverse[n].netpath != NULL) + dump_netpath(&inverse[n]); + +/* + * open .pat file + */ + if (inverse[n].pat != NULL) + { + strcpy(string, inverse[n].pat); + if (replace(".pat", ".pat", string) != TRUE) + { + strcat(string, ".pat"); + } + netpath_file = fopen(string, "w"); + if (netpath_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", string); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + count_inverse_models = 0; + count_pat_solutions = 0; + /* Header */ + fprintf(netpath_file, "2.14 # File format\n"); + } +/* + * Fill in stucture "use". + */ + use.Set_inverse_in(true); + use.Set_inverse_ptr(&inverse[n]); + use.Set_n_inverse_user(inverse[n].n_user); +/* + * Initial prints + */ + error_string = sformatf( + "Beginning of inverse modeling %d calculations.", + inverse[n].n_user); + dup_print(error_string, TRUE); + + if (inverse[n].mp == TRUE) + { + output_msg(sformatf( + "Using Cl1MP multiprecision optimization routine.\n")); + + } + else + { + output_msg(sformatf( + "Using Cl1 standard precision optimization routine.\n")); + } + status(0, NULL); + +/* + * Setup and solve + */ + count_calls = 0; + setup_inverse(&(inverse[n])); + punch_model_heading(&inverse[n]); + solve_inverse(&(inverse[n])); + if (inverse[n].isotope_unknowns.size() > 0) + { + inverse[n].isotope_unknowns.clear(); + } + inverse[n].new_def = FALSE; + if (inverse[n].pat != NULL) + { + fclose(netpath_file); + netpath_file = NULL; + } + } + } + + //user_punch_count_headings = (int) old_headings.size(); + //user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings, + // (size_t) (user_punch_count_headings + 1) * sizeof(char *)); + //if (user_punch_headings == NULL) + // malloc_error(); + //for (i = 0; i < user_punch_count_headings; i++) + //{ + // user_punch_headings[i] = string_hsave(old_headings[i].c_str()); + //} + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_inverse(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in array for an inverse problem + */ + int i, j, k, i_alk, i_carb; + size_t max; + size_t count_rows_t; + size_t column, row; + int temp; + LDBLE isotope_number; + LDBLE f, coef, cb, conc; + char token[MAX_LENGTH]; + class phase *phase_ptr; + cxxSolution *solution_ptr; + CReaction *rxn_ptr; + class master *master_ptr; +/* + * Determine array sizes, row and column positions + */ + toler = inv_ptr->tolerance; + if (inv_ptr->mp == TRUE) + { + toler = inv_ptr->mp_tolerance; + } +/* + * Alkalinity derivatives with pH and carbon + */ + carbon = 1; + temp = pr.status; + pr.status = FALSE; + + // current_selected_output is NULL at this point + carbon_derivs(inv_ptr); + pr.status = temp; + + //current_selected_output->Set_inverse(temp_inv); + state = INVERSE; +/* + * tidy isotopes if necessary + */ + if (inv_ptr->isotopes.size() > 0) + { + set_isotope_unknowns(inv_ptr); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + check_isotopes(inv_ptr); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + } + +/* + * count unknowns + */ + max_column_count = inv_ptr->elts.size() * inv_ptr->count_solns + /* epsilons */ + inv_ptr->count_solns + /* solutions */ + inv_ptr->phases.size() + /* phases */ + inv_ptr->count_redox_rxns + /* redox reactions */ + carbon * inv_ptr->count_solns + /* pH */ + 1 + /* water */ + inv_ptr->isotope_unknowns.size() * inv_ptr->count_solns + /* isotopes in solution */ + inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* isotopes in phases */ + 1 + 1; /* rhs, ineq */ + count_unknowns = max_column_count - 2; + col_phases = inv_ptr->count_solns; + col_redox = col_phases + inv_ptr->phases.size(); + col_epsilon = col_redox + inv_ptr->count_redox_rxns; + col_ph = col_epsilon + inv_ptr->elts.size() * inv_ptr->count_solns; + col_water = col_ph + carbon * inv_ptr->count_solns; + col_isotopes = col_water + 1; + col_phase_isotopes = + col_isotopes + inv_ptr->isotope_unknowns.size() * inv_ptr->count_solns; + max_row_count = inv_ptr->count_solns * inv_ptr->elts.size() + /* optimize */ + carbon * inv_ptr->count_solns + /* optimize ph */ + 1 + /* optimize water */ + inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + /* optimize isotopes */ + inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* optimize phase isotopes */ + inv_ptr->elts.size() + /* mass balances */ + 1 + 1 + /* fractions, init and final */ + inv_ptr->count_solns + /* charge balances */ + carbon * inv_ptr->count_solns + /* dAlk = dC + dph */ + inv_ptr->isotopes.size() + /* isotopes */ + 2 * inv_ptr->count_solns * inv_ptr->elts.size() + /* epsilon constraints */ + 2 * carbon * inv_ptr->count_solns + /* epsilon on ph */ + 2 + /* epsilon for water */ + 2 * inv_ptr->isotope_unknowns.size() * inv_ptr->count_solns + /* epsilon for isotopes */ + 2 * inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* epsilon for isotopes in phases */ + 2; /* work space */ + + row_mb = inv_ptr->count_solns * inv_ptr->elts.size() + + carbon * inv_ptr->count_solns + 1 + + inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + + inv_ptr->isotopes.size() * inv_ptr->phases.size(); + row_fract = row_mb + inv_ptr->elts.size(); + row_charge = row_fract + 2; + row_carbon = row_charge + inv_ptr->count_solns; + row_isotopes = row_carbon + carbon * inv_ptr->count_solns; + row_epsilon = row_isotopes + inv_ptr->isotopes.size(); +/* The next three are not right, some rows of epsilon are deleted */ +/* + row_ph_epsilon = row_epsilon + 2 * inv_ptr->count_solns * inv_ptr->count_elts; + row_water_epsilon = row_ph + 2 * carbon * inv_ptr->count_solns; + row_isotope_epsilon + row_isotope_phase_epsilon + */ +/* + * Malloc space for arrays + */ + my_array.resize(max_column_count * max_row_count); + array1.resize(max_column_count * max_row_count); + col_name.resize(max_column_count); + row_name.resize(max_row_count); + delta.resize(max_column_count); + inv_delta1.resize(max_column_count); + delta2.resize(max_column_count); + delta3.resize(max_column_count); + delta_save.resize(max_column_count); + min_delta.resize(max_column_count); + max_delta.resize(max_column_count); + inv_res.resize(max_row_count); + + if (max_column_count < max_row_count) + { + max = max_row_count; + } + else + { + max = max_column_count; + } + inv_zero.resize((size_t) max); +/* + * Define inv_zero and inv_zero array, delta + */ + for (i = 0; i < (int) max; i++) + inv_zero[i] = 0.0; + + memcpy((void *) &(delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + for (i = 0; i < max_row_count; i++) + { + memcpy((void *) &(my_array[(size_t)i * max_column_count]), (void *) &(inv_zero[0]), + max_column_count * sizeof(LDBLE)); + } +/* + * begin filling array + */ + count_rows = 0; +/* + * optimization + */ + count_optimize = inv_ptr->count_solns * inv_ptr->elts.size() + /* optimize */ + carbon * inv_ptr->count_solns + /* optimize ph */ + 1 + /* optimize water */ + inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + /* optimize isotopes */ + inv_ptr->isotopes.size() * inv_ptr->phases.size(); /* optimize phase isotopes */ + + for (i = 0; i < count_optimize; i++) + { + row_name[count_rows] = string_hsave("optimize"); + count_rows++; + } + write_optimize_names(inv_ptr); +/* + * equalities + */ + +/* + * Mass_balance: solution data + */ + + /* initialize master species */ + for (i = 0; i < (int)master.size(); i++) + { + master[i]->in = -1; + if (strstr(master[i]->elt->name, "Alk") == master[i]->elt->name) + { + master_alk = master[i]; + } + } + /* mark master species included in model, write row names */ + count_rows_t = count_rows; + i_alk = -1; + i_carb = -1; + for (i = 0; i < inv_ptr->elts.size(); i++) + { + master_ptr = inv_ptr->elts[i].master; + if (master_ptr == master_alk) + i_alk = i; + if (strcmp(master_ptr->elt->name, "C(4)") == 0) + i_carb = i; + inv_ptr->elts[i].master->in = (int)count_rows_t; + row_name[count_rows_t] = inv_ptr->elts[i].master->elt->name; + count_rows_t++; + } + /* put concentrations in array */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + xsolution_zero(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (solution_ptr == NULL) + { + error_string = sformatf( "Solution number %d not found.", + inv_ptr->solns[i]); + error_msg(error_string, STOP); + } + /* write master species concentrations */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total += jit->second; + /* List elements not included in model */ + if (master_ptr->in < 0) + { + error_string = sformatf( + "%s is included in solution %d, but is not included as a mass-balance constraint.", + jit->first.c_str(), + inv_ptr->solns[i]); + warning_msg(error_string); + } + } + master_alk->total = solution_ptr->Get_total_alkalinity(); + f = 1.0; + if (i == (inv_ptr->count_solns - 1)) + { + f = -1.0; + } + column = i; + sprintf(token, "soln %d", i); + col_name[column] = string_hsave(token); + for (j = 0; j < (int)master.size(); j++) + { + if (master[j]->in >= 0) + { + my_array[(size_t)master[j]->in * max_column_count + (size_t)i] = + f * master[j]->total; + if (master[j]->s == s_eminus) + { + my_array[(size_t)master[j]->in * max_column_count + (size_t)i] = 0.0; + } + } + } + /* calculate charge balance for elements in model */ + cb = 0; + for (j = 0; j < (int)master.size(); j++) + { + if (master[j]->in >= 0) + { + if (master[j]->s == s_eminus) + { + coef = 0.0; + } + else if (master[j] == master_alk) + { + coef = -1.0; + } + else + { + coef = master[j]->s->z + master[j]->s->alk; + } + cb += coef * master[j]->total; + } + } + if (fabs(cb) < toler) + cb = 0.0; + my_array[((size_t)row_charge + (size_t)i) * max_column_count + (size_t)i] = cb; + } + +/* mass_balance: phase data */ + + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + phase_ptr = inv_ptr->phases[i].phase; + rxn_ptr = &phase_ptr->rxn_s; + column = col_phases + i; + col_name[column] = phase_ptr->name; + for (j = 1; rxn_ptr->token[j].s != NULL; j++) + { + if (rxn_ptr->token[j].s->secondary != NULL) + { + master_ptr = rxn_ptr->token[j].s->secondary; + } + else + { + master_ptr = rxn_ptr->token[j].s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Setup_inverse, reaction for phase, %s.", + phase_ptr->name); + error_msg(error_string, STOP); + } + if (master_ptr->s == s_hplus) + continue; + if (master_ptr->s == s_h2o) + { + row = row_fract; +/* turn off h2o from minerals in water mass balance */ + if (inv_ptr->mineral_water != TRUE) + continue; + + } + else + { + row = master_ptr->in; + } + /* e- has coef of 0 for some reason */ + coef = master_ptr->coef; + if (coef <= 0) + coef = 1.0; + my_array[(size_t)row * max_column_count + (size_t)column] = + rxn_ptr->token[j].coef * coef; + } + row = master_alk->in; /* include alkalinity for phase */ + my_array[(size_t)row * max_column_count + (size_t)column] = calc_alk(*rxn_ptr); + } + +/* mass balance: redox reaction data */ + + k = 0; + for (i = 0; i < inv_ptr->elts.size(); i++) + { + if (inv_ptr->elts[i].master->s->primary == NULL) + { + coef = inv_ptr->elts[i].master->coef; + rxn_ptr = &inv_ptr->elts[i].master->rxn_primary; + column = col_redox + k; + col_name[column] = inv_ptr->elts[i].master->elt->name; + k++; + for (j = 0; rxn_ptr->token[j].s != NULL; j++) + { + if (rxn_ptr->token[j].s->secondary != NULL) + { + master_ptr = rxn_ptr->token[j].s->secondary; + } + else + { + master_ptr = rxn_ptr->token[j].s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Subroutine setup_inverse, element not found, %s.", + rxn_ptr->token[j].s->name); + error_msg(error_string, STOP); + } + if (master_ptr->s == s_hplus) + continue; + if (master_ptr->s == s_h2o) + { + row = row_fract; +/* turn off h2o from minerals in water mass balance */ + if (inv_ptr->mineral_water != TRUE) + continue; + } + else + { + row = master_ptr->in; + } + assert(row * max_column_count + column < max_column_count * max_row_count); + assert(row >= 0); + assert(column >= 0); + my_array[(size_t)row * max_column_count + (size_t)column] = + rxn_ptr->token[j].coef; + /* if coefficient of element is not 1.0 in master species */ + if (j != 0) + my_array[(size_t)row * max_column_count + (size_t)column] /= coef; + } + row = master_alk->in; /* include alkalinity for redox reaction */ + my_array[(size_t)row * max_column_count + (size_t)column] = + (calc_alk(*rxn_ptr) - inv_ptr->elts[i].master->s->alk) / coef; + } + } + +/* mass-balance: epsilons */ + + column = col_epsilon; + for (i = 0; i < inv_ptr->elts.size(); i++) + { + row = inv_ptr->elts[i].master->in; + for (j = 0; j < inv_ptr->count_solns; j++) + { + if (j < (inv_ptr->count_solns - 1)) + { + my_array[(size_t)row * max_column_count + (size_t)column] = 1.0; + } + else + { + my_array[(size_t)row * max_column_count + (size_t)column] = -1.0; + } + if (inv_ptr->elts[i].master->s == s_eminus) + { + my_array[(size_t)row * max_column_count + (size_t)column] = 0.0; + } + sprintf(token, "%s %d", row_name[row], j); + col_name[column] = string_hsave(token); + column++; + } + } + count_rows += inv_ptr->elts.size(); + +/* put names in col_name for ph */ + + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "ph %d", i); + col_name[column] = string_hsave(token); + column++; + } +/* put names in col_name for water */ + + sprintf(token, "water"); + col_name[column] = string_hsave(token); + column++; + +/* put names of isotopes in col_name */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + sprintf(token, "%d%s %d", + (int) inv_ptr->isotope_unknowns[j].isotope_number, + inv_ptr->isotope_unknowns[j].elt_name, i); + col_name[column] = string_hsave(token); + column++; + } + } + +/* put phase isotopes in col_name */ + + if (inv_ptr->isotopes.size() > 0) + { + /* isotopes of phases phases */ + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + for (j = 0; j < inv_ptr->isotopes.size(); j++) + { + sprintf(token, "%d%s %s", + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name, + inv_ptr->phases[i].phase->name); + col_name[column] = string_hsave(token); + column++; + } + } + } +/* + * Initial solution mixing fractions or water mass balance + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (i < inv_ptr->count_solns - 1) + { + my_array[count_rows * max_column_count + (size_t)i] = + 1.0 / gfw_water * solution_ptr->Get_mass_water(); + } + else + { + my_array[count_rows * max_column_count + (size_t)inv_ptr->count_solns - 1] = + -1.0 / gfw_water * solution_ptr->Get_mass_water(); + } + } + /* coefficient for water uncertainty */ + if (inv_ptr->water_uncertainty > 0) + { + my_array[count_rows * max_column_count + (size_t)col_water] = 1.0; + } + row_name[count_rows] = string_hsave("H2O"); + row_water = count_rows; + count_rows++; + +/* + * Final solution fraction equals 1.0 + */ + + my_array[count_rows * max_column_count + (size_t)inv_ptr->count_solns - 1] = 1.0; + my_array[count_rows * max_column_count + count_unknowns] = 1.0; + row_name[count_rows] = string_hsave("fract, final"); + count_rows++; + +/* + * Charge balance: + */ + + for (i = 0; i < inv_ptr->count_solns; i++) + { +/* solution_ptr = solution_bsearch(inv_ptr->solns[i], &j, TRUE); */ +/* array[count_rows * max_column_count + (size_t)i] = solution_ptr->cb; */ + for (j = 0; j < inv_ptr->elts.size(); j++) + { + column = col_epsilon + j * inv_ptr->count_solns + i; + coef = + inv_ptr->elts[j].master->s->z + + inv_ptr->elts[j].master->s->alk; + if (inv_ptr->elts[j].master == master_alk) + { + coef = -1.0; + } + my_array[count_rows * max_column_count + (size_t)column] = coef; + if (inv_ptr->elts[j].master->s == s_eminus) + { + my_array[count_rows * max_column_count + (size_t)column] = 0.0; + } + } + sprintf(token, "%s %d", "charge", i); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * dC = (dC/dph)*dph + (dC/dAlk)*dAlk for each solution + */ +/* + * dAlk = (dAlk/dC)*dC + (dAlk/dpH)*dpH for each solution + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (inv_ptr->dalk_dph[i] != 0 || inv_ptr->dalk_dc[i] != 0) + { + column = col_ph + i; + my_array[count_rows * max_column_count + (size_t)column] = + inv_ptr->dalk_dph[i]; + column = col_epsilon + i_alk * inv_ptr->count_solns + i; + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + column = col_epsilon + i_carb * inv_ptr->count_solns + i; + my_array[count_rows * max_column_count + (size_t)column] = + inv_ptr->dalk_dc[i]; + } + sprintf(token, "%s %d", "dAlk", i); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * Isotope mass balances + */ + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + if (inv_ptr->isotopes.size() != 0) + { + for (size_t j = 0; j < inv_ptr->isotopes.size(); j++) + { + isotope_balance_equation(inv_ptr, (int)count_rows, (int)j); + sprintf(token, "%d%s", (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities + */ + row_epsilon = count_rows; + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->elts.size(); j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + column = col_epsilon + j * inv_ptr->count_solns + i; + +/* calculate magnitude of bound */ + + coef = inv_ptr->elts[j].uncertainties[i]; + if (coef <= 0.0) + { + coef = -coef; + } + else + { + coef = my_array[(size_t)inv_ptr->elts[j].master->in * + max_column_count + (size_t)i] * coef; + coef = fabs(coef); + } + + if (coef < toler) + coef = 0; + +/* zero column if uncertainty is zero */ + if (coef == 0.0) + { + for (k = 0; k < count_rows; k++) + { + my_array[(size_t)k * max_column_count + (size_t)column] = 0.0; + } + continue; + } + +/* this statement probably obviates some of the following logic. */ +/* coef += toler; */ + +/* scale epsilon optimization equation */ + + if (coef < toler) + { + my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = + SCALE_EPSILON / toler; + } + else + { + my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = + SCALE_EPSILON / coef; + } + +/* set upper limit of change in positive direction */ + if (coef < toler) + { + coef = toler; + f = 10; + } + else + { + f = 1.0; + } + my_array[count_rows * max_column_count + (size_t)column] = 1.0 * f; + my_array[count_rows * max_column_count + (size_t)i] = -coef * f; + sprintf(token, "%s %s", inv_ptr->elts[j].master->elt->name, "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + conc = my_array[(size_t)inv_ptr->elts[j].master->in * max_column_count + (size_t)i]; + + /* if concentration is zero, only positive direction allowed */ + if (conc == 0.0) + { + delta[column] = 1.0; + continue; + } + /* if uncertainty is less than tolerance, set uncertainty to toler */ + if (coef <= toler) + { +/* f = 10 * toler / coef; */ + coef = toler; + f = 10; + } + else + { + f = 1.0; + } + /* if uncertainty is greater than concentration, + maximum negative is equal to concentrations, + except alkalinity */ + if (coef > fabs(conc) && + (strstr(inv_ptr->elts[j].master->elt->name, "Alkalinity") != + inv_ptr->elts[j].master->elt->name)) + coef = fabs(conc) + toler; + + my_array[count_rows * max_column_count + (size_t)i] = -coef * f; + my_array[count_rows * max_column_count + (size_t)column] = -1.0 * f; + sprintf(token, "%s %s", inv_ptr->elts[j].master->elt->name, + "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities for pH + */ + /* row_ph_epsilon = count_rows; */ + if (inv_ptr->carbon == TRUE) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + column = col_ph + i; + coef = inv_ptr->ph_uncertainties[i]; + +/* scale epsilon in optimization equation */ + + my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = + SCALE_EPSILON / coef; + +/* set upper limit of change in positive direction */ + + my_array[count_rows * max_column_count + (size_t)column] = 1.0; + my_array[count_rows * max_column_count + (size_t)i] = -coef; + sprintf(token, "%s %s", "pH", "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + my_array[count_rows * max_column_count + (size_t)i] = -coef; + sprintf(token, "%s %s", "pH", "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities for water + */ + column = col_water; + coef = inv_ptr->water_uncertainty; + /* row_water_epsilon = count_rows; */ + if (coef > 0.0) + { +/* set upper limit of change in positive direction */ + my_array[count_rows * max_column_count + (size_t)column] = 1.0; + my_array[count_rows * max_column_count + count_unknowns] = coef; + sprintf(token, "%s %s", "water", "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + my_array[count_rows * max_column_count + count_unknowns] = coef; + sprintf(token, "%s %s", "water", "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * inequalities for isotopes + */ + row_isotope_epsilon = count_rows; + if (inv_ptr->isotopes.size() > 0) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + for (j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + column = + col_isotopes + (i * inv_ptr->isotope_unknowns.size()) + j; + master_ptr = inv_ptr->isotope_unknowns[j].master; + isotope_number = inv_ptr->isotope_unknowns[j].isotope_number; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + class master *master_kit = master_bsearch(kit->second.Get_elt_name().c_str()); + if (master_kit == master_ptr && + kit->second.Get_isotope_number() == + isotope_number) + { + coef = kit->second.Get_x_ratio_uncertainty(); + +/* scale epsilon in optimization equation */ + + my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + + (size_t)column] = SCALE_EPSILON / coef; + +/* set upper limit of change in positive direction */ + my_array[count_rows * max_column_count + (size_t)column] = 1.0; + my_array[count_rows * max_column_count + (size_t)i] = -coef; + sprintf(token, "%d%s %s", + (int) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + my_array[count_rows * max_column_count + (size_t)i] = -coef; + sprintf(token, "%d%s %s", + (int) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + break; + } + } + } + } + } +/* + * inequalities for isotopes in phases + */ + /* row_isotope_phase_epsilon = count_rows; */ + phase_isotope_inequalities(inv_ptr); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } +/* + * Set non-negativity constraints + */ + + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + if (inv_ptr->phases[i].constraint == PRECIPITATE) + { + delta[(size_t)col_phases + (size_t)i] = -1.0; + } + else if (inv_ptr->phases[i].constraint == DISSOLVE) + { + delta[(size_t)col_phases + (size_t)i] = 1.0; + } + } + for (i = 0; i < (inv_ptr->count_solns - 1); i++) + { + delta[i] = 1.0; + } +/* + * Scale water equation + */ + for (i = 0; i < max_column_count; i++) + { + my_array[(size_t)row_water * max_column_count + (size_t)i] *= SCALE_WATER; + } +/* + * Arrays are complete + */ + if (debug_inverse == TRUE) + { + for (i = 0; i < count_unknowns; i++) + { + output_msg(sformatf( "%d\t%s\n", i, col_name[i])); + } + for (i = 0; i < count_rows; i++) + { + k = 0; + output_msg(sformatf( "%d\t%s\n", i, row_name[i])); + for (j = 0; j < count_unknowns + 1; j++) + { + if (k > 7) + { + output_msg(sformatf( "\n")); + k = 0; + } + output_msg(sformatf("%11.2e", + (double)my_array[(size_t)i * max_column_count + (size_t)j])); + k++; + } + if (k != 0) + { + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "row_mb %d\n", row_mb)); + output_msg(sformatf( "row_fract %d\n", row_fract)); + output_msg(sformatf( "row_charge %d\n", row_charge)); + output_msg(sformatf( "row_carbon %d\n", row_carbon)); + output_msg(sformatf( "row_isotopes %d\n", row_isotopes)); + output_msg(sformatf( "row_epsilon %d\n", row_epsilon)); + + output_msg(sformatf( "col_phases %d\n", col_phases)); + output_msg(sformatf( "col_redox %d\n", col_redox)); + output_msg(sformatf( "col_epsilon %d\n", col_epsilon)); + output_msg(sformatf( "col_ph %d\n", col_ph)); + output_msg(sformatf( "col_water %d\n", col_water)); + output_msg(sformatf( "col_isotopes %d\n", col_isotopes)); + output_msg(sformatf( "col_phase_isotopes %d\n", + col_phase_isotopes)); + output_msg(sformatf( "count_unknowns %d\n", count_unknowns)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solve_inverse(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Exhaustively search for mass-balance models with two options + * -minimal on or off + * -range on or off + * + */ + int i, j, n; + int quit, print, first; + int first_of_model_size, model_size; + unsigned long minimal_bits, good_bits; + char token[MAX_LENGTH]; + + n = (int)count_unknowns; /* columns in A, C, E */ + klmd = (max_row_count - 2); + nklmd = (n + klmd); + n2d = (size_t)n + 2; + + max_good = MAX_MODELS; + max_bad = MAX_MODELS; + max_minimal = MAX_MODELS; + + good.resize(max_good); + count_good = 0; + + bad.resize(max_bad); + count_bad = 0; + + minimal.resize(max_minimal); + count_minimal = 0; + + col_back.resize(max_column_count); + row_back.resize(max_row_count); + +/* + * Allocate space for arrays + */ + inv_cu.resize( 2 * (size_t)nklmd); + memset(&inv_cu[0], 0, ((2 * nklmd * sizeof(LDBLE)))); + inv_iu.resize(2 * nklmd); + inv_is.resize(klmd); + + for (i = 0; i < 79; i++) + token[i] = '='; + token[79] = '\0'; +/* + * Set solutions, largest bit is final solution, smallest bit is initial solution 1 + * Set phases, largest bit is last phase, smallest bit is first phase + * Set current bits to complete list. + */ + soln_bits = 0; + if (inv_ptr->count_solns + inv_ptr->phases.size() > 32) + { + error_msg + ("For inverse modeling, sum of initial solutions and phases must be <= 32.\n\tFor all reasonable calculations, the sum should be much less than 32.", + STOP); + } + for (size_t i = inv_ptr->count_solns; i > 0; i--) + { + temp_bits = 1 << (i - 1); + soln_bits += temp_bits; + } + if (check_solns(inv_ptr) == ERROR) + { + error_msg("Calculations terminating.", STOP); + } +/* + * solutions are in highest bits, phases are in lower bits; + */ +/* + * All combinations of solutions + */ + first = TRUE; + for (; get_bits(soln_bits, (int)(inv_ptr->count_solns - 2), + (int)(inv_ptr->count_solns - 1)) > 0; soln_bits--) + { +/* + * Loop through all models of of descending size + */ + for (model_size = (int)inv_ptr->phases.size(); model_size >= 0; model_size--) + { + first_of_model_size = TRUE; + quit = TRUE; + while (next_set_phases(inv_ptr, first_of_model_size, model_size) == TRUE) + { + first_of_model_size = FALSE; + current_bits = (soln_bits << inv_ptr->phases.size()) + phase_bits; + + if (subset_bad(current_bits) == TRUE + || subset_minimal(current_bits) == TRUE) + continue; + quit = FALSE; +/* + * Switch for finding minimal models only + */ + if (inv_ptr->minimal == TRUE + && superset_minimal(current_bits) == TRUE) + continue; +/* + * Solve for minimum epsilons, continue if no solution found. + */ + if (solve_with_mask(inv_ptr, current_bits) == ERROR) + { + save_bad(current_bits); + if (first == TRUE) + { + post_mortem(); + quit = TRUE; + break; + } + else + { + continue; + } + } + first = FALSE; +/* + * Model has been found, set bits + */ + good_bits = current_bits; + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == TRUE) + { + good_bits = set_bit(good_bits, (int)i, 0); + } + } + for (size_t i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, TOL) == TRUE) + { + good_bits = set_bit(good_bits, (int)(i + inv_ptr->phases.size()), 0); + } + } +/* + * Determine if model is new + */ + for (j = 0; j < count_good; j++) + { + if (good_bits == good[j]) + break; + } +/* + * Calculate ranges and print model only if NOT looking for minimal models + */ + print = FALSE; + if (j >= count_good && inv_ptr->minimal == FALSE) + { + print = TRUE; + save_good(good_bits); + if (inv_ptr->range == TRUE) + { + range(inv_ptr, good_bits); + } + print_model(inv_ptr); + punch_model(inv_ptr); + dump_netpath_pat(inv_ptr); + } +/* + * If superset of a minimal model continue + */ + minimal_bits = good_bits; + if (superset_minimal(minimal_bits) == TRUE) + { + if (print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "%s\n\n", token)); + } + } + continue; + } +/* + * If not superset of minimal model, find minimal model + */ + minimal_bits = minimal_solve(inv_ptr, minimal_bits); + if (minimal_bits == good_bits && print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "\nModel contains minimum number of phases.\n")); + } + } + if (print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "%s\n\n", token)); + } + } + for (j = 0; j < count_good; j++) + { + if (minimal_bits == good[j]) + break; + } + if (j >= count_good) + { + save_good(minimal_bits); + if (inv_ptr->range == TRUE) + { + range(inv_ptr, minimal_bits); + } + print_model(inv_ptr); + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "\nModel contains minimum number of phases.\n")); + output_msg(sformatf( "%s\n\n", token)); + } + punch_model(inv_ptr); + dump_netpath_pat(inv_ptr); + } + save_minimal(minimal_bits); + } + if (quit == TRUE) + break; + } + } +/* + * Summary print + */ + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "\nSummary of inverse modeling:\n\n")); + output_msg(sformatf( "\tNumber of models found: %d\n", + count_good)); + output_msg(sformatf( "\tNumber of minimal models found: %d\n", + count_minimal)); + output_msg(sformatf( + "\tNumber of infeasible sets of phases saved: %d\n", + count_bad)); + output_msg(sformatf( "\tNumber of calls to cl1: %d\n", + count_calls)); + } + my_array.clear(); + delta.clear(); + array1.clear(); + inv_zero.clear(); + inv_res.clear(); + inv_delta1.clear(); + delta2.clear(); + delta3.clear(); + delta_save.clear(); + inv_cu.clear(); + inv_iu.clear(); + inv_is.clear(); + col_name.clear(); + row_name.clear(); + col_back.clear(); + row_back.clear(); + min_delta.clear(); + max_delta.clear(); + good.clear(); + bad.clear(); + minimal.clear(); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +minimal_solve(class inverse *inv_ptr, unsigned long minimal_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Starting with phases indicated in minimal bits, sequentially + * remove phases to find minimal solution + */ + unsigned long temp_bits_l; + if (debug_inverse == TRUE) + { + output_msg(sformatf( "Beginning minimal solve: \n")); + bit_print(minimal_bits, (int)(inv_ptr->phases.size() + inv_ptr->count_solns)); + } + for (size_t i = 0; i < inv_ptr->phases.size() + inv_ptr->count_solns - 1; i++) + { + if (get_bits(minimal_bits, (int)i, 1) == 0) + continue; + temp_bits_l = 1 << (int)i; /* 0's and one 1 */ + temp_bits_l = ~temp_bits_l; /* 1's and one 0 */ + minimal_bits = minimal_bits & temp_bits_l; + if (debug_inverse == TRUE) + { + output_msg(sformatf( "Solving for minimal\n")); + bit_print(minimal_bits, (int)(inv_ptr->phases.size() + inv_ptr->count_solns)); + } + +/* + * minimal_bits cannot be superset of a minimal model, but + * could be subset of one of the sets of minerals with no feasible solution + * If it is a subset, then replace mineral and go on to next + */ + if (subset_bad(minimal_bits) == TRUE) + { + /* put bit back */ + minimal_bits = minimal_bits | ~temp_bits_l; /* 0's and one 1 */ + continue; + } + if (solve_with_mask(inv_ptr, minimal_bits) == ERROR) + { + save_bad(minimal_bits); + /* put bit back */ + minimal_bits = minimal_bits | ~temp_bits_l; /* 0's and one 1 */ + } + + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\n\nMINIMAL MODEL\n\n")); + bit_print(minimal_bits, (int)(inv_ptr->phases.size() + inv_ptr->count_solns)); + } + + solve_with_mask(inv_ptr, minimal_bits); + unsigned long actual_bits = 0; + for (size_t i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, TOL) == FALSE) + { + actual_bits = set_bit(actual_bits, (int)(i + inv_ptr->phases.size()), 1); + } + } + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == FALSE) + { + actual_bits = set_bit(actual_bits, (int)i, 1); + } + } + if (actual_bits != minimal_bits) + { + warning_msg("Roundoff errors in minimal calculation"); + } + return (actual_bits); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solve_with_mask(class inverse *inv_ptr, unsigned long cur_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Uses cur_bits to zero out columns of the array and then solves. + */ + int i, k, l, m, n; + +/* + * Calculate dimensions + */ + k = (int)row_mb; /* rows in A */ + l = (int)(row_epsilon - row_mb); /* rows in C */ + m = (int)(count_rows - row_epsilon); /* rows in E */ + n = (int)count_unknowns; + + + + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta_save[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + + shrink(inv_ptr, &my_array[0], &array1[0], + &k, &l, &m, &n, cur_bits, &delta2[0], &col_back[0], &row_back[0]); + /* + * Save delta constraints + */ + for (i = 0; i < n; i++) + { + delta_save[col_back[i]] = delta2[i]; + } + + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\nColumns\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "\t%d\t%s\n", i, + col_name[col_back[i]])); + } + + output_msg(sformatf( "\nRows\n")); + for (i = 0; i < k + l + m; i++) + { + output_msg(sformatf( "\t%d\t%s\n", i, + row_name[row_back[i]])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(&array1[0], k + l + m, n + 1, (int)max_column_count); + + output_msg(sformatf( "\nInput delta vector:\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + col_name[col_back[i]], (double) delta2[i])); + output_msg(sformatf( "\n")); + } + + for (i = 0; i < k + l + m; i++) + { + if (inv_res[i] == 0) + continue; + output_msg(sformatf( "\nInput inv_res is non zero:\n")); + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + row_name[row_back[i]], (double) inv_res[i])); + output_msg(sformatf( "\n")); + } + } +/* + * Call CL1 + */ + + if (debug_inverse == TRUE) + { + output_msg(sformatf( + "k, l, m, n, max_col, max_row\t%d\t%d\t%d\t%d\t%d\t%d\n", + k, l, m, n, max_column_count, max_row_count)); + } + + kode = 1; + iter = 100000; + count_calls++; +#ifdef INVERSE_CL1MP + if (inv_ptr->mp == TRUE) + { + cl1mp(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], + &kode, inv_ptr->mp_tolerance, &iter, &delta2[0], &inv_res[0], + &error, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE, inv_ptr->mp_censor); + } + else + { + cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], + &kode, toler, &iter, &delta2[0], &inv_res[0], + &error, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); + } +#else + cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], + &kode, toler, &iter, &delta2[0], &inv_res[0], + &error, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); +#endif + if (kode == 3) + { + error_string = sformatf( + "Exceeded maximum iterations in inverse modeling: %d.\n" + "Recompile program with larger limit.", iter); + error_msg(error_string, STOP); + } + memcpy((void *) &(inv_delta1[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + for (i = 0; i < n; i++) + { + inv_delta1[col_back[i]] = delta2[i]; + } + +/* + * Debug, write results + */ + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", kode, + iter, (double) error)); + output_msg(sformatf( "\nsolution vector:\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + col_name[col_back[i]], (double) delta2[i])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (i = 0; i < (k + l + m); i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + row_name[row_back[i]], (double) inv_res[i])); + } + } + + if (kode != 0) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +get_bits(unsigned long bits, int position, int number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns number of bits from position and below. + * position begins at 0. + */ + return ((bits >> (position + 1 - number)) & ~(~0ul << number)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of minimal models + */ + minimal[count_minimal] = bits; + count_minimal++; + if (count_minimal >= max_minimal) + { + max_minimal *= 2; + minimal.resize(max_minimal); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_good(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of good models, not necessarily minimal + */ + good[count_good] = bits; + count_good++; + if (count_good >= max_good) + { + max_good *= 2; + good.resize(max_good); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_bad(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of sets of phases with no feasible solution + */ + bad[count_bad] = bits; + count_bad++; + if (count_bad >= max_bad) + { + max_bad *= 2; + bad.resize(max_bad); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +superset_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a superset of any of the minimal models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_minimal; i++) + { + temp_bits_l = bits | minimal[i]; + if (temp_bits_l == bits) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +subset_bad(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a superset of any of the bad models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_bad; i++) + { + temp_bits_l = bits | bad[i]; + if (temp_bits_l == bad[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +subset_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a subset of any of the minimal models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_minimal; i++) + { + temp_bits_l = bits | minimal[i]; + if (temp_bits_l == minimal[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +bit_print(unsigned long bits, int l) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints l bits of an unsigned long + */ + int i; + + for (i = l - 1; i >= 0; i--) + { + output_msg(sformatf( "%lu ", get_bits(bits, i, 1))); + } + output_msg(sformatf( "\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_model(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model + */ + int i, j; + size_t column; + int print_msg; + cxxSolution *solution_ptr; + class master *master_ptr; + LDBLE d1, d2, d3, d4; + char token[MAX_LENGTH]; +/* + * Update screen + */ + status(count_good, NULL); +/* + * print solution data, epsilons, and revised data + */ + if (pr.inverse == FALSE || pr.all == FALSE) + return (OK); + max_pct = 0; + scaled_error = 0; + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + xsolution_zero(); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + output_msg(sformatf( "\nSolution %d: %s\n", inv_ptr->solns[i], + solution_ptr->Get_description().c_str())); + output_msg(sformatf( + "\n%15.15s %12.12s %12.12s %12.12s\n", " ", + "Input", "Delta", "Input+Delta")); + master_alk->total = solution_ptr->Get_total_alkalinity(); + if (inv_ptr->carbon == TRUE) + { + d1 = solution_ptr->Get_ph(); + d2 = inv_delta1[col_ph + i] / inv_delta1[i]; + d3 = d1 + d2; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( + "%15.15s %12.3e +%12.3e =%12.3e\n", "pH", + (double) d1, (double) d2, (double) d3)); + if (inv_ptr->ph_uncertainties[i] > 0) + { + scaled_error += fabs(d2) / inv_ptr->ph_uncertainties[i]; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / inv_ptr->ph_uncertainties[i], fabs(d2), inv_ptr->ph_uncertainties[i])); + */ + } + else if (d2 != 0.0) + { + error_msg("Computing delta pH/uncertainty", CONTINUE); + } + } + for (j = 0; j < inv_ptr->elts.size(); j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + d1 = inv_ptr->elts[j].master->total; + d2 = inv_delta1[col_epsilon + j * inv_ptr->count_solns + + i] / inv_delta1[i]; + d3 = d1 + d2; + + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + + output_msg(sformatf( + "%15.15s %12.3e +%12.3e =%12.3e\n", + inv_ptr->elts[j].master->elt->name, (double) d1, + (double) d2, (double) d3)); + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == FALSE) + { + d3 = fabs(d2 / d1); + if (d3 > max_pct) + max_pct = d3; + } + d4 = 0; + if (inv_ptr->elts[j].uncertainties[i] > 0) + { + d4 = fabs(inv_ptr->elts[j].uncertainties[i] * d1); + } + else if (inv_ptr->elts[j].uncertainties[i] < 0) + { + d4 = -inv_ptr->elts[j].uncertainties[i]; + } + if (d4 > 0) + { + scaled_error += fabs(d2) / d4; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / d4, fabs(d2), d4)); + */ + } + else if (d2 != 0.0) + { + error_msg("Computing delta element/uncertainty", CONTINUE); + } + } + if (inv_ptr->isotopes.size() > 0) + { + /* adjustments to solution isotope composition */ + for (j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (inv_ptr->isotope_unknowns[j].elt_name != + string_hsave(kit->second.Get_elt_name().c_str()) || + inv_ptr->isotope_unknowns[j].isotope_number != + kit->second.Get_isotope_number()) + continue; + d1 = kit->second.Get_ratio(); + d2 = inv_delta1[col_isotopes + + i * inv_ptr->isotope_unknowns.size() + + j] / inv_delta1[i]; + d3 = d1 + d2; + + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + sprintf(token, "%d%s", + (int) inv_ptr->isotope_unknowns[j]. + isotope_number, + inv_ptr->isotope_unknowns[j].elt_name); + output_msg(sformatf( + "%15.15s %12g +%12g =%12g\n", token, + (double) d1, (double) d2, (double) d3)); +/* + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == FALSE ) { + d3 = fabs(d2/d1); + if (d3 > max_pct) max_pct = d3; + } + */ + if (kit->second.Get_x_ratio_uncertainty() > 0) + { + scaled_error += + fabs(d2) / + kit->second.Get_x_ratio_uncertainty(); +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / solution_ptr->isotopes[k].x_ratio_uncertainty , fabs(d2), solution_ptr->isotopes[k].x_ratio_uncertainty)); + */ + } + else if (d2 != 0.0) + { + error_msg + ("Computing delta solution isotope/uncertainty", + CONTINUE); + } + } + } + } + } + +/* + * Adjustments to phases + */ + print_msg = FALSE; + if (inv_ptr->isotopes.size() > 0) + { + output_msg(sformatf( "\nIsotopic composition of phases:\n")); + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + if (inv_ptr->phases[i].isotopes.size() == 0) + continue; + size_t j = col_phases + i; + if (equal(inv_delta1[j], 0.0, toler) == TRUE && + equal(min_delta[j], 0.0, toler) == TRUE && + equal(max_delta[j], 0.0, toler) == TRUE) + continue; + std::vector& isotope_ref = inv_ptr->phases[i].isotopes; + for (size_t j = 0; j < inv_ptr->isotopes.size(); j++) + { + for (size_t k = 0; k < inv_ptr->phases[i].isotopes.size(); k++) + { + if (inv_ptr->isotopes[j].elt_name != + isotope_ref[k].elt_name || + inv_ptr->isotopes[j].isotope_number != + isotope_ref[k].isotope_number) + continue; + d1 = isotope_ref[k].ratio; + column = col_phase_isotopes + i * inv_ptr->isotopes.size() + j; + if (inv_delta1[col_phases + i] != 0.0) + { + d2 = inv_delta1[column] / inv_delta1[col_phases + i]; + } + else + { + continue; + } + d3 = d1 + d2; + if (equal(d1, 0.0, 1e-7) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, 1e-7) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, 1e-7) == TRUE) + d3 = 0.0; + sprintf(token, "%d%s %s", + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name, + inv_ptr->phases[i].phase->name); + output_msg(sformatf( + "%15.15s %12g +%12g =%12g", token, + (double) d1, (double) d2, (double) d3)); + if (fabs(d2) > (isotope_ref[k].ratio_uncertainty + toler)) + { + output_msg(sformatf( " **")); + print_msg = TRUE; + } + output_msg(sformatf( "\n")); + if (isotope_ref[k].ratio_uncertainty > 0) + { + scaled_error += + fabs(d2) / isotope_ref[k].ratio_uncertainty; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / isotope_ptr[k].ratio_uncertainty, fabs(d2), isotope_ptr[k].ratio_uncertainty)); + */ + } + else if (d2 != 0.0) + { + error_msg + ("Computing delta phase isotope/uncertainty", + CONTINUE); + } + } + } + } + } + if (print_msg == TRUE) + { + output_msg(sformatf( + "\n**\tWARNING: The adjustment to at least one isotopic" + "\n\tcomposition of a phase exceeded the specified uncertainty" + "\n\tfor the phase. If the phase is not constrained to dissolve" + "\n\tor precipitate, then the isotopic composition of the phase" + "\n\tis also unconstrained.\n")); + } + output_msg(sformatf( "\n%-20.20s %7s %12.12s %12.12s\n", + "Solution fractions:", " ", "Minimum", "Maximum")); + for (i = 0; i < inv_ptr->count_solns; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( "%11s%4d %12.3e %12.3e %12.3e\n", + "Solution", inv_ptr->solns[i], (double) d1, (double) d2, + (double) d3)); + } + + // appt, calculate and print SI's + LDBLE t_i, p_i, iap, lk, t; + const char *name; + class rxn_token *rxn_ptr; + CReaction *reaction_ptr; + + output_msg(sformatf( "\n%-25.25s %2s %12.12s %12.12s %-18.18s (Approximate SI in solution ", + "Phase mole transfers:", " ", "Minimum", "Maximum", "Formula")); + + for (i = 0; i < inv_ptr->count_solns - 1; i++) + output_msg(sformatf("%d, ", inv_ptr->solns[i])); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + t_i = solution_ptr->Get_tc() + 273.15; + p_i = solution_ptr->Get_patm(); + output_msg(sformatf("%d at %3d K, %3d atm)\n", inv_ptr->solns[i], int(t_i), int(floor(p_i + 0.5)))); + p_i *= PASCAL_PER_ATM; + + for (size_t i = col_phases; i < col_redox; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE && + equal(min_delta[i], 0.0, toler) == TRUE && + equal(max_delta[i], 0.0, toler) == TRUE) + continue; + + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( + "%15.15s %12.3e %12.3e %12.3e %-25.25s (", col_name[i], + (double)d1, (double)d2, (double)d3, inv_ptr->phases[i - col_phases].phase->formula)); + + size_t i1 = 0; + for (; i1 < phases.size(); i1++) + { + if (Utilities::strcmp_nocase(phases[i1]->name, col_name[i])) + continue; + reaction_ptr = &phases[i1]->rxn_s; + for (size_t i2 = 0; i2 < inv_ptr->count_solns; i2++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i2]); + + reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) - phases[i1]->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + lk = k_calc(reaction_ptr->logk, t_i, p_i); + + iap = 0.0; + for (rxn_ptr = &reaction_ptr->token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + t = 0; + if (rxn_ptr->s == s_eminus) + t = -solution_ptr->Get_pe(); + else if (!Utilities::strcmp_nocase(rxn_ptr->s->name, "H2O")) + t = log10(solution_ptr->Get_ah2o()); + else if (!Utilities::strcmp_nocase(rxn_ptr->s->name, "H+")) + t = -solution_ptr->Get_ph(); + else + { + if (rxn_ptr->s->secondary) + name = rxn_ptr->s->secondary->elt->name; + else + name = rxn_ptr->s->primary->elt->name; + t = solution_ptr->Get_master_activity()[name]; + } + if (t) + iap += t * rxn_ptr->coef; + else + { + iap = -999; break; + } + } + + if (iap == -999) + output_msg(sformatf(" ")); + else + output_msg(sformatf("%6.2f", iap - lk)); + if (i2 < inv_ptr->count_solns - 1) + output_msg(sformatf(",")); + } + } + output_msg(sformatf(")\n")); + } + output_msg(sformatf( "\n%-25.25s\n", "Redox mole transfers:")); + for (size_t i = col_redox; i < col_epsilon; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + output_msg(sformatf( "%15.15s %12.3e\n", col_name[i], + (double) inv_delta1[i])); + } + + output_msg(sformatf( + "\nSum of residuals (epsilons in documentation): %12.3e\n", + ((double) (error / SCALE_EPSILON)))); + output_msg(sformatf( + "Sum of delta/uncertainty limit: %12.3e\n", + (double) scaled_error)); + output_msg(sformatf( + "Maximum fractional error in element concentration: %12.3e\n", + (double) max_pct)); +/* + * Flush buffer after each model + */ + output_flush(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_model_heading(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + /* + * Prints model headings to selected output file + */ + int i; + char token[MAX_LENGTH]; + + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + // set punch file + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_inverse() || + !current_selected_output->Get_active()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + int l = (!current_selected_output->Get_high_precision()) ? 15 : 20; + inverse_heading_names.clear(); + /* + * Print sum of residuals and maximum fractional error + */ + inverse_heading_names.push_back(sformatf("%*s\t", l, "Sum_resid")); + inverse_heading_names.push_back(sformatf("%*s\t", l, "Sum_Delta/U")); + inverse_heading_names.push_back(sformatf("%*s\t", l, "MaxFracErr")); + + /* + * Print solution numbers + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "Soln_%d", inv_ptr->solns[i]); + std::string tok1(token); + tok1.append("_min"); + std::string tok2(token); + tok2.append("_max"); + + inverse_heading_names.push_back(sformatf("%*s\t", l, token)); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + } + /* + * Print phase names + */ + for (size_t i = col_phases; i < col_redox; i++) + { + + std::string tok1(col_name[i]); + tok1.append("_min"); + std::string tok2(col_name[i]); + tok2.append("_max"); + + inverse_heading_names.push_back(sformatf("%*s\t", l, col_name[i])); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + } + for (size_t j = 0; j < inverse_heading_names.size(); j++) + { + fpunchf_heading(inverse_heading_names[j].c_str()); + //user_punch_headings[j] = string_hsave(heading_names[j].c_str()); + } + fpunchf_heading("\n"); + } + current_selected_output = NULL; + phrq_io->Set_punch_ostream(NULL); + /* + * Flush buffer after each model + */ + punch_flush(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_model(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model to selected output file + */ + int i; + LDBLE d1, d2, d3; + //if (punch.in == FALSE || pr.punch == FALSE || punch.inverse == FALSE) + // return (OK); + + UserPunch temp_user_punch; + current_user_punch = & temp_user_punch; + temp_user_punch.Set_headings(inverse_heading_names); + + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_inverse() || + !current_selected_output->Get_active()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + n_user_punch_index = 0; + /* + * write residual info + */ + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) (error / SCALE_EPSILON)); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) scaled_error); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) max_pct); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) (error / SCALE_EPSILON)); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) scaled_error); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) max_pct); + } + /* + * write solution fractions + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d3); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d3); + } + } + /* + * write phase transfers + */ + for (size_t i = col_phases; i < col_redox; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d3); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d3); + } + } + punch_msg("\n"); + + /* + * Flush buffer after each model + */ + punch_flush(); + } + current_selected_output = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +set_bit(unsigned long bits, int position, int value) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets a single bit + */ + unsigned long temp_bits_l; + + temp_bits_l = 1 << position; + if (value == 0) + { + temp_bits_l = ~temp_bits_l; + temp_bits_l = bits & temp_bits_l; + } + else + { + temp_bits_l = bits | temp_bits_l; + } + return (temp_bits_l); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +next_set_phases(class inverse *inv_ptr, + int first_of_model_size, int model_size) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + unsigned long temp_bits_l; + +/* + * min_ and max_position are arrays, logically with length + * of model_size, that contain minimum and maximum + * phase numbers that can be in that model position. + * + * now contains a list of phase numbers to mark as in for this + * model + */ + +/* + * Initialize for a given model_size + */ + if (first_of_model_size == TRUE) + { + for (i = 0; i < model_size; i++) + { + min_position[i] = i; + now[i] = i; + max_position[i] = (int)inv_ptr->phases.size() - model_size + i; + } + } + else + { +/* + * Determine next combination of phases for fixed model_size + */ + for (i = (model_size - 1); i >= 0; i--) + { + if (now[i] < max_position[i]) + { + now[i]++; + if (i < (model_size - 1)) + { + k = now[i]; + for (j = (i + 1); j < model_size; j++) + { + k++; + now[j] = k; + } + } + break; + } + } + if (i < 0) + return (FALSE); + } +/* + * Set bits which switch in phases + */ + temp_bits_l = 0; + for (j = 0; j < model_size; j++) + { + temp_bits_l += (1 << now[j]); + } + phase_bits = temp_bits_l; + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +range(class inverse *inv_ptr, unsigned long cur_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Takes the model from cur_bits and sequentially determines the + * minimum and maximum values for each solution fraction and + * each phase mass transfer. + */ + int i, j; + int k, l, m, n; + int f; + unsigned long bits; + LDBLE error2; +/* + * Include forced solutions and phases in range calculation + */ + for (size_t i = 0; i < inv_ptr->count_solns + inv_ptr->phases.size(); i++) + { + if (i < inv_ptr->phases.size()) + { + if (inv_ptr->phases[i].force == TRUE) + { + cur_bits = set_bit(cur_bits, (int)i, 1); + } + } + else + { + if (inv_ptr->force_solns[i - inv_ptr->phases.size()] == TRUE) + { + cur_bits = set_bit(cur_bits, (int)i, 1); + } + } + } + + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); +/* + * Switch bits so that phases are high and solutions are low + */ + bits = + get_bits(cur_bits, (int)(inv_ptr->phases.size() + inv_ptr->count_solns) - 1, + (int)inv_ptr->count_solns); + bits += + (get_bits(cur_bits, (int)inv_ptr->phases.size() - 1, (int)inv_ptr->phases.size()) + << (int)inv_ptr->count_solns); +/* + * Do range calculation + */ + for (i = 0; i < inv_ptr->count_solns + inv_ptr->phases.size(); i++) + { + if (inv_ptr->count_solns == i + 1) + { + min_delta[i] = 1.0; + max_delta[i] = 1.0; + continue; + } + if (get_bits(bits, i, 1) == 0) + continue; +/* + * Calculate min and max + */ + for (f = -1; f < 2; f += 2) + { + k = (int)row_mb; /* rows in A */ + l = (int)(row_epsilon - row_mb); /* rows in C */ + m = (int)(count_rows - row_epsilon); /* rows in E */ + n = (int)count_unknowns; /* number of variables */ +/* + * Copy equations + */ + memcpy((void *) &(array1[0]), (void *) &(my_array[0]), + (size_t) max_column_count * max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta3[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta_save[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + +/* + * Change optimization + */ + for (j = 0; j < k; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } + array1[i] = 1.0; + if (f < 1) + { + array1[n] = -fabs(inv_ptr->range_max); + } + else + { + array1[n] = fabs(inv_ptr->range_max); + } + shrink(inv_ptr, &array1[0], &array1[0], + &k, &l, &m, &n, cur_bits, &delta2[0], &col_back[0], &row_back[0]); + /* + * Save delta constraints + */ + for (j = 0; j < n; j++) + { + delta_save[col_back[j]] = delta2[j]; + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\nInput delta:\n\n")); + for (j = 0; j < n; j++) + { + output_msg(sformatf( "\t%d %s\t%g\n", j, + col_name[col_back[j]], (double) delta2[j])); + } + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(&array1[0], k + l + m, n + 1, (int)max_column_count); + } + kode = 1; + iter = 200; + count_calls++; +#ifdef INVERSE_CL1MP + if (inv_ptr->mp == TRUE) + { + cl1mp(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], + &kode, inv_ptr->mp_tolerance, &iter, &delta2[0], &inv_res[0], + &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE, inv_ptr->mp_censor); + } + else + { + cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], + &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); + } +#else + cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], + &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); +#endif + if (kode != 0) + { + output_msg(sformatf( + "Error in subroutine range. Kode = %d\n", kode)); + } + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", + kode, iter, (double) error2)); + output_msg(sformatf( "k, l, m, n: %d\t%d\t%d\t%d\n", k, + l, m, n)); + output_msg(sformatf( "\nsolution vector %s\n", + col_name[i])); + for (j = 0; j < n; j++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, + col_name[col_back[j]], (double) delta2[j])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (j = 0; j < (k + l + m); j++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", j, + row_name[row_back[j]], (double) inv_res[j])); + } + } + for (j = 0; j < n; j++) + { + if (col_back[j] == i) + break; + } + if (f < 0) + { + min_delta[i] = delta2[j]; + } + else + { + max_delta[i] = delta2[j]; + } + for (j = 0; j < n; j++) + { + delta3[col_back[j]] = delta2[j]; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +shrink(class inverse *inv_ptr, LDBLE * array_in, LDBLE * array_out, + int *k, int *l, int *m, int *n, + unsigned long cur_bits, + LDBLE * delta_l, int *col_back_l, int *row_back_l) +/* ---------------------------------------------------------------------- */ +{ +/* + * Shrink eliminates any rows that are all zeros and any columns + * that are not in cur_bits, result is put in array_out + * + * k, l, m, n return the new sizes of the array. + * delta is remapped to retain any non-negativity constraints + * Col_back maps columns that remain back to original columns + * Row_back maps rows that remain back to original rows + */ + int i, j, row; + int k1, l1, m1; + size_t cur_col, column; + int nonzero; +/* + * Copy array_in to array_out + */ + if (array_in != array_out) + { + for (i = 0; i < (*k + *l + *m); i++) + { + memcpy(&(array_out[i * max_column_count]), + &(array_in[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + } + } +/* + * Determine columns to eliminate + */ + for (i = 0; i < (*n + 1); i++) + col_back_l[i] = i; + +/* + * Drop phases not in model + */ + for (i = 0; i < inv_ptr->phases.size(); i++) + { + if (get_bits(cur_bits, i, 1) == 0) + { + col_back_l[col_phases + i] = -1; + /* drop isotopes */ + if (inv_ptr->isotopes.size() > 0) + { + for (j = 0; j < inv_ptr->isotopes.size(); j++) + { + column = + col_phase_isotopes + i * inv_ptr->isotopes.size() + j; + col_back_l[column] = -1; + } + } + } + } +/* + * Drop solutions not in model + */ + for (i = 0; i < (inv_ptr->count_solns - 1); i++) + { + if (get_bits(cur_bits, (int)inv_ptr->phases.size() + i, 1) == 0) + { + col_back_l[i] = -1; + /* drop all epsilons for the solution */ + for (j = 0; j < inv_ptr->elts.size(); j++) + { + column = col_epsilon + j * inv_ptr->count_solns + i; + col_back_l[column] = -1; + } + /* drop pH for the solution */ + if (inv_ptr->carbon == TRUE) + { + column = col_ph + i; + col_back_l[column] = -1; + } + /* drop isotopes */ + if (inv_ptr->isotopes.size() > 0) + { + for (size_t j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + column = col_isotopes + + i * inv_ptr->isotope_unknowns.size() + j; + col_back_l[column] = -1; + } + } + } + } + +/* + * Drop epsilons not used + */ + for (i = (int)col_epsilon; i < *n; i++) + { + if (col_back_l[i] < 0) + continue; + for (j = 0; j < (*k + *l + *m); j++) + { + if (array_out[(size_t)j * max_column_count + (size_t)i] != 0) + break; + } + if (j == (*k + *l + *m)) + { + col_back_l[i] = -1; + } + } +/* + * rewrite array_out + */ + cur_col = 0; + for (i = 0; i < (*n + 1); i++) + { + if (col_back_l[i] < 0) + continue; + if (cur_col == col_back_l[i]) + { + cur_col++; + continue; + } + for (j = 0; j < (*k + *l + *m); j++) + { + array_out[j * max_column_count + cur_col] = + array_out[j * max_column_count + i]; + } + col_back_l[cur_col] = col_back_l[i]; + delta_l[cur_col] = delta_l[i]; + cur_col++; + } + *n = (int)cur_col - 1; +/* + * Eliminate unnecessary optimization eqns + */ + row = 0; + k1 = 0; + for (i = 0; i < *k; i++) + { + if (memcmp(&(array_out[i * max_column_count]), &(inv_zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) + { + continue; + } +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + + + memcpy(&(array_out[row * max_column_count]), + &(array_out[(size_t)i * max_column_count]), + ((size_t)*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + k1++; + } + +/* + * Eliminate unnecessary equality eqns + */ + l1 = 0; + for (i = *k; i < (*k + *l); i++) + { + nonzero = FALSE; + for (j = 0; j < *n; j++) + { + if (equal(array_out[i * max_column_count + j], 0.0, toler) == + FALSE) + { + nonzero = TRUE; + break; + } + } + if (nonzero == FALSE) + continue; +/* + if (memcmp(&(array_out[i * max_column_count]), &(zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) { + continue; + } + */ +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + memcpy(&(array_out[row * max_column_count]), + &(array_out[(size_t)i * max_column_count]), + ((size_t)*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + l1++; + } +/* + * Eliminate unnecessary inequality eqns + */ + m1 = 0; + for (i = (*k + *l); i < (*k + *l + *m); i++) + { + nonzero = FALSE; + for (j = 0; j < *n; j++) + { + if (equal(array_out[i * max_column_count + j], 0.0, toler) == + FALSE) + { + nonzero = TRUE; + break; + } + } + if (nonzero == FALSE) + continue; +/* + if (memcmp(&(array_out[i * max_column_count]), &(zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) { + continue; + } + */ +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + memcpy(&(array_out[(size_t)row * max_column_count]), + &(array_out[(size_t)i * max_column_count]), + ((size_t)*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + m1++; + } + + *k = k1; + *l = l1; + *m = m1; +/* + * Scale all inequality rows + */ + + for (i = *k + *l; i < *k + *l + *m; i++) + { + for (j = 0; j < *n + 1; j++) + { + array_out[i * max_column_count + j] *= SCALE_ALL; + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_solns(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check_solns checks that each solution can be charge balanced within + * the given constraints. If not, it is an error and the program will + * terminate. + */ + int i; + size_t j; + int k, l, m, n; + int return_value; + unsigned long bits; + LDBLE error2; + + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + +/* + * Switch bits so that phases are high and solutions are low + */ + return_value = OK; + for (i = 0; i < inv_ptr->count_solns; i++) + { + bits = 0; + bits += 1 << (inv_ptr->phases.size() + i); +/* + * Check for feasibility of charge balance with given uncertainties + */ + k = (int)row_mb; /* rows in A */ + l = (int)(row_epsilon - row_mb); /* rows in C */ + m = (int)(count_rows - row_epsilon); /* rows in E */ + n = (int)count_unknowns; /* number of variables */ +/* debug + output_msg(sformatf( "\nColumns\n")); + for (j = 0; j < n; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, col_name[j])); + } + + output_msg(sformatf( "\nRows\n")); + for (j = 0; j < k + l + m; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, row_name[j])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array, k + l + m, + n + 1, max_column_count); + */ +/* + * Copy equations + */ + memcpy((void *) &(array1[0]), (void *) &(my_array[0]), + (size_t) max_column_count * max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + +/* + * Keep optimization + */ +/* + * Zero out mass balance rows and fraction rows + */ + for (j = row_mb; j < row_charge; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + max_column_count * sizeof(LDBLE)); + } +/* + * Set fraction of solution to 1.0 + */ + array1[(row_charge - 1) * max_column_count + i] = 1.0; + array1[(row_charge - 1) * max_column_count + n] = 1.0; + +/* + * Zero out charge balance rows for other solutions + */ + for (j = 0; j < inv_ptr->count_solns; j++) + { + if (j == i) + continue; + memcpy((void *) &(array1[(row_charge + j) * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } + +/* + * Zero out isotope mole balance + */ + for (size_t j = row_isotopes; j < row_epsilon; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + max_column_count * sizeof(LDBLE)); + } + +/* + * Zero out isotope uncertainties + */ + for (size_t j = row_isotope_epsilon; j < count_rows; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + max_column_count * sizeof(LDBLE)); + } +/* + * Can`t Zero out epsilon constraint rows for other solutions because not sure which + * are which + */ + + shrink(inv_ptr, &array1[0], &array1[0], + &k, &l, &m, &n, bits, &delta2[0], &col_back[0], &row_back[0]); +/* Debug + + output_msg(sformatf( "\nColumns\n")); + for (j = 0; j < n; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, col_name[col_back[j]])); + } + + output_msg(sformatf( "\nRows\n")); + for (j = 0; j < k + l + m; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, row_name[row_back[j]])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array1, k + l + m, + n + 1, max_column_count); + + output_msg(sformatf( "\nInput delta vector:\n")); + for (j=0; j < n; j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, col_name[col_back[j]], delta2[j])); + output_msg(sformatf( "\n")); + } + */ + + kode = 1; + iter = 200; + count_calls++; + cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, + &delta2[0], &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); + + if (kode != 0) + { + error_string = sformatf( + "Not possible to balance solution %d with input uncertainties.", + inv_ptr->solns[i]); + error_msg(error_string, CONTINUE); + return_value = ERROR; + } + +/* Debug + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", kode, iter, error)); + output_msg(sformatf( "k, l, m, n: %d\t%d\t%d\t%d\n", k, l, m, n)); + + output_msg(sformatf( "\nsolution vector %s\n", col_name[i])); + for (j = 0; j < n; j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, col_name[col_back[j]], delta2[j])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (j = 0; j < (k + l + m); j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", j, row_name[row_back[j]], inv_res[j])); + } + */ + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +post_mortem(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Post_mortem simply identifies which equality and inequality of the + * array have not been satisfied. + * + */ + LDBLE sum; +/* + * Check equalities + */ + output_msg(sformatf( + "\nPost_mortem examination of inverse modeling:\n\n")); + for (size_t i = row_mb; i < row_epsilon; i++) + { + sum = 0; + for (size_t j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * my_array[i * max_column_count + j]; + } + + if (equal(sum, my_array[(i * max_column_count) + count_unknowns], toler) + == FALSE) + { + output_msg(sformatf( + "\tERROR: equality not satisfied for %s, %e.\n", + row_name[i], + (double) (sum - my_array[(i * max_column_count) + count_unknowns]))); + } + } +/* + * Check inequalities + */ + for (size_t i = row_epsilon; i < count_rows; i++) + { + sum = 0; + for (size_t j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * my_array[i * max_column_count + j]; + } + + if (sum > my_array[(i * max_column_count) + count_unknowns] + toler) + { + output_msg(sformatf( + "\tERROR: inequality not satisfied for %s, %e\n", + row_name[i], + (double) (sum - my_array[(i * max_column_count) + count_unknowns]))); + } + } +/* + * Check dissolution/precipitation constraints + */ + for (size_t i = 0; i < count_unknowns; i++) + { + if (delta_save[i] > 0.5 && inv_delta1[i] < -toler) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + else if (delta_save[i] < -0.5 && inv_delta1[i] > toler) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +test_cl1_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * checks that equality and inequalities are satisfied + * + */ + int i; + LDBLE sum; +/* + * Check equalities + */ + bool rv = true; + if (debug_inverse) + { + output_msg(sformatf( + "\nTesting cl1 inverse modeling:\n\n")); + } + for (size_t i = row_mb; i < row_epsilon; i++) + { + sum = 0; + for (size_t j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * my_array[i * max_column_count + j]; + } + + if (equal(sum, my_array[(i * max_column_count) + count_unknowns], toler) == FALSE) + { + if (debug_inverse) + { + output_msg(sformatf("\tERROR: equality not satisfied for %s, %e.\n", row_name[i], + (double) (sum - my_array[(i * max_column_count) + count_unknowns]))); + } + rv = false; + } + } +/* + * Check inequalities + */ + for (size_t i = row_epsilon; i < count_rows; i++) + { + sum = 0; + for (size_t j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * my_array[i * max_column_count + j]; + } + + if (sum > my_array[(i * max_column_count) + count_unknowns] + toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: inequality not satisfied for %s, %e\n", + row_name[i], + (double) (sum - my_array[(i * max_column_count) + count_unknowns]))); + } + rv = false; + } + } +/* + * Check dissolution/precipitation constraints + */ + for (i = 0; i < count_unknowns; i++) + { + if (delta_save[i] > 0.5 && inv_delta1[i] < -toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + rv = false; + + } + else if (delta_save[i] < -0.5 && inv_delta1[i] > toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + rv = false; + } + } + + return (rv); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +carbon_derivs(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, temp; + LDBLE c_uncertainty, d_carbon, alk_plus, alk_minus; + cxxSolution *solution_ptr_orig, *solution_ptr; + + inv_ptr->dalk_dph.resize(inv_ptr->count_solns); + inv_ptr->dalk_dc.resize(inv_ptr->count_solns); + + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr_orig = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (solution_ptr_orig == NULL) + { + error_string = sformatf( "Solution %d for inverse " + "modeling not found.", inv_ptr->solns[i]); + error_msg(error_string, STOP); + } +/* + * Find carbon uncertainty + */ + c_uncertainty = 0; + d_carbon = 0; + for (j = 0; j < inv_ptr->elts.size(); j++) + { + if (inv_ptr->elts[j].master == s_co3->secondary) + { + c_uncertainty = inv_ptr->elts[j].uncertainties[i]; + break; + } + } + if (c_uncertainty < 0.0) + { + d_carbon = -c_uncertainty; + } + else if (c_uncertainty > 0.0) + { + cxxNameDouble::iterator kit = solution_ptr_orig->Get_totals().begin(); + for ( ; kit != solution_ptr_orig->Get_totals().end(); kit++) + { + if (strcmp(kit->first.c_str(), "C(4)") == 0) + { + d_carbon = kit->second / + solution_ptr_orig->Get_mass_water() * c_uncertainty; + break; + } + + } + } + +/* + * Make four copies of solution + * Modify ph and carbon in solutions + */ + set_ph_c(inv_ptr, i, solution_ptr_orig, -5, 0.0, 1.0, 0.0); + set_ph_c(inv_ptr, i, solution_ptr_orig, -4, 0.0, -1.0, 0.0); + if (c_uncertainty != 0) + { + set_ph_c(inv_ptr, i, solution_ptr_orig, -3, d_carbon, 0.0, 1.0); + set_ph_c(inv_ptr, i, solution_ptr_orig, -2, d_carbon, 0.0, -1.0); + } +/* */ + temp = pr.all; + pr.all = FALSE; + initial_solutions(FALSE); + pr.all = temp; +/* + * dAlk/dpH + */ + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -5); + alk_plus = solution_ptr->Get_total_alkalinity(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -4); + alk_minus = solution_ptr->Get_total_alkalinity(); + inv_ptr->dalk_dph[i] = (alk_plus - alk_minus) / + (2.0 * inv_ptr->ph_uncertainties[i]); +/* + * dAlk/dC + */ + if (d_carbon != 0) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -3); + alk_plus = solution_ptr->Get_total_alkalinity(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -2); + alk_minus = solution_ptr->Get_total_alkalinity(); + inv_ptr->dalk_dc[i] = (alk_plus - alk_minus) / (2.0 * d_carbon); + } + else + { + inv_ptr->dalk_dc[i] = 0.0; + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "dAlk/dph = %e\tdAlk/dC = %e\n", + (double) inv_ptr->dalk_dph[i], + (double) inv_ptr->dalk_dc[i])); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_ph_c(class inverse *inv_ptr, + int i, + cxxSolution *solution_ptr_orig, + int n_user_new, LDBLE d_carbon, LDBLE ph_factor, LDBLE c_factor) +/* ---------------------------------------------------------------------- */ +{ + int n_user_orig; + cxxSolution *solution_ptr; + + n_user_orig = inv_ptr->solns[i]; + Utilities::Rxn_copy(Rxn_solution_map, n_user_orig, n_user_new); + + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user_new); + solution_ptr->Set_new_def(true); + solution_ptr->Create_initial_data(); + solution_ptr->Set_n_user_end(n_user_new); + LDBLE ph = solution_ptr->Get_ph(); + ph += inv_ptr->ph_uncertainties[i] * ph_factor; + solution_ptr->Set_ph(ph); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + cxxISolutionComp temp_comp; + temp_comp.Set_description(jit->first.c_str()); + temp_comp.Set_input_conc(jit->second / solution_ptr_orig->Get_mass_water()); + temp_comp.Set_units("Mol/kgw"); + if (strcmp(jit->first.c_str(), "C(4)") == 0) + { + LDBLE c = temp_comp.Get_input_conc(); + c += d_carbon * c_factor; + temp_comp.Set_input_conc(c); + } + solution_ptr->Get_initial_data()->Get_comps()[jit->first] = temp_comp; + } + solution_ptr->Get_totals().clear(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isotope_balance_equation(class inverse *inv_ptr, int row, int n) +/* ---------------------------------------------------------------------- */ +/* + * routine fills in an isotope balance equation + * + * row is the row in array that needs to be filled + * n is the isotope number in inv_ptr + */ +{ + int i, j, k; + LDBLE isotope_number; + size_t column; + LDBLE f; + class master *primary_ptr; + cxxSolution *solution_ptr; +/* + * Determine primary master species and isotope number for + * isotope mass-balance equation + */ + column = 0; + primary_ptr = master_bsearch_primary(inv_ptr->isotopes[n].elt_name); + isotope_number = inv_ptr->isotopes[n].isotope_number; + /* isotope element must be defined */ + if (primary_ptr == NULL) + { + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + inv_ptr->isotopes[n].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + } + + /* isotope element must be primary */ + if (primary_ptr->primary != TRUE) + { + error_string = sformatf( "Isotope mass-balance may only be used" + " for total element concentrations.\n" + "Secondary species not allowed: %s.", + inv_ptr->isotopes[n].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + } + +/* + * Fill in terms for each solution + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (i == (inv_ptr->count_solns - 1)) + { + f = -1.0; + } + else + { + f = 1.0; + } + + /* mixing fraction term */ + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + std::map < std::string, cxxSolutionIsotope >::iterator jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + class master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + my_array[(size_t)row * max_column_count + (size_t)i] += + f * jit->second.Get_total() * jit->second.Get_ratio(); + } + } + + /* epsilon of total moles of element valence * ratio */ + jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + /* What to do with H and O, skip for now ??? */ + if (primary_ptr == s_hplus->primary + || primary_ptr == s_h2o->primary) + continue; + class master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); + class master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + /* find column of master for solution i */ + for (k = 0; k < inv_ptr->elts.size(); k++) + { + if (master_jit == inv_ptr->elts[k].master) + break; + } + column = col_epsilon + (k * inv_ptr->count_solns) + i; + my_array[(size_t)row * max_column_count + (size_t)column] += + f * jit->second.Get_ratio(); + } + } + + /* epsilon of ratio * total of element valence */ + jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + class master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); + class master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + + /* find column of epsilon for ratio of valence */ + for (k = 0; k < inv_ptr->isotope_unknowns.size(); k++) + { + if (master_jit == + inv_ptr->isotope_unknowns[k].master + && jit->second.Get_isotope_number() == + inv_ptr->isotope_unknowns[k].isotope_number) + { + column = + col_isotopes + + (i * inv_ptr->isotope_unknowns.size()) + k; + } + } + my_array[(size_t)row * max_column_count + (size_t)column] += + f * jit->second.Get_total(); + } + } + } +/* + * Fill in terms for each phase + */ + for (i = 0; i < inv_ptr->phases.size(); i++) + { + if (inv_ptr->phases[i].isotopes.size() == 0) + continue; + std::vector& isotope_ref = inv_ptr->phases[i].isotopes; + for (j = 0; j < inv_ptr->phases[i].isotopes.size(); j++) + { + if (isotope_ref[j].primary == primary_ptr && + isotope_ref[j].isotope_number == isotope_number) + { + /* term for alpha phase unknowns */ + column = col_phases + i; + my_array[(size_t)row * max_column_count + (size_t)column] = + isotope_ref[j].ratio * isotope_ref[j].coef; + /* term for phase isotope uncertainty unknown */ + column = col_phase_isotopes + i * inv_ptr->isotopes.size() + (size_t)n; + my_array[(size_t)row * max_column_count + column] = isotope_ref[j].coef; + break; + } + } + + } + return OK; +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +set_isotope_unknowns(class inverse* inv_ptr) + /* ---------------------------------------------------------------------- */ +{ + /* + * Go through elements for which isotope balances are requested + * and make a array of isotope structures + * return total number of isotope unknowns and structure array + */ + int i, k; + LDBLE isotope_number; + class master* primary_ptr; + size_t count_isotopes; + std::vector& isotopes = inv_ptr->isotope_unknowns; + + if (inv_ptr->isotopes.size() == 0) + { + isotopes.clear(); + return true; + } + count_isotopes = 0; + + for (i = 0; i < inv_ptr->isotopes.size(); i++) + { + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + if (primary_ptr == NULL) + { + error_string = sformatf( + "Element not found for isotope calculation: %s.", + inv_ptr->isotopes[i].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (primary_ptr->primary != TRUE) + { + error_string = sformatf("Isotope mass-balance may only be used" + " for total element concentrations.\n" + "Secondary species not allowed: %s.", + inv_ptr->isotopes[i].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + + /* nonredox element */ + if (primary_ptr->s->secondary == NULL) + { + isotopes.resize(count_isotopes + 1); + isotopes[count_isotopes].primary = primary_ptr; + isotopes[count_isotopes].master = primary_ptr; + isotopes[count_isotopes].isotope_number = isotope_number; + isotopes[count_isotopes].elt_name = primary_ptr->elt->name; + count_isotopes++; + + /* redox element */ + } + else + { + + /* find master */ + for (k = 0; k < (int)master.size(); k++) + { + if (master[k] == primary_ptr) + break; + } + + /* sum all secondary for master */ + k++; + for (; k < (int)master.size(); k++) + { + isotopes.resize(count_isotopes + 1); + isotopes[count_isotopes].primary = primary_ptr; + isotopes[count_isotopes].master = master[k]; + isotopes[count_isotopes].isotope_number = isotope_number; + isotopes[count_isotopes].elt_name = master[k]->elt->name; + count_isotopes++; + } + } + } + return true; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_isotopes(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through elements for which isotope balances are requested + * and make sure each solution has isotope ratios defined + */ + int i, ii, j, k, l; + int err, found_isotope; + LDBLE isotope_number; + class master *master_ptr, *primary_ptr; + cxxSolution *solution_ptr; + class phase *phase_ptr; + char token[MAX_LENGTH]; + +/* + * Check solutions for necessary isotope data + */ + for (j = 0; j < inv_ptr->count_solns; j++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[j]); + xsolution_zero(); + add_solution(solution_ptr, 1.0, 1.0); +/* + * Go through inverse isotopes and make sure isotope data for each solution + * inv_ptr->isotopes has elements; inv_ptr->i_u has redox states and uncertainties + */ + for (i = 0; i < inv_ptr->isotopes.size(); i++) + { + err = FALSE; + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + found_isotope = FALSE; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + class master *primary_kit = master_bsearch_primary(kit->second.Get_elt_name().c_str()); + if (primary_kit == primary_ptr && + kit->second.Get_isotope_number() == + isotope_number) + { + found_isotope = TRUE; + break; + } + } + if (found_isotope == TRUE) + continue; + + /* did not find isotope, which is ok if element not in solution */ + if (primary_ptr == s_h2o->primary + || primary_ptr == s_hplus->primary) + { + err = TRUE; + } + else if (primary_ptr->total > 0) + { + err = TRUE; + } + if (err == TRUE) + { + error_string = sformatf( + "In solution %d, isotope ratio(s) are needed for element: %g%s.", + solution_ptr->Get_n_user(), (double) isotope_number, + primary_ptr->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } +/* + * Go through solution isotopes and set uncertainties + */ + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + class master *master_kit = master_bsearch(kit->second.Get_elt_name().c_str()); + class master *primary_kit = master_bsearch_primary(kit->second.Get_elt_name().c_str()); + kit->second.Set_x_ratio_uncertainty(NAN); +/* + * Search for secondary or primary master in inverse uncertainties + */ + ii = -1; + for (i = 0; i < inv_ptr->i_u.size(); i++) + { + master_ptr = master_bsearch(inv_ptr->i_u[i].elt_name); + if (master_ptr == master_kit) + { + ii = i; + break; + } + if (master_ptr == primary_kit) + { + ii = i; + } + } + /* solution isotope data not being used in inverse */ + if (ii == -1) + continue; + + i = ii; + /* use inverse-defined uncertainties first */ +#ifdef NPP + if (j < inv_ptr->i_u[i].uncertainties.size() + && !isnan(inv_ptr->i_u[i].uncertainties[j])) +#else + if (j < inv_ptr->i_u[i].uncertainties.size() + && inv_ptr->i_u[i].uncertainties[j] != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[j]); + + /* use solution-defined uncertainties second */ + } +#ifdef NPP + else if (inv_ptr->i_u[i].uncertainties.size() > 0 + && !isnan(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].uncertainties.size() - 1])) +#else + else if (inv_ptr->i_u[i].uncertainties.size() > 0 + && inv_ptr->i_u[i].uncertainties[(size_t)inv_ptr->i_u[i].uncertainties.size() - 1] != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].uncertainties.size() - 1]); + + /* use solution-defined uncertainties second */ + } +#ifdef NPP + else if (!isnan(kit->second.Get_ratio_uncertainty())) +#else + else if (kit->second.Get_ratio_uncertainty() != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty( + kit->second.Get_ratio_uncertainty()); + /* use isotope defaults third */ + } + else + { + sprintf(token, "%g%s", + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str()); + for (l = 0; l < count_iso_defaults; l++) + { + if (strcmp(token, iso_defaults[l].name) == 0) + { + kit->second.Set_x_ratio_uncertainty( + iso_defaults[l].uncertainty); + error_string = sformatf( + "Solution %d, element %g%s: default isotope ratio uncertainty is used, %g.", + solution_ptr->Get_n_user(), + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), + kit->second.Get_x_ratio_uncertainty()); + warning_msg(error_string); + break; + } + } + } +#ifdef NPP + if (isnan(kit->second.Get_x_ratio_uncertainty())) +#else + if (kit->second.Get_x_ratio_uncertainty() == NAN) +#endif + { + error_string = sformatf( + "In solution %d, isotope ratio uncertainty is needed for element: %g%s.", + solution_ptr->Get_n_user(), + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } +/* + * Check phases for necessary isotope data + */ + for (j = 0; j < inv_ptr->phases.size(); j++) + { + for (i = 0; i < inv_ptr->isotopes.size(); i++) + { + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + found_isotope = FALSE; + for (k = 0; k < inv_ptr->phases[j].isotopes.size(); k++) + { + if (inv_ptr->phases[j].isotopes[k].primary == primary_ptr && + inv_ptr->phases[j].isotopes[k].isotope_number == + isotope_number) + { + found_isotope = TRUE; + break; + } + } + if (found_isotope == TRUE) + continue; + + /* did not find isotope, which is ok if element not in solution */ + phase_ptr = inv_ptr->phases[j].phase; + k = 0; + while (phase_ptr->next_elt[k].elt != NULL) + { + if (phase_ptr->next_elt[k].elt->primary == primary_ptr) + { + if (s_hplus->primary == primary_ptr || + s_h2o->primary == primary_ptr) + { + k++; + continue; + } + else + { + error_string = sformatf( + "In phase %s, isotope ratio(s) are needed for element: %g%s.", + phase_ptr->name, (double) isotope_number, + primary_ptr->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + } + k++; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_isotope_inequalities(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + size_t column; + char token[MAX_LENGTH]; + if (inv_ptr->isotopes.size() <= 0) + return OK; + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + if (inv_ptr->phases[i].isotopes.size() == 0) + continue; + + for (size_t j = 0; j < inv_ptr->phases[i].isotopes.size(); j++) + { + /* find index number */ + size_t k = 0; + for (k = 0; k < inv_ptr->isotopes.size(); k++) + { + if (inv_ptr->phases[i].isotopes[j].elt_name == + inv_ptr->isotopes[k].elt_name + && inv_ptr->phases[i].isotopes[j].isotope_number == + inv_ptr->isotopes[k].isotope_number) + { + break; + } + } + if (k >= inv_ptr->isotopes.size()) + break; + column = col_phase_isotopes + i * inv_ptr->isotopes.size() + k; +/* + * zero column if uncertainty is zero + */ + if (inv_ptr->phases[i].isotopes[j].ratio_uncertainty == 0) + { + for (k = 0; k < count_rows; k++) + { + my_array[(size_t)k * max_column_count + (size_t)column] = 0.0; + } + continue; + } + +/* + * optimization + */ + my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = + SCALE_EPSILON / inv_ptr->phases[i].isotopes[j].ratio_uncertainty; +/* + * two inequalities to account for absolute value + */ + /* for phases constrained to precipitate */ + if (inv_ptr->phases[i].constraint == PRECIPITATE) + { + my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = + inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + my_array[count_rows * max_column_count + (size_t)column] = 1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso pos"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = + inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso neg"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + /* for phases constrained to dissolve */ + } + else if (inv_ptr->phases[i].constraint == DISSOLVE) + { + my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = + -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + my_array[count_rows * max_column_count + (size_t)column] = -1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso pos"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = + -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + my_array[count_rows * max_column_count + (size_t)column] = 1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso neg"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + /* Error if phase is not constrained */ + } + else + { + error_string = sformatf( + "In isotope calculations, all phases containing isotopes must be" + " constrained.\nPhase %s is not constrained.\n", + inv_ptr->phases[i].phase->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_optimize_names(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, row; + char token[MAX_LENGTH]; + row = 0; +/* + * epsilons for analytical data + */ + for (j = 0; j < inv_ptr->elts.size(); j++) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "%s %s %d", "optimize", + inv_ptr->elts[j].master->elt->name, inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * pH + */ + if (carbon > 0) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "%s %s %d", "optimize", "pH", inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * water + */ + sprintf(token, "%s %s", "optimize", "water"); + row_name[row] = string_hsave(token); + row++; +/* + * solution isotopes + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + sprintf(token, "%s %d%s %d", "optimize", + (int) inv_ptr->isotope_unknowns[j].isotope_number, + inv_ptr->isotope_unknowns[j].elt_name, inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * phase isotopes + */ + + for (i = 0; i < inv_ptr->phases.size(); i++) + { + for (j = 0; j < inv_ptr->isotopes.size(); j++) + { + sprintf(token, "%s %s %d%s", "optimize", + inv_ptr->phases[i].phase->name, + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + row_name[row] = string_hsave(token); + row++; + } + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_netpath(class inverse *inverse_ptr) +/* ---------------------------------------------------------------------- */ +{ + std::string string; + //const char* cptr; + + if (inverse_ptr->netpath == NULL) + return; + + /* open file */ + string = inverse_ptr->netpath; + if (replace(".lon", ".lon", string) != true) + { + string.append(".lon"); + } + netpath_file = fopen(string.c_str(), "w"); + if (netpath_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", inverse_ptr->netpath); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + add_to_file("netpath.fil", inverse_ptr->netpath); + + /* Header */ + fprintf(netpath_file, + "2.14 # File format\n"); + + /* write out each solution */ + std::map::iterator it = Rxn_solution_map.begin(); + for ( ; it != Rxn_solution_map.end(); it++) + { + if (it->second.Get_n_user() < 0) + continue; + if (it->second.Get_description().size() > 0) + { + string = it->second.Get_description(); + } + else + { + string = sformatf("Solution %d", it->second.Get_n_user()); + } + fprintf(netpath_file, "4020%s\n", string.c_str()); + //description = (char *) free_check_null(description); + /* lat/lon */ + fprintf(netpath_file, + " # Lat/lon\n"); + + /* well number */ + fprintf(netpath_file, + "%15d # Well number\n", + it->second.Get_n_user()); + + /* total number of wells */ + fprintf(netpath_file, + "%15d # Total wells\n", + (int) Rxn_solution_map.size()); + + /* address */ + fprintf(netpath_file, + " # Address1\n"); + fprintf(netpath_file, + " # Address2\n"); + fprintf(netpath_file, + " # Address3\n"); + fprintf(netpath_file, + " # Address4\n"); + fprintf(netpath_file, + " # Address5\n"); + + /* temperature */ + fprintf(netpath_file, + "%15g # Temperature\n", + (double) it->second.Get_tc()); + + /* pH */ + fprintf(netpath_file, + "%15g # pH\n", + (double) it->second.Get_ph()); + + /* DO */ + print_total(netpath_file, &(it->second), "O(0)", "Dissolved Oxygen"); + + /* TDIC */ + print_total(netpath_file, &(it->second), "C(4)", "TDIC"); + + /* Tritium */ + print_isotope(netpath_file, &(it->second), "3H(1)", "Tritium"); + + /* H2S */ + print_total(netpath_file, &(it->second), "S(-2)", "H2S"); + + /* Calcium */ + print_total(netpath_file, &(it->second), "Ca", "Calcium"); + + /* Eh */ + fprintf(netpath_file, + "%15g # Eh\n", + (double) (0.059 * it->second.Get_pe())); + + /* Magnesium */ + print_total(netpath_file, &(it->second), "Mg", "Magnesium"); + + /* Sodium */ + print_total(netpath_file, &(it->second), "Na", "Sodium"); + + /* Potassium */ + print_total(netpath_file, &(it->second), "K", "Potassium"); + + /* Chloride */ + print_total(netpath_file, &(it->second), "Cl", "Chloride"); + + /* Sulfate */ + print_total(netpath_file, &(it->second), "S(6)", "Sulfate"); + + /* Fluoride */ + print_total(netpath_file, &(it->second), "F", "Fluoride"); + + /* Silica */ + print_total(netpath_file, &(it->second), "Si", "Silica"); + + /* Bromide */ + print_total(netpath_file, &(it->second), "Br", "Bromide"); + + /* Boron */ + print_total(netpath_file, &(it->second), "B", "Boron"); + + /* Barium */ + print_total(netpath_file, &(it->second), "Ba", "Barium"); + + /* Lithium */ + print_total(netpath_file, &(it->second), "Li", "Lithium"); + + /* Strontium */ + print_total(netpath_file, &(it->second), "Sr", "Strontium"); + + /* Iron */ + print_total_multi(netpath_file, &(it->second), "Iron", "Fe", "Fe(2)", + "Fe(3)", "", ""); + + + /* Manganese */ + print_total_multi(netpath_file, &(it->second), "Manganese", "Mn", + "Mn(2)", "Mn(3)", "Mn(6)", "Mn(7)"); + + /* Nitrate */ + print_total(netpath_file, &(it->second), "N(5)", "Nitrate"); + + /* Ammonium */ + print_total_multi(netpath_file, &(it->second), "Ammonium", "N(-3)", + "Amm", "", "", ""); + + /* Phosphate */ + print_total(netpath_file, &(it->second), "P", "Phosphate"); + + /* DOC */ + print_total_multi(netpath_file, &(it->second), "DOC", "Fulvate", + "Humate", "", "", ""); + + /* Sp. Cond. */ + fprintf(netpath_file, + " # Sp. Cond.\n"); + + /* Density */ + fprintf(netpath_file, + " # Density\n"); + + /* Delta C-13 TDIC */ + print_isotope(netpath_file, &(it->second), "13C(4)", "Delta C-13 TDIC"); + + /* C-14 TDIC */ + print_isotope(netpath_file, &(it->second), "14C(4)", "C-14 TDIC"); + + /* Delta S-34 (SO4) */ + print_isotope(netpath_file, &(it->second), "34S(6)", + "Delta S-34 (SO4)"); + + /* Delta S-34 (H2S) */ + print_isotope(netpath_file, &(it->second), "34S(-2)", + "Delta S-34 (H2S)"); + + /* Delta Deuterium */ + print_isotope(netpath_file, &(it->second), "2H(1)", "Delta Deuterium"); + + /* Delta O-18 */ + print_isotope(netpath_file, &(it->second), "18O(-2)", "Delta O-18"); + + /* CH4 (aq) */ + print_total(netpath_file, &(it->second), "C(-4)", "CH4 (aq)"); + + /* Sr 87/86 */ + print_isotope(netpath_file, &(it->second), "87Sr", "Sr 87/86"); + + /* Al */ + print_total(netpath_file, &(it->second), "Al", "Alumninum"); + + /* N2 (aq) */ + print_total(netpath_file, &(it->second), "N(0)", "N2 (aq)"); + + /* N-15 of N2 (aq) */ + print_isotope(netpath_file, &(it->second), "15N(0)", "N-15 of N2 (aq)"); + + /* N-15 of Nitrate */ + print_isotope(netpath_file, &(it->second), "15N(5)", "N-15 of Nitrate"); + + /* N-15 of Ammonium */ + print_isotope(netpath_file, &(it->second), "15N(-3)", + "N-15 of Ammonium"); + + /* Formation */ + fprintf(netpath_file, + " # Formation\n"); + + } + if (netpath_file != NULL) + { + fclose(netpath_file); + netpath_file = NULL; + } + return; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +get_inv_total(cxxSolution *solution_ptr, const char *elt) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + if (strcmp(elt, jit->first.c_str()) == 0) + return jit->second; + } + return (0); + +} +/* ---------------------------------------------------------------------- */ +cxxSolutionIsotope *Phreeqc::get_isotope(cxxSolution *solution_ptr, const char *elt) +/* ---------------------------------------------------------------------- */ +{ + std::string str_elt = elt; + std::map::iterator it; + it = solution_ptr->Get_isotopes().find(str_elt); + if (it != solution_ptr->Get_isotopes().end()) + { + return &(it->second); + } + return (NULL); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + LDBLE moles = get_inv_total(solution_ptr, elt); + if (moles == 0) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) (1000 * moles / solution_ptr->Get_mass_water()), + string); + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_isotope(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + cxxSolutionIsotope *iso_ptr; + iso_ptr = get_isotope(solution_ptr, elt); + if (iso_ptr == NULL) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) iso_ptr->Get_ratio(), string); + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total_multi(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *string, const char *elt0, const char *elt1, + const char *elt2, const char *elt3, const char *elt4) +/* ---------------------------------------------------------------------- */ +{ + char elts[5][MAX_LENGTH]; + LDBLE moles; + LDBLE sum; + int i, found; + + strcpy(elts[0], elt0); + strcpy(elts[1], elt1); + strcpy(elts[2], elt2); + strcpy(elts[3], elt3); + strcpy(elts[4], elt4); + + + sum = 0; + found = FALSE; + for (i = 0; i < 5; i++) + { + moles = get_inv_total(solution_ptr, elts[i]); + if (moles == 0) + { + continue; + } + else + { + sum += moles; + found = TRUE; + } + } + if (found != TRUE) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) (1000 * sum / solution_ptr->Get_mass_water()), string); + } + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_netpath_pat(class inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model + */ + int i, j, k; + cxxSolution *solution_ptr, *solution_ptr_orig; + class master *master_ptr; + LDBLE d1, d2, d3; + LDBLE sum, sum1, sum_iso, d; + std::vector array_save, l_delta_save; + size_t count_unknowns_save, max_row_count_save, + max_column_count_save, count_current_solutions; + int temp, temp_punch; + int solnmap[10][2]; + FILE *model_file; + const class elt_list *next_elt; + int exch; + size_t column; + LDBLE f; + class rxn_token *rxn_ptr; +/* + * print solution data, epsilons, and revised data + */ + if (inv_ptr->pat == NULL) + return (OK); + + array_save = my_array; + l_delta_save = delta; + count_unknowns_save = count_unknowns; + max_row_count_save = max_row_count; + max_column_count_save = max_column_count; + + count_unknowns = 0; + max_row_count = 0; + max_column_count = 0; + + count_current_solutions = 0; + count_inverse_models++; + + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + solution_ptr_orig = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + Utilities::Rxn_copy(Rxn_solution_map, solution_ptr_orig->Get_n_user(), -6); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -6); + xsolution_zero(); + + /* Adjust pH */ + if (inv_ptr->carbon == TRUE) + { + d1 = solution_ptr->Get_ph(); + d2 = inv_delta1[col_ph + i] / inv_delta1[i]; + d3 = d1 + d2; + solution_ptr->Set_ph(d3); + } + + /* put original totals in master */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + /* ignore alkalinity */ + /*master_alk->total = solution_ptr->total_alkalinity; */ + + /* update total in master */ + for (j = 0; j < inv_ptr->elts.size(); j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + d1 = inv_ptr->elts[j].master->total; + d2 = inv_delta1[col_epsilon + j * inv_ptr->count_solns + + i] / inv_delta1[i]; + d3 = d1 + d2; + inv_ptr->elts[j].master->total = d3; + } + + /* put updated total back in solution */ + cxxNameDouble nd; + jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + nd[jit->first] = master_ptr->total; + } + solution_ptr->Set_totals(nd); + + /* update isotopes in solution */ + if (inv_ptr->isotopes.size() > 0) + { + /* adjustments to solution isotope composition */ + for (j = 0; j < inv_ptr->isotope_unknowns.size(); j++) + { + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (inv_ptr->isotope_unknowns[j].elt_name != + string_hsave(kit->second.Get_elt_name().c_str()) || + inv_ptr->isotope_unknowns[j].isotope_number != + kit->second.Get_isotope_number()) + continue; + d1 = kit->second.Get_ratio(); + d2 = inv_delta1[col_isotopes + + i * inv_ptr->isotope_unknowns.size() + + j] / inv_delta1[i]; + d3 = d1 + d2; + kit->second.Set_ratio(d3); + } + } + } + + set_initial_solution(-6, -7); + temp = pr.all; + pr.all = FALSE; + temp_punch = pr.punch; + pr.punch = FALSE; + phrq_io->Set_punch_on(false); + initial_solutions(FALSE); + pr.all = temp; + pr.punch = temp_punch; + phrq_io->Set_punch_on(pr.punch == TRUE); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -7); + + /* Header */ + std::string string; + if (solution_ptr_orig->Get_description().size() > 0) + { + fprintf(netpath_file, "%d. %s\n", count_inverse_models, + solution_ptr_orig->Get_description().c_str()); + } + else + { + fprintf(netpath_file, "%d. Solution %d\n", count_inverse_models, + solution_ptr_orig->Get_n_user()); + } + + /* bookkeeping */ + count_pat_solutions++; + solnmap[count_current_solutions][0] = solution_ptr_orig->Get_n_user(); + solnmap[count_current_solutions][1] = count_pat_solutions; + count_current_solutions++; + + /* Dump info to .pat file */ + print_total_pat(netpath_file, "C", "C"); + print_total_pat(netpath_file, "S", "S"); + print_total_pat(netpath_file, "Ca", "CA"); + print_total_pat(netpath_file, "Al", "AL"); + print_total_pat(netpath_file, "Mg", "MG"); + print_total_pat(netpath_file, "Na", "NA"); + print_total_pat(netpath_file, "K", "K"); + print_total_pat(netpath_file, "Cl", "CL"); + print_total_pat(netpath_file, "F", "F"); + print_total_pat(netpath_file, "Si", "SI"); + print_total_pat(netpath_file, "Br", "BR"); + print_total_pat(netpath_file, "B", "B"); + print_total_pat(netpath_file, "Ba", "BA"); + print_total_pat(netpath_file, "Li", "LI"); + print_total_pat(netpath_file, "Sr", "SR"); + print_total_pat(netpath_file, "Fe", "FE"); + print_total_pat(netpath_file, "Mn", "MN"); + print_total_pat(netpath_file, "N", "N"); + print_total_pat(netpath_file, "P", "P"); + fprintf(netpath_file, "%14g # TEMP\n", (double) solution_ptr->Get_tc()); + print_total_pat(netpath_file, "S(-2)", "H2S"); + print_total_pat(netpath_file, "S(6)", "SO4"); + + /* N15 */ + sum_iso = 0; + sum = 0; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "15N") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # N15\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # N15\n", + (double) (sum_iso / sum)); + } + + /* RS of N */ + sum = 0; + sum = + total("N(-3)") * -3 + total("N(0)") * 0 + total("N(3)") * 3 + + total("N(5)") * 5; + sum1 = total("N(-3)") + total("N(0)") + total("N(3)") + total("N(5)"); + if (sum1 == 0) + { + fprintf(netpath_file, "%14g* # RS of N\n", (double) sum1); + } + else + { + fprintf(netpath_file, "%14g # RS of N\n", + (double) (sum / sum1)); + } + + /* DOX */ + print_total_pat(netpath_file, "O(0)", "DOX"); + + /*HCO3 */ + d = 1000 * sum_match_species("*HCO3*", "C"); + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # HCO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # HCO3\n", (double) d); + } + + /* pH */ + fprintf(netpath_file, "%14g # PH\n", (double) solution_ptr->Get_ph()); + + /*H2CO3* */ + d = 1000 * (molality("H2CO3") + molality("CO2")); + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # H2CO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # H2CO3\n", (double) d); + } + + /*CO3 */ + d = sum_match_species("*CO3*", "C"); + d -= sum_match_species("*HCO3*", "C"); + d *= 1000.0; + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # CO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # CO3\n", (double) d); + } + + /* CARBONATES */ + print_total_pat(netpath_file, "C(4)", "CARBONATES"); + print_total_pat(netpath_file, "Fe(2)", "FE2+"); + print_total_pat(netpath_file, "Fe(3)", "FE3+"); + print_total_pat(netpath_file, "Mn(2)", "MN2+"); + print_total_pat(netpath_file, "Mn(3)", "MN3+"); + print_total_pat(netpath_file, "Mn(6)", "MN6+"); + print_total_pat(netpath_file, "Mn(7)", "MN7+"); + print_total_pat(netpath_file, "C(-4)", "CH4"); + print_total_pat(netpath_file, "Doc", "DOC"); + + /*RS OF DOC */ + fprintf(netpath_file, "%14g* # RS OF DOC\n", 0.0); + + /* Blank */ + print_total_pat(netpath_file, "Blank", "BLANK"); + + /*C13 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "13C") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # C13\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # C13\n", + (double) (sum_iso / sum)); + } + + /*C14 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "14C") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # C14\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # C14\n", + (double) (sum_iso / sum)); + } + + /*SR87 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "87Sr") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # SR87\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # SR87\n", + (double) (sum_iso / sum)); + } + + /*D*/ sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "2H") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # D\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # D\n", (double) (sum_iso / sum)); + } + + /*O-18 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "18O") != NULL) + { + if (strcmp(kit->second.Get_elt_name().c_str(), "O(-2)") == 0) + { + d = solution_ptr->Get_total_o() - total("O(0)"); + } + else if (strcmp(kit->second.Get_elt_name().c_str(), "H(1)") + == 0) + { + d = solution_ptr->Get_total_h() - total("H(0)"); + } + else + { + d = total(kit->second.Get_elt_name().c_str()); + } + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # O-18\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # O-18\n", + (double) (sum_iso / sum)); + } + + /*TRITIUM*/ sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "3H") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # TRITIUM\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # TRITIUM\n", + (double) (sum_iso / sum)); + } + + /*34SSO4 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "34S(6)") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # 34SSO4\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # 34SSO4\n", + (double) (sum_iso / sum)); + } + + /*34SH2S */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "34S(-2)") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # 34SH2S\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # 34SH2S\n", + (double) (sum_iso / sum)); + } + + /* Well number */ + fprintf(netpath_file, "%14d # Well number\n", + count_pat_solutions); + } + my_array = array_save; + delta = l_delta_save; + count_unknowns = count_unknowns_save; + max_row_count = max_row_count_save; + max_column_count = max_column_count_save; + +/* + * Open model file + */ + std::string string; + string = inv_ptr->pat; + replace(".pat", "", string); + trim(string); + std::string string1 = sformatf("%s-%d.mod", string.c_str(), count_inverse_models); + model_file = fopen(string1.c_str(), "w"); + if (model_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", string.c_str()); + error_msg(error_string, STOP); + } + add_to_file("model.fil", string1.c_str()); + + +/* + * Write header + */ + fprintf(model_file, "%s\n", string.c_str()); + +/* + * Write well numbers + */ + for (i = 0; i < count_current_solutions; i++) + { + fprintf(model_file, "%3d", solnmap[i][1]); + } + fprintf(model_file, "\n"); +/* + * Write elements + */ + xsolution_zero(); + for (j = 0; j < (int)master.size(); j++) + { + master[j]->in = FALSE; + } + for (j = 0; j < inv_ptr->elts.size(); j++) + { + master_ptr = inv_ptr->elts[j].master; + master_ptr = master_ptr->elt->primary; + if (strcmp(master_ptr->elt->name, "Alkalinity") == 0) + continue; + if (strcmp(master_ptr->elt->name, "H") == 0) + continue; + if (strcmp(master_ptr->elt->name, "O") == 0) + continue; + if (strcmp(master_ptr->elt->name, "X") == 0) + continue; + if (strcmp(master_ptr->elt->name, "E") == 0) + continue; + master_ptr->in = TRUE; + } + for (j = 0; j < (int)master.size(); j++) + { + if (master[j]->in == TRUE) + { + string = master[j]->elt->name; + Utilities::str_toupper(string); + fprintf(model_file, " %-2s", string.c_str()); + } + } + fprintf(model_file, " %-2s", "RS"); +/* + * Add isotope mole balance + */ + for (j = 0; j < inv_ptr->isotopes.size(); j++) + { + string = sformatf("%d%s", (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + if (strcmp(string.c_str(), "13C") == 0) + fprintf(model_file, " %-2s", "I1"); + if (strcmp(string.c_str(), "14C") == 0) + fprintf(model_file, " %-2s", "I2"); + if (strcmp(string.c_str(), "34S") == 0) + fprintf(model_file, " %-2s", "I3"); + if (strcmp(string.c_str(), "87Sr") == 0) + fprintf(model_file, " %-2s", "I4"); + if (strcmp(string.c_str(), "15N") == 0) + fprintf(model_file, " %-2s", "I9"); + if (strcmp(string.c_str(), "2H") == 0) + fprintf(model_file, " %-2s", "D "); + if (strcmp(string.c_str(), "3H") == 0) + fprintf(model_file, " %-2s", "TR"); + if (strcmp(string.c_str(), "18O") == 0) + fprintf(model_file, " %-2s", "18"); + } + + /* end of element line */ + fprintf(model_file, "\n"); + +/* + * Write phase information + */ + for (size_t i = 0; i < inv_ptr->phases.size(); i++) + { + size_t j = col_phases + i; + /* skip if not in model */ +/* if (equal (inv_delta1[j], 0.0, toler) == TRUE) continue;*/ + + /* Do not include Na exchange phase */ + if (strcmp_nocase(inv_ptr->phases[i].name, "NaX") == 0) + continue; +/* + * Determine if exchange reaction + */ + exch = FALSE; + for (next_elt = &inv_ptr->phases[i].phase->next_elt[0]; + next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "X") == 0) + { + exch = TRUE; + break; + } + } +/* + * Write phase name and constraints + */ + string = inv_ptr->phases[i].name; + string = string.substr(0,8); + string = Utilities::pad_right(string, 8); + if (inv_ptr->phases[i].force == TRUE) + { + string += 'F'; + } + else + { + string += ' '; + } + switch (inv_ptr->phases[i].constraint) + { + case EITHER: + string += ' '; + break; + case PRECIPITATE: + if (exch == TRUE) + { + string += '+'; + } + else + { + string += '-'; + } + break; + case DISSOLVE: + if (exch == TRUE) + { + string += '-'; + } + else + { + string += '+'; + } + break; + } + fprintf(model_file, "%-10s", string.c_str()); +/* + * Write stoichiometry + */ + for (next_elt = &inv_ptr->phases[i].phase->next_elt[0]; + next_elt->elt != NULL; next_elt++) + { + f = 1.0; + if (exch == TRUE) + f = -1.0; + master_ptr = next_elt->elt->primary; + if (strcmp(master_ptr->elt->name, "Alkalinity") == 0) + continue; + if (strcmp(master_ptr->elt->name, "H") == 0) + continue; + if (strcmp(master_ptr->elt->name, "O") == 0) + continue; + if (strcmp(master_ptr->elt->name, "E") == 0) + continue; + string = master_ptr->elt->name; + + if (strcmp(master_ptr->elt->name, "X") == 0) + { + string = "Na"; + f = 1.0; + } + Utilities::str_toupper(string); + fprintf(model_file, " %-2s%12.7f", string.c_str(), + (double) (next_elt->coef * f)); + } +/* + * Calculate RS + */ + std::string token; + sum = 0; + for (rxn_ptr = &inv_ptr->phases[i].phase->rxn_s.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s == s_hplus) + continue; + if (rxn_ptr->s == s_h2o) + continue; + if (rxn_ptr->s->secondary == NULL && rxn_ptr->s != s_eminus) + continue; + if (rxn_ptr->s == s_o2) + { + sum += 4 * rxn_ptr->coef; + } + else if (rxn_ptr->s == s_h2) + { + sum += -2 * rxn_ptr->coef; + } + else if (rxn_ptr->s == s_eminus) + { + sum += -1 * rxn_ptr->coef; + } + else + { + string = rxn_ptr->s->secondary->elt->name; + replace("(", " ", string); + replace(")", " ", string); + std::string::iterator b = string.begin(); + std::string::iterator e = string.end(); + CParser::copy_token(token, b, e); + CParser::copy_token(string1, b, e); + (void)sscanf(string1.c_str(), SCANFORMAT, &f); + sum += f * rxn_ptr->coef; + } + } + if (sum != 0.0) + fprintf(model_file, " %-2s%12.7f", "RS", (double) sum); +/* + * Add isotopes + */ + + for (k = 0; k < inv_ptr->phases[i].isotopes.size(); k++) + { + std::vector& isotope_ref = inv_ptr->phases[i].isotopes; + d1 = isotope_ref[k].ratio; + for (j = 0; j < inv_ptr->isotopes.size(); j++) + { + if ((inv_ptr->isotopes[j].elt_name != isotope_ref[k].elt_name) + || (inv_ptr->isotopes[j].isotope_number != + isotope_ref[k].isotope_number)) + continue; + break; + } + d2 = 0.0; + if (j < inv_ptr->isotopes.size()) + { + column = col_phase_isotopes + i * inv_ptr->isotopes.size() + j; + if (inv_delta1[col_phases + i] != 0.0) + { + d2 = inv_delta1[column] / inv_delta1[col_phases + i]; + } + } + d3 = d1 + d2; + string = sformatf("%d%s", (int)isotope_ref[k].isotope_number, + isotope_ref[k].elt_name); + if (strcmp(string.c_str(), "13C") == 0) + fprintf(model_file, " %-2s%12.7f", "I1", (double) d3); + if (strcmp(string.c_str(), "14C") == 0) + fprintf(model_file, " %-2s%12.7f", "I2", (double) d3); + if (strcmp(string.c_str(), "34S") == 0) + { + fprintf(model_file, " %-2s%12.7f", "I3", (double) d3); + fprintf(model_file, " %-2s%12.7f", "I7", 0.0); + } + if (strcmp(string.c_str(), "87Sr") == 0) + fprintf(model_file, " %-2s%12.7f", "I4", (double) d3); + if (strcmp(string.c_str(), "15N") == 0) + fprintf(model_file, " %-2s%12.7f", "I9", (double) d3); + if (strcmp(string.c_str(), "2H") == 0) + fprintf(model_file, " %-2s%12.7f", "D ", (double) d3); + if (strcmp(string.c_str(), "3H") == 0) + fprintf(model_file, " %-2s%12.7f", "TR", (double) d3); + if (strcmp(string.c_str(), "18O") == 0) + fprintf(model_file, " %-2s%12.7f", "18", (double) d3); + } +/* + * Done with stoichiometry + */ + fprintf(model_file, "\n"); + } + fprintf(model_file, "\n"); +/* + * Write extra stuff at bottom + */ + /* + (Iflag(i),i=2,6), (P(i),i=1,2), (Isdocrs(i),i=0,5), Disalong, + (C14dat(i),i=1,5), (Usera(i),i=1,2), + (C14dat(i),i=8,9), i10, i11, (C14dat(i),i=12,13), + (Dbdata(Well(0),i),i=44,47), Dbdata(Well(0),49), + ((Dbdata(Well(iwell),i),i=44,47),Dbdata(Well(iwell),49),Usera(iwell),iwell=1,Iflag(1)+1) + 9030 FORMAT (5(I2),2(F8.4),6(I1),F6.3,/, + 7(F8.3),/, + 2(F8.3),2(F8.0), 2(F8.3),/, + 5(F8.3),/, + 7(6(F8.3),/)) + */ + /* iflags */ + /*fprintf(model_file,"%2d", i); */ /* not written, 1, mixing, number of mixing wells -1 */ + fprintf(model_file, "%2d", 3); /* 2, exchange */ + i = 0; + if (inv_ptr->isotopes.size() > 0) + i = 1; + fprintf(model_file, "%2d", i); /* 3, Rayleigh */ + fprintf(model_file, "%2d", 1); /* 4, A0 model */ + fprintf(model_file, "%2d", 0); /* 5, Mook/Deines */ + fprintf(model_file, "%2d", 0); /* 6, Evaporation/Dilution */ + /* p */ + fprintf(model_file, "%8.4f%8.4f", 1.0, 0.0); /* P(1),(2) fraction of CO2, fraction of Ca in exch */ + fprintf(model_file, "000000"); /* isdocrs(0,5) doc, rs? */ + fprintf(model_file, "%6.3f\n", 1.0); /* disalong */ + fprintf(model_file, + " 0.000 100.000 0.000 0.000 -25.000 100.000 100.000\n"); + fprintf(model_file, + " 0.000 0.000 0. 0. 0.000 0.000 \n"); + /* Final well data */ + fprintf(model_file, " -40.000 -25.000 0.000 0.000 0.000\n"); + /* other wells */ + for (i = 0; i < count_current_solutions - 1; i++) + { + fprintf(model_file, + " -40.000 -25.000 0.000 0.000 0.000 100.000\n"); + } + fprintf(model_file, "\n"); + fclose(model_file); + state = INVERSE; + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total_pat(FILE * l_netpath_file, const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d; + d = 1000.0 * total(elt); + if (strcmp(elt,"O(0)") == 0) + { + d = d/2.0; + } + if (d == 0) + { + fprintf(l_netpath_file, "%14g%1s # %s\n", (double) d, "*", string); + } + else + { + fprintf(l_netpath_file, "%14g%1s # %s\n", (double) d, " ", string); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_initial_solution(int n_user_old, int n_user_new) +/* ---------------------------------------------------------------------- */ +{ + cxxSolution *solution_ptr; + Utilities::Rxn_copy(Rxn_solution_map, n_user_old, n_user_new); + Rxn_new_solution.insert(n_user_new); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user_new); + solution_ptr->Set_new_def(true); + if (solution_ptr->Get_initial_data() == NULL) + solution_ptr->Create_initial_data(); + solution_ptr->Set_n_user_end(n_user_new); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + cxxISolutionComp temp_comp; + temp_comp.Set_description(jit->first.c_str()); + temp_comp.Set_input_conc(jit->second / solution_ptr->Get_mass_water()); + temp_comp.Set_units("Mol/kgw"); + solution_ptr->Get_initial_data()->Get_comps()[jit->first.c_str()] = temp_comp; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_to_file(const char *filename, const char *string) +/* ---------------------------------------------------------------------- */ +{ + FILE *model_file; + char c; + int i; + char string_line[MAX_LINE]; + + model_file = fopen(filename, "r"); + if (model_file == NULL) + { + model_file = fopen(filename, "w"); + } + if (model_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", filename); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + i = 0; + /* + * Read each line of file, check if equal to string; if not, append string to file + */ + for (;;) + { + c = getc(model_file); + if (c != EOF && c != '\n' && i != MAX_LINE) + { + string_line[i] = c; + i++; + continue; + } + if (i >= MAX_LINE) + { + string_line[MAX_LINE - 1] = '\0'; + error_string = sformatf( "File name in %s is greater than %d characters: %s\n", filename, MAX_LINE, string_line); + warning_msg(error_string); + } + else + { + string_line[i] = '\0'; + } + /* new line or eof */ + string_trim(string_line); + if (strcmp(string_line, string) == 0) + { + fclose(model_file); + return (OK); + } + /* eof, add line */ + if (c == EOF) + { + fclose(model_file); + model_file = fopen(filename, "a"); + if (model_file) + { + fprintf(model_file, "%s\n", string); + fclose(model_file); + } + else + { + error_string = sformatf("Could not open netpath model file: %s\n", filename); + error_msg(error_string, STOP); + } + return (OK); + } + /* new line */ + i = 0; + } +} + diff --git a/phreeqcpp/isotopes.cpp b/phreeqcpp/isotopes.cpp new file mode 100644 index 00000000..68570473 --- /dev/null +++ b/phreeqcpp/isotopes.cpp @@ -0,0 +1,1824 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Solution.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species information for isotopes + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + + int l; + class master_isotope *master_isotope_ptr; + char token[MAX_LENGTH]; + class element *elt_ptr; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "isotope", /* 0 */ + "total_is_major" /* 1 */ + }; + int count_opt_list = 2; + + master_isotope_ptr = NULL; + elt_ptr = NULL; +/* + * Read name followed by options + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* isotope */ + if (elt_ptr == NULL) + { + error_string = sformatf( + "The element of which this isotope is a minor isotope has not been defined, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + /* + * Save an isotope + */ + master_isotope_ptr = NULL; + copy_token(token, &next_char, &l); + master_isotope_ptr = master_isotope_store(token, TRUE); + master_isotope_ptr->elt = elt_ptr; + master_isotope_ptr->minor_isotope = TRUE; + master_isotope_ptr->total_is_major = FALSE; + /* + * Read units + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting units for isotopic values, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + master_isotope_ptr->units = string_hsave(token); + /* + * Read standard + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting isotope ratio of standard, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + (void)sscanf(token, SCANFORMAT, &(master_isotope_ptr->standard)); + opt_save = OPTION_DEFAULT; + break; + case 1: /* total_is_major_isotope */ + error_string = sformatf( + "Obsolete identifier. The total of the element must be the sum of all isotopes. ISOTOPES data block.\n%s", + line); + warning_msg(error_string); + break; + case OPTION_DEFAULT: +/* + * Read and element name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting an element name for isotope definition, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + elt_ptr = element_store(token); + master_isotope_ptr = master_isotope_store(token, TRUE); + master_isotope_ptr->elt = elt_ptr; + master_isotope_ptr->minor_isotope = FALSE; + master_isotope_ptr->total_is_major = FALSE; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate calculate_value + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int l; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + class calculate_value *calculate_value_ptr; + const char* next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + calculate_value_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in CALCULATE_VALUE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPT_1; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read calculate_value name */ +/* + * Read calculate_value name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for calculate_value definition, %s. CALCULATE_VALUES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + calculate_value_ptr = calculate_value_store(token, TRUE); + calculate_value_ptr->new_def = TRUE; + calculate_value_ptr->commands.clear(); + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + opt_save = OPT_1; + break; + + case OPT_1: /* read command */ + if (calculate_value_ptr) + { + calculate_value_ptr->commands.append(";\0"); + calculate_value_ptr->commands.append(line); + opt_save = OPT_1; + } + else + { + error_string = sformatf( + "Expecting a calculate_value definition, %s. CALCULATE_VALUES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", calculate_value[0].commands)); + */ return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads isotope_ratio info, ratios are calculated with + * Basic programs read in CALCULATE_VALUE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int l; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + class isotope_ratio *isotope_ratio_ptr; + const char* next_char; + const char *opt_list[] = { + "no_options" /* 0 */ + }; + int count_opt_list = 0; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + isotope_ratio_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ISOTOPE_RATIOS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* read isotope_ratio name */ +/* + * Read isotope_ratio name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for isotope_ratio definition, %s. ISOTOPE_RATIOS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_ratio_ptr = isotope_ratio_store(token, TRUE); + /* + * Read isotope + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name of isotope for an isotope_ratio definition, %s. ISOTOPE_RATIOS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_ratio_ptr->isotope_name = string_hsave(token); + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads isotope_alpha info, ratios are calculated with + * Basic programs read in CALCULATE_VALUE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int l; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + class isotope_alpha *isotope_alpha_ptr; + const char* next_char; + const char *opt_list[] = { + "no_options" /* 0 */ + }; + int count_opt_list = 0; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + isotope_alpha_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ISOTOPE_ALPHAS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* read isotope_alpha name */ +/* + * Read isotope_alpha name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for isotope_alpha definition, %s. ISOTOPE_ALPHAS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_alpha_ptr = isotope_alpha_store(token, TRUE); + isotope_alpha_ptr->name = string_hsave(token); + if (copy_token(token, &next_char, &l) != EMPTY) + { + isotope_alpha_ptr->named_logk = string_hsave(token); + } + + + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_isotopes(cxxSolution &solution_ref) +/* ---------------------------------------------------------------------- */ +{ + int i; + class master_isotope *master_isotope_ptr; + LDBLE total_moles; + /* + * zero out isotopes + */ + for (i = 0; i < (int)master_isotope.size(); i++) + { + master_isotope[i]->moles = 0; + } + master_isotope_ptr = master_isotope_search("H"); + if (master_isotope_ptr != NULL) + { + total_moles = total_h_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + master_isotope_ptr = master_isotope_search("O"); + if (master_isotope_ptr != NULL) + { + total_moles = total_o_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + cxxNameDouble::iterator it = solution_ref.Get_totals().begin(); + for ( ; it != solution_ref.Get_totals().end(); it++) + { + master_isotope_ptr = master_isotope_search(it->first.c_str()); + if (master_isotope_ptr == NULL) + continue; + if (master_isotope_ptr->minor_isotope == FALSE) + { + total_moles = total(master_isotope_ptr->name) * mass_water_aq_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + } + /* + * Set isotopes flag + */ + initial_solution_isotopes = FALSE; + for (i = 0; i < (int)master_isotope.size(); i++) + { + if (master_isotope[i]->minor_isotope == TRUE + && master_isotope[i]->moles > 0) + { + initial_solution_isotopes = TRUE; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_isotope_moles(class element *elt_ptr, + cxxSolution *solution_ptr, LDBLE total_moles) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l_iter; + int count_isotopes, total_is_major; + class master_isotope *master_isotope_ptr, *master_isotope_ptr1; + class master_isotope list[MAX_ELTS]; + LDBLE m_major, tot; + /* + * Get total concentration of elt_ptr + */ + if (total_moles <= 0) + { + error_string = sformatf( + "Cannot calculate molality of isotopes, molality of element is zero, %s", + elt_ptr->name); + warning_msg(error_string); + return (ERROR); + } + m_major = total_moles; + /* + * Make a list of isotopes + */ + count_isotopes = 0; + total_is_major = FALSE; + master_isotope_ptr = master_isotope_search("H"); + if ((master_isotope_ptr != NULL) && (master_isotope_ptr->elt == elt_ptr)) + { + // memcpy(&(list[count_isotopes]), master_isotope_ptr, + // sizeof(class master_isotope)); + list[count_isotopes] = *master_isotope_ptr; + list[count_isotopes].ratio = 1.0; + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + master_isotope_ptr = master_isotope_search("O"); + if ((master_isotope_ptr != NULL) && (master_isotope_ptr->elt == elt_ptr)) + { + // memcpy(&(list[count_isotopes]), master_isotope_ptr, + // sizeof(class master_isotope)); + list[count_isotopes] = *master_isotope_ptr; + list[count_isotopes].ratio = 1.0; + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + if (solution_ptr->Get_initial_data() != NULL) + { + std::map::iterator it = solution_ptr->Get_initial_data()->Get_comps().begin(); + for ( ; it != solution_ptr->Get_initial_data()->Get_comps().end(); it++) + { + master_isotope_ptr = master_isotope_search(it->first.c_str()); + if (master_isotope_ptr == NULL) + continue; + if (master_isotope_ptr->elt != elt_ptr) + continue; + // memcpy(&(list[count_isotopes]), master_isotope_ptr, + // sizeof(class master_isotope)); + list[count_isotopes] = *master_isotope_ptr; + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + } + /* + * Loop to calculate isotope molalities + */ + for (l_iter = 0; l_iter < itmax; l_iter++) + { + tot = 0; + for (i = 0; i < count_isotopes; i++) + { + if (list[i].minor_isotope == FALSE) + { + list[i].moles = m_major; + tot += m_major; + continue; + } + if (strcmp_nocase(list[i].units, "permil") == 0) + { + from_permil(&(list[i]), m_major); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pct") == 0) + { + from_pct(&(list[i]), total_moles); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pmc") == 0) + { + from_pct(&(list[i]), total_moles); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "tu") == 0) + { + from_tu(&(list[i])); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pci/l") == 0) + { + from_pcil(&(list[i])); + tot += list[i].moles; + continue; + } + error_string = sformatf( "Isotope units not recognized, %s", + list[i].units); + input_error++; + error_msg(error_string, CONTINUE); + } + if (total_is_major == TRUE) + break; + if (fabs(total_moles - tot) < convergence_tolerance * total_moles) + { + break; + } + else + { + m_major = m_major * total_moles / tot; + } + } + if (l_iter >= itmax) + { + error_msg("Failed to converge in CALCULATE_ISOTOPE_MOLES.", STOP); + } + /* + * Update master_isotope + */ + for (j = 0; j < (int)master_isotope.size(); j++) + { + for (i = 0; i < count_isotopes; i++) + { + if (list[i].name == master_isotope[j]->name) + { + // memcpy(master_isotope[j], &(list[i]), + // sizeof(class master_isotope)); + *master_isotope[j] = list[i]; + } + } + } + /* + * Update solution + */ + master_isotope_ptr1 = master_isotope_search("H"); + if (master_isotope_ptr1 != NULL && master_isotope_ptr1->elt == elt_ptr) + { + total_h_x = m_major; + } + master_isotope_ptr1 = master_isotope_search("O"); + if (master_isotope_ptr1 != NULL && master_isotope_ptr1->elt == elt_ptr) + { + total_o_x = m_major; + } + //cxxNameDouble nd(solution_ptr->Get_totals()); + //cxxNameDouble::iterator iit = solution_ptr->Get_totals().begin(); + //for ( ; iit != solution_ptr->Get_totals().end(); iit++) + //{ + // master_isotope_ptr = master_isotope_search(iit->first.c_str()); + // if (master_isotope_ptr == NULL) + // continue; + // if (master_isotope_ptr->elt != elt_ptr) + // continue; + // nd[iit->first] = master_isotope_ptr->moles; + //} + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_permil(class master_isotope *master_isotope_ptr, LDBLE major_total) +/* ---------------------------------------------------------------------- */ +{ + LDBLE r; + + r = (master_isotope_ptr->ratio / 1000. + + 1.0) * master_isotope_ptr->standard; + master_isotope_ptr->moles = major_total * r; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_pct(class master_isotope *master_isotope_ptr, LDBLE total_moles) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio / 100 * master_isotope_ptr->standard * + total_moles; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_tu(class master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio * master_isotope_ptr->standard * + mass_water_aq_x / gfw_water; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_pcil(class master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio * master_isotope_ptr->standard * + mass_water_aq_x / gfw_water; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_initial_solution_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + + if (pr.initial_isotopes == FALSE || pr.all == FALSE) + return (OK); + if (state != INITIAL_SOLUTION) + return (OK); + if (initial_solution_isotopes == FALSE) + return (OK); +/* + * Print heading + */ + print_centered("Isotopes"); + output_msg(sformatf( "%10s\t%12s\t%12s\t%12s\t%12s\n\n", "Isotope", + "Molality", "Moles", "Ratio", "Units")); + for (i = 0; i < (int)master_isotope.size(); i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + { + print_isotope = FALSE; + for (j = 0; j < (int)master_isotope.size(); j++) + { + if ((master_isotope[j]->elt == master_isotope[i]->elt) && + (master_isotope[j]->minor_isotope == TRUE) && + (master_isotope[j]->moles > 0)) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + continue; + /* + * Print isotope values + */ + output_msg(sformatf( "%10s\t%12.5e\t%12.5e\n", + master_isotope[i]->name, + (double) (master_isotope[i]->moles / mass_water_aq_x), + (double) master_isotope[i]->moles)); + for (j = 0; j < (int)master_isotope.size(); j++) + { + if (i == j) + continue; + if ((master_isotope[j]->elt == master_isotope[i]->elt) && + (master_isotope[j]->minor_isotope == TRUE)) + { + output_msg(sformatf( + "%10s\t%12.5e\t%12.5e\t%12.5e\t%12s\n", + master_isotope[j]->name, + (double) (master_isotope[j]->moles / + mass_water_aq_x), + (double) master_isotope[j]->moles, + (double) master_isotope[j]->ratio, + master_isotope[j]->units)); + } + } + output_msg(sformatf( "\n")); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch isotope ratios relative to standards + */ + //int i; + LDBLE iso; + class isotope_ratio *isotope_ratio_ptr; + class master_isotope *master_isotope_ptr; + + //if (punch.in == FALSE || punch.isotopes == FALSE) + // return (OK); + if (current_selected_output->Get_isotopes().size() == 0) + return(OK); + //if (punch.count_isotopes == 0) + // return (OK); + //for (i = 0; i < punch.count_isotopes; i++) + for (size_t i = 0; i < current_selected_output->Get_isotopes().size(); i++) + { + iso = MISSING; + if (state == INITIAL_SOLUTION) + { + isotope_ratio_ptr = isotope_ratio_search(current_selected_output->Get_isotopes()[i].first.c_str()); + if (isotope_ratio_ptr != NULL) + { + master_isotope_ptr = + master_isotope_search(isotope_ratio_ptr->isotope_name); + if (master_isotope_ptr != NULL + && master_isotope_ptr->minor_isotope == TRUE) + { + iso = master_isotope_ptr->ratio; + } + } + } + else + { + isotope_ratio_ptr = isotope_ratio_search(current_selected_output->Get_isotopes()[i].first.c_str()); + if (isotope_ratio_ptr != NULL) + { + iso = isotope_ratio_ptr->converted_ratio; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("I_%s", current_selected_output->Get_isotopes()[i].first.c_str()), "%12.4e\t", + (double) iso); + } + else + { + fpunchf(sformatf("I_%s", current_selected_output->Get_isotopes()[i].first.c_str()), "%20.12e\t", + (double) iso); + } + + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch calculate values + */ + //int i; + LDBLE result; + class calculate_value *calculate_value_ptr; + char l_command[] = "run"; + + if (current_selected_output->Get_calculate_values().size() == 0) + return OK; + + for (size_t i = 0; i < current_selected_output->Get_calculate_values().size(); i++) + { + result = MISSING; + calculate_value_ptr = + calculate_value_search(current_selected_output->Get_calculate_values()[i].first.c_str()); + if (!calculate_value_ptr) + { + error_string = sformatf( + "Definition not found for CALCULATE_VALUES %s.", + current_selected_output->Get_calculate_values()[i].first.c_str()); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands.c_str(), &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + if (calculate_value_ptr != NULL) + { + result = calculate_value_ptr->value; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("V_%s", current_selected_output->Get_calculate_values()[i].first.c_str()), + "%12.4e\t", (double) result); + } + else + { + fpunchf(sformatf("V_%s", current_selected_output->Get_calculate_values()[i].first.c_str()), + "%20.12e\t", (double) result); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + class master *master_ptr; + class master_isotope *master_isotope_ptr; + char token[MAX_LENGTH]; + + + if (pr.isotope_ratios == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + return (OK); +/* + * Print heading + */ + print_isotope = FALSE; + for (i = 0; i < (int)master_isotope.size(); i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + continue; + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + continue; + if (master_ptr->total > 0 || master_ptr->s->moles > 0) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + return (OK); + + print_centered("Isotope Ratios"); + output_msg(sformatf( "%25s\t%12s\t%15s\n\n", "Isotope Ratio", + "Ratio", "Input Units")); + + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + master_isotope_ptr = + master_isotope_search(isotope_ratio[j]->isotope_name); + /* + * Print isotope ratio + */ + strcpy(token, isotope_ratio[j]->name); + while (replace("_", " ", token) == TRUE); + output_msg(sformatf( " %-20s\t%12.5e\t%15.5g %-10s\n", + token, (double) isotope_ratio[j]->ratio, + (double) isotope_ratio[j]->converted_ratio, + master_isotope_ptr->units)); + } + output_msg(sformatf( "\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + class master *master_ptr; + char token[MAX_LENGTH]; + LDBLE log_alpha; + + if (pr.isotope_alphas == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + return (OK); +/* + * Print heading + */ + print_isotope = FALSE; + for (i = 0; i < (int)master_isotope.size(); i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + continue; + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + continue; + if (master_ptr->total > 0 || master_ptr->s->moles > 0) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + return (OK); + + print_centered("Isotope Alphas"); + output_msg(sformatf( "%75s\n", "1000ln(Alpha)")); + output_msg(sformatf( "%79s\n", "----------------------")); + output_msg(sformatf( "%-37s%14s%14s%12.1f C\n\n", + " Isotope Ratio", "Solution alpha", "Solution", + (double) tc_x)); + + for (j = 0; j < (int)isotope_alpha.size(); j++) + { + if (isotope_alpha[j]->value == MISSING) + continue; + /* + * Print isotope ratio + */ + strcpy(token, isotope_alpha[j]->name); + while (replace("_", " ", token) == TRUE); + if (isotope_alpha[j]->named_logk != NULL) + { + if (isotope_alpha[j]->value <= 0) + { + log_alpha = -999.999; + } + else + { + log_alpha = 1000 * log(isotope_alpha[j]->value); + } + output_msg(sformatf( "%-37s%14.5g%14.5g%14.5g\n", token, + (double) isotope_alpha[j]->value, (double) log_alpha, + (double) (1000 * + calc_logk_n(isotope_alpha[j]->named_logk) * + LOG_10))); + } + else + { + output_msg(sformatf( "%-37s%14.5g%14.5g\n", token, + (double) isotope_alpha[j]->value, + (double) (1000 * log(isotope_alpha[j]->value)))); + } + + } + output_msg(sformatf( "\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ + int j; + class calculate_value *calculate_value_ptr; + class isotope_ratio *isotope_ratio_ptr; + class isotope_alpha *isotope_alpha_ptr; + class master_isotope *master_isotope_ptr; + char l_command[] = "run"; + + + /* + * initialize ratios as missing + */ + for (j = 0; j < calculate_value.size(); j++) + { + calculate_value[j]->calculated = FALSE; + calculate_value[j]->value = MISSING; + } + if (pr.isotope_ratios == TRUE) + { + for (j = 0; j < (int)isotope_ratio.size(); j++) + { + isotope_ratio_ptr = isotope_ratio[j]; + master_isotope_ptr = + master_isotope_search(isotope_ratio_ptr->isotope_name); + if (master_isotope_ptr->master->s->in == FALSE) continue; + calculate_value_ptr = calculate_value_search(isotope_ratio_ptr->name); + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands.c_str(), &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->value == MISSING) + { + isotope_ratio_ptr->ratio = MISSING; + isotope_ratio_ptr->converted_ratio = MISSING; + } + else + { + isotope_ratio_ptr->ratio = calculate_value_ptr->value; + isotope_ratio_ptr->converted_ratio = + convert_isotope(master_isotope_ptr, + calculate_value_ptr->value); + } + } + } + if (pr.isotope_alphas == TRUE) + { + for (j = 0; j < (int)isotope_alpha.size(); j++) + { + isotope_alpha_ptr = isotope_alpha[j]; + calculate_value_ptr = calculate_value_search(isotope_alpha_ptr->name); + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands.c_str(), &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + if (calculate_value_ptr->value == MISSING) + { + isotope_alpha_ptr->value = MISSING; + } + else + { + isotope_alpha_ptr->value = calculate_value_ptr->value; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +convert_isotope(class master_isotope * master_isotope_ptr, LDBLE ratio) +/* ---------------------------------------------------------------------- */ +{ + const char *units; + units = master_isotope_ptr->units; + + if (strcmp_nocase(units, "permil") == 0) + { + return ((ratio / master_isotope_ptr->standard - 1) * 1000); + } + if (strcmp_nocase(units, "pct") == 0) + { + return (ratio / master_isotope_ptr->standard * 100.); + } + if (strcmp_nocase(units, "pmc") == 0) + { + return (ratio / master_isotope_ptr->standard * 100.); + } + if (strcmp_nocase(units, "tu") == 0) + { + return (ratio / master_isotope_ptr->standard); + } + if (strcmp_nocase(units, "pci/l") == 0) + { + return (ratio / master_isotope_ptr->standard); + } + error_string = sformatf( + "Did not recognize isotope units in convert_isotope, %s", units); + error_msg(error_string, STOP); + return (-99.0); +} + +/* + * Utility routines for master_isotope + */ + +/* ---------------------------------------------------------------------- */ +class master_isotope * Phreeqc:: +master_isotope_store(const char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for master_isotope. + * + * Pointer to a master_isotope structure is always returned. + * + * If the string is not found, a new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old master_isotope structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old master_isotope structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "master_isotope". + * replace_if_found input, TRUE means reinitialize master_isotope structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to master_isotope structure "master_isotope" where "name" can be found. + */ + int n; + class master_isotope *master_isotope_ptr; +/* + * Search list + */ + std::map::iterator mi_it = + master_isotope_map.find(name); + if (mi_it != master_isotope_map.end() && replace_if_found == FALSE) + { + master_isotope_ptr = mi_it->second; + return (master_isotope_ptr); + } + else if (mi_it != master_isotope_map.end() && replace_if_found == TRUE) + { + master_isotope_ptr = mi_it->second; + master_isotope_init(master_isotope_ptr); + } + else + { + n = (int)master_isotope.size(); + master_isotope.resize((size_t)n + 1); + /* Make new master_isotope structure */ + master_isotope[n] = master_isotope_alloc(); + master_isotope_ptr = master_isotope[n]; + } + /* set name and z in pointer in master_isotope structure */ + master_isotope_ptr->name = string_hsave(name); +/* + * Update map + */ + master_isotope_map[name] = master_isotope_ptr; + return (master_isotope_ptr); +} + +/* ---------------------------------------------------------------------- */ +class master_isotope * Phreeqc:: +master_isotope_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a master_isotope structure, initializes + * arguments: void + * return: pointer to a master_isotope structure + */ +{ + class master_isotope *master_isotope_ptr = new class master_isotope; +/* + * set pointers in structure to NULL, variables to zero + */ + master_isotope_init(master_isotope_ptr); + + return (master_isotope_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +master_isotope_init(class master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a master_isotope structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (master_isotope_ptr) + { + master_isotope_ptr->name = NULL; + master_isotope_ptr->master = NULL; + master_isotope_ptr->elt = NULL; + master_isotope_ptr->units = NULL; + master_isotope_ptr->standard = 0; + master_isotope_ptr->ratio = 0; + master_isotope_ptr->moles = 0; + master_isotope_ptr->total_is_major = 0; + master_isotope_ptr->minor_isotope = 1; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class master_isotope * Phreeqc:: +master_isotope_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for master_isotope. + * + * Arguments: + * name input, character string to be found in "master_isotope". + * + * Returns: + * pointer to master_isotope structure "master_isotope" where "name" can be found. + * or NULL if not found. + */ + class master_isotope* master_isotope_ptr = NULL; +/* + * Search list + */ + std::map::iterator mi_it = + master_isotope_map.find(name); + if (mi_it != master_isotope_map.end()) + { + master_isotope_ptr = mi_it->second; + return (master_isotope_ptr); + } + return (NULL); +} + +/* + * Utility routines for calculate_value + */ + +/* ---------------------------------------------------------------------- */ +class calculate_value * Phreeqc:: +calculate_value_store(const char *name_in, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for calculate_value. + * + * Pointer to a calculate_value structure is always returned. + * + * If the string is not found, a new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old calculate_value structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old calculate_value structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "calculate_value". + * replace_if_found input, TRUE means reinitialize calculate_value structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to calculate_value structure "calculate_value" where "name" can be found. + */ + class calculate_value *calculate_value_ptr=NULL; +/* + * Search list + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator cv_it = + calculate_value_map.find(name); + if (cv_it != calculate_value_map.end() && replace_if_found == FALSE) + { + calculate_value_ptr = cv_it->second; + return (calculate_value_ptr); + } + else if (cv_it != calculate_value_map.end() && replace_if_found == TRUE) + { + calculate_value_ptr = cv_it->second; + calculate_value_free(calculate_value_ptr); + calculate_value_init(calculate_value_ptr); + } + else + { + size_t n = calculate_value.size(); + calculate_value.resize(n+1); + /* Make new calculate_value structure */ + calculate_value[n] = calculate_value_alloc(); + calculate_value_ptr = calculate_value[n]; + } + /* set name in calculate_value structure */ + calculate_value_ptr->name = string_hsave(name_in); +/* + * Update map + */ + calculate_value_map[name] = calculate_value_ptr; + return (calculate_value_ptr); +} + +/* ---------------------------------------------------------------------- */ +class calculate_value * Phreeqc:: +calculate_value_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a calculate_value structure, initializes + * arguments: void + * return: pointer to a calculate_value structure + */ +{ + class calculate_value *calculate_value_ptr = + new class calculate_value; +/* + * set pointers in structure to NULL, variables to zero + */ + calculate_value_init(calculate_value_ptr); + + return (calculate_value_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +calculate_value_init(class calculate_value *calculate_value_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a calculate_value structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (calculate_value_ptr) + { + calculate_value_ptr->name = NULL; + calculate_value_ptr->value = 0.0; + calculate_value_ptr->commands.clear(); + calculate_value_ptr->new_def = TRUE; + calculate_value_ptr->calculated = FALSE; + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class calculate_value * Phreeqc:: +calculate_value_search(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for calculate_value. + * + * Arguments: + * name input, character string to be found in "calculate_value". + * + * Returns: + * pointer to calculate_value structure "calculate_value" where "name" can be found. + * or NULL if not found. + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator cv_it = + calculate_value_map.find(name); + if (cv_it != calculate_value_map.end()) + { + return (cv_it->second); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_value_free(class calculate_value *calculate_value_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within calculate_value[i], does not free calculate_value structure + * Input: i, number of calculate_value + * Return: OK + */ + char cmd[] = "new; quit"; + + if (calculate_value_ptr == NULL) + return (ERROR); + calculate_value_ptr->commands.clear(); + basic_run(cmd, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase); + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + return (OK); +} + +/* + * Utility routines for isotope_ratio + */ + +/* ---------------------------------------------------------------------- */ +class isotope_ratio * Phreeqc:: +isotope_ratio_store(const char *name_in, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for isotope_ratio. + * + * Pointer to a isotope_ratio structure is always returned. + * + * If the string is not found, a new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old isotope_ratio structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old isotope_ratio structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "isotope_ratio". + * replace_if_found input, TRUE means reinitialize isotope_ratio structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to isotope_ratio structure "isotope_ratio" where "name" can be found. + */ + class isotope_ratio *isotope_ratio_ptr; +/* + * Search list + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator it = + isotope_ratio_map.find(name); + + if (it != isotope_ratio_map.end() && replace_if_found == FALSE) + { + isotope_ratio_ptr = it->second; + return (isotope_ratio_ptr); + } + else if (it != isotope_ratio_map.end() && replace_if_found == TRUE) + { + isotope_ratio_ptr = it->second; + isotope_ratio_init(isotope_ratio_ptr); + } + else + { + size_t n = isotope_ratio.size(); + isotope_ratio.resize(n + 1); + /* Make new isotope_ratio structure */ + isotope_ratio[n] = isotope_ratio_alloc(); + isotope_ratio_ptr = isotope_ratio[n]; + } + /* set name and z in pointer in isotope_ratio structure */ + isotope_ratio_ptr->name = string_hsave(name_in); +/* + * Update map + */ + isotope_ratio_map[name] = isotope_ratio_ptr; + return (isotope_ratio_ptr); +} + +/* ---------------------------------------------------------------------- */ +class isotope_ratio * Phreeqc:: +isotope_ratio_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a isotope_ratio structure, initializes + * arguments: void + * return: pointer to a isotope_ratio structure + */ +{ + class isotope_ratio* isotope_ratio_ptr = + new class isotope_ratio; +/* + * set pointers in structure to NULL, variables to zero + */ + isotope_ratio_init(isotope_ratio_ptr); + + return (isotope_ratio_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +isotope_ratio_init(class isotope_ratio *isotope_ratio_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a isotope_ratio structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (isotope_ratio_ptr) + { + isotope_ratio_ptr->name = NULL; + isotope_ratio_ptr->isotope_name = NULL; + isotope_ratio_ptr->ratio = MISSING; + isotope_ratio_ptr->converted_ratio = MISSING; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class isotope_ratio * Phreeqc:: +isotope_ratio_search(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for isotope_ratio. + * + * Arguments: + * name input, character string to be found in "isotope_ratio". + * + * Returns: + * pointer to isotope_ratio structure "isotope_ratio" where "name" can be found. + * or NULL if not found. + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator it = + isotope_ratio_map.find(name); + + if (it != isotope_ratio_map.end()) + { + return (it->second); + } + return (NULL); +} + +/* + * Utility routines for isotope_alpha + */ + +/* ---------------------------------------------------------------------- */ +class isotope_alpha * Phreeqc:: +isotope_alpha_store(const char *name_in, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for isotope_alpha. + * + * Pointer to a isotope_alpha structure is always returned. + * + * If the string is not found, a new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old isotope_alpha structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old isotope_alpha structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "isotope_alpha". + * replace_if_found input, TRUE means reinitialize isotope_alpha structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to isotope_alpha structure "isotope_alpha" where "name" can be found. + */ + class isotope_alpha *isotope_alpha_ptr; + std::string name = name_in; + str_tolower(name); + std::map::iterator it = + isotope_alpha_map.find(name); + + if (it != isotope_alpha_map.end() && replace_if_found == FALSE) + { + return (it->second); + } + else if (it != isotope_alpha_map.end() && replace_if_found == TRUE) + { + isotope_alpha_ptr = it->second; + isotope_alpha_init(isotope_alpha_ptr); + } + else + { + size_t n = isotope_alpha.size(); + isotope_alpha.resize(n + 1); + /* Make new isotope_alpha structure */ + isotope_alpha[n] = isotope_alpha_alloc(); + isotope_alpha_ptr = isotope_alpha[n]; + } + /* set name and z in pointer in isotope_alpha structure */ + isotope_alpha_ptr->name = string_hsave(name_in); +/* + * Update map + */ + isotope_alpha_map[name] = isotope_alpha_ptr; + return (isotope_alpha_ptr); +} + +/* ---------------------------------------------------------------------- */ +class isotope_alpha * Phreeqc:: +isotope_alpha_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a isotope_alpha structure, initializes + * arguments: void + * return: pointer to a isotope_alpha structure + */ +{ + class isotope_alpha* isotope_alpha_ptr = + new class isotope_alpha; +/* + * set pointers in structure to NULL, variables to zero + */ + isotope_alpha_init(isotope_alpha_ptr); + + return (isotope_alpha_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +isotope_alpha_init(class isotope_alpha *isotope_alpha_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a isotope_alpha structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (isotope_alpha_ptr) + { + isotope_alpha_ptr->name = NULL; + isotope_alpha_ptr->named_logk = NULL; + isotope_alpha_ptr->value = MISSING; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class isotope_alpha * Phreeqc:: +isotope_alpha_search(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for isotope_alpha. + * + * Arguments: + * name input, character string to be found in "isotope_alpha". + * + * Returns: + * pointer to isotope_alpha structure "isotope_alpha" where "name" can be found. + * or NULL if not found. + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator it = + isotope_alpha_map.find(name); + + if (it != isotope_alpha_map.end()) + { + return (it->second); + } + return (NULL); +} diff --git a/phreeqcpp/kinetics.cpp b/phreeqcpp/kinetics.cpp new file mode 100644 index 00000000..27ce4bdc --- /dev/null +++ b/phreeqcpp/kinetics.cpp @@ -0,0 +1,3180 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" + +#include + +#include "StorageBin.h" +#include "Reaction.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "cxxMix.h" +#include "PPassemblage.h" +#include "Surface.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "Temperature.h" +#include "cxxKinetics.h" +#include +#include +#include +#include "nvector_serial.h" /* definitions of type N_Vector and macro */ + /* NV_Ith_S, prototypes for N_VNew, N_VFree */ +/* These macros are defined in order to write code which exactly matches + the mathematical problem description given above. + + Ith(v,i) references the ith component of the vector v, where i is in + the range [1..NEQ] and NEQ is defined below. The Ith macro is defined + using the N_VIth macro in nvector.h. N_VIth numbers the components of + a vector starting from 0. + + IJth(A,i,j) references the (i,j)th element of the dense matrix A, where + i and j are in the range [1..NEQ]. The IJth macro is defined using the + DENSE_ELEM macro in dense.h. DENSE_ELEM numbers rows and columns of a + dense matrix starting from 0. */ + +#define Ith(v,i) NV_Ith_S(v,i-1) /* Ith numbers components 1..NEQ */ +#define IJth(A,i,j) DENSE_ELEM(A,i-1,j-1) /* IJth numbers rows,cols 1..NEQ */ + +#define MAX_DIVIDE 2 +#define KINETICS_TOL 1e-8; + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_kinetic_reaction(cxxKinetics *kinetics_ptr, LDBLE time_step) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * determine rates and + * a list of elements and amounts in + * the reaction. + */ + int j, return_value; + LDBLE coef; + char l_command[] = "run"; + class rate *rate_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + return_value = OK; + count_elts = 0; + paren_count = 0; + rate_time = time_step; + +/* t1 = clock(); */ + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + coef = 0.0; +/* + * Send command to basic interpreter + */ + rate_ptr = rate_search(kinetics_comp_ptr->Get_rate_name().c_str(), &j); + if (rate_ptr == NULL) + { + error_string = sformatf( "Rate not found for %s", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + else + { + rate_moles = NAN; + rate_m = kinetics_comp_ptr->Get_m(); + rate_m0 = kinetics_comp_ptr->Get_m0(); + rate_p = kinetics_comp_ptr->Get_d_params(); + count_rate_p = (int) kinetics_comp_ptr->Get_d_params().size(); + if (rate_ptr->new_def == TRUE) + { + if (basic_compile + (rates[j].commands.c_str(), &rates[j].linebase, &rates[j].varbase, + &rates[j].loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in rate %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + + rate_ptr->new_def = FALSE; + } + if (basic_run + (l_command, rates[j].linebase, rates[j].varbase, + rates[j].loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in rate %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Moles of reaction not SAVEed for %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + else + { + + coef = rate_moles; + } + } +/* + * Accumulate moles of reaction for component + */ + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() + coef); + if (coef == 0.0) + continue; + } +/* t2=clock(); + printf("secs in reac %e, t2 %e\n", t2-t1, t1); + */ + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_final_kinetic_reaction(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * using extrapolated values, which were + * stored in moles in run_kinetics + */ + LDBLE coef; + class phase *phase_ptr; + class master *master_ptr; + int count= 0; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ +RESTART: // if limiting rates, jump to here + count++; + kinetics_ptr->Get_totals().clear(); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + count_elts = 0; + paren_count = 0; + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_moles() > m_temp[i]) + { + kinetics_comp_ptr->Set_moles(m_temp[i]); + kinetics_comp_ptr->Set_m(0); + } + coef = kinetics_comp_ptr->Get_moles(); + if (coef == 0.0) + continue; +/* + * Reactant is a pure phase, copy formula into token + */ + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef1 = it->second; + phase_ptr = NULL; + int k; + phase_ptr = phase_bsearch(name.c_str(), &k, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, + coef *coef1); + } + else + { + const char* ptr = name.c_str(); + if (get_elts_in_species(&ptr, coef * coef1) == ERROR) + { + error_string = sformatf("Error in -formula: %s", name.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + if (use.Get_exchange_ptr() != NULL + && use.Get_exchange_ptr()->Get_related_rate()) + { + cxxExchange * exchange_ptr = use.Get_exchange_ptr(); + for(size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + std::string name(exchange_ptr->Get_exchange_comps()[j].Get_rate_name()); + if (name.size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + name.c_str()) == 0) + { + /* found kinetics component */ + std::string formula = exchange_ptr->Get_exchange_comps()[j].Get_formula().c_str(); + const char* ptr = formula.c_str(); + if (get_elts_in_species(&ptr, -coef*exchange_ptr->Get_exchange_comps()[j].Get_phase_proportion()) == ERROR) + { + error_string = sformatf("Error in -formula: %s", formula.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + + } + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_related_rate()) + { + for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_rate_name().size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + surface_comp_ptr->Get_rate_name().c_str()) == 0) + { + /* found kinetics component */ + std::string temp_formula = surface_comp_ptr->Get_formula().c_str(); + const char* cptr = temp_formula.c_str(); + /* Surface = 0 when m becomes low ... + */ + if (0.9 * surface_comp_ptr->Get_phase_proportion() * + (kinetics_comp_ptr->Get_m()) < MIN_RELATED_SURFACE) + { + master_ptr = master_bsearch(surface_comp_ptr->Get_master_element().c_str()); + if (master_ptr != NULL) + { + master_ptr->total = 0.0; + } + } + else + { + if (get_elts_in_species(&cptr, -coef * surface_comp_ptr->Get_phase_proportion()) == ERROR) + { + error_string = sformatf("Error in -formula: %s", temp_formula.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + } + } + kinetics_comp_ptr->Set_moles_of_reaction(elt_list_NameDouble()); + kinetics_ptr->Get_totals().add_extensive(kinetics_comp_ptr->Get_moles_of_reaction(), 1.0); + } + if (count > 2) + { +#if !defined(R_SO) + fprintf(stderr, "Too many limit_rates-.\n"); +#else + error_msg("Too many limit_rates-.\n"); +#endif + } + else + { + if (limit_rates(kinetics_ptr)) + goto RESTART; + } + if (count > 2) + { +#if !defined(R_SO) + fprintf(stderr, "Too many limit_rates+.\n"); +#else + error_msg("Too many limit_rates+.\n"); +#endif + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rk_kinetics(int i, LDBLE kin_time, int use_mix, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Runge-Kutta-Fehlberg method; 6 evaluations of the derivative + * give O(h^5) global error and error estimate + * calc_kinetic_reaction(.., ..) calculates moles of intermediate derivatives; + * these are calc'd for the whole step h. + * calc_final_kinetic reaction(..) translates moles to PHREEQC reaction. + */ + int k, save_old; + int l_bad, step_bad, step_ok; + int n_reactions; + LDBLE h, h_old, h_sum; + LDBLE l_error, error_max, safety, moles_max, moles_reduction; + cxxKinetics *kinetics_ptr; + int equal_rate, zero_rate; + + cxxPPassemblage *pp_assemblage_save = NULL; + cxxSSassemblage *ss_assemblage_save = NULL; + + LDBLE b31 = 3. / 40., b32 = 9. / 40., + b51 = -11. / 54., b53 = -70. / 27., b54 = 35. / 27., + b61 = 1631. / 55296., b62 = 175. / 512., b63 = 575. / 13824., b64 = + 44275. / 110592., b65 = 253. / 4096., c1 = 37. / 378., c3 = + 250. / 621., c4 = 125. / 594., c6 = 512. / 1771., dc5 = + -277. / 14336.; + LDBLE dc1 = c1 - 2825. / 27648., dc3 = c3 - 18575. / 48384., dc4 = + c4 - 13525. / 55296., dc6 = c6 - 0.25; +/* + * Save kinetics i and solution i, if necessary + */ + save_old = -2 - (count_cells * (1 + stag_data.count_stag) + 2); + Utilities::Rxn_copy(Rxn_kinetics_map, i, save_old); + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + } + +/* + * Malloc some space + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kinetics_ptr == NULL) + return (OK); + n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + rk_moles.resize(6 * (size_t)n_reactions); + + /*if (use_mix != NOMIX) last_model.force_prep = TRUE; */ + set_and_run_wrapper(i, use_mix, FALSE, i, step_fraction); + run_reactions_iterations += iterations; + + saver(); + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, NOMIX, TRUE, i); + } + else if (state == ADVECTION) + { + set_advection(i, NOMIX, TRUE, i); + } + else if (state == REACTION) + { + set_reaction(i, NOMIX, TRUE); + } + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + + step_bad = step_ok = 0; + l_bad = FALSE; + h_sum = 0.; + h = h_old = kin_time; + moles_max = 0.1; + moles_reduction = 1.0; + safety = 0.7; + if (kinetics_ptr->Get_rk() < 1) + kinetics_ptr->Set_rk(1); + else if (kinetics_ptr->Get_rk() > 3) + kinetics_ptr->Set_rk(6); + + if (kinetics_ptr->Get_rk() == 6) + equal_rate = FALSE; + else + equal_rate = TRUE; +/* + * if step_divide > 1, initial timestep is divided + * if < 1, step_divide indicates maximal reaction... + */ + if (kinetics_ptr->Get_step_divide() > 1.0) + { + h = h_old = kin_time / kinetics_ptr->Get_step_divide(); + equal_rate = FALSE; + } + else if (kinetics_ptr->Get_step_divide() < 1.0) + moles_max = kinetics_ptr->Get_step_divide(); + + rate_sim_time = rate_sim_time_start + h_sum; + + status(0, NULL); + while (h_sum < kin_time) + { + + if (step_bad > kinetics_ptr->Get_bad_step_max()) + { + error_string = sformatf( + "Bad RK steps > %d in cell %d. Please decrease (time)step or increase -bad_step_max.", + kinetics_ptr->Get_bad_step_max(), cell_no); + error_msg(error_string, STOP); + } + + MOLES_TOO_LARGE: + if (moles_reduction > 1.0) + { + h_old = h; + h = safety * h / (1.0 + moles_reduction); + moles_reduction = 1.0; + equal_rate = FALSE; + l_bad = TRUE; + } +/* + * find k1 + */ + if (l_bad == TRUE) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + rk_moles[j] *= (h / h_old); + kinetics_comp_ptr->Set_moles(rk_moles[j] * 0.2); + kinetics_comp_ptr->Set_m(m_temp[j]); + } + l_bad = FALSE; + } + else + { +/* + * define pointers for calc_kinetic_, they are lost after saver()... + */ + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, NOMIX, TRUE, i); + } + else if (state == ADVECTION) + { + set_advection(i, NOMIX, TRUE, i); + } + else if (state == REACTION) + { + set_reaction(i, NOMIX, TRUE); + } + /* + * Moles of minerals and solid solutions may change to make positive + * concentrations. Reactions may take out more than is present in + * solution. + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_pp_assemblage_ptr()->Get_n_user()); + assert(pp_assemblage_ptr); + pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_ss_assemblage_ptr()->Get_n_user()); + assert(ss_assemblage_ptr); + ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } + + rate_sim_time = rate_sim_time_start + h_sum; + calc_kinetic_reaction(kinetics_ptr, h); + + /* store k1 in rk_moles ... */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k2 ... */ + rk_moles[j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * 0.2); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + } +/* + * Quit rk with rk = 1 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 1 && equal_rate) + { + zero_rate = TRUE; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(rk_moles[j]); + if (fabs(kinetics_comp_ptr->Get_moles()) > MIN_TOTAL) + zero_rate = FALSE; + } + + if (zero_rate == FALSE) + { + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == + MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + } + if (zero_rate || equal_rate) + { + /* removing the following line causes different results for + example 6 distributed with the program */ + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + goto EQUAL_RATE_OUT; + } + else + { + kinetics_ptr->Set_rk(3); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.2 * rk_moles[j]); + } + } + } +/* + * Continue with rk ... + */ + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; + +/* + * find k2 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.2 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k2 in rk_moles */ + k = n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k3 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + + kinetics_comp_ptr->Set_moles(b31 * rk_moles[j] + + b32 * rk_moles[k + j]); +/* + * check for equal_rate ... + */ + if (equal_rate + && fabs(rk_moles[j] - rk_moles[k + j]) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + } + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; +/* + * Quit rk with rk = 2 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 2 && equal_rate) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles( + 0.3 * rk_moles[j] + 0.7 * rk_moles[k + j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate) + kinetics_ptr->Set_rk(1); + + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + goto EQUAL_RATE_OUT; + } +/* + * Continue runge_kutta.. + */ + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k3 + */ + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m (m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.3 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k3 in rk_moles */ + k = 2 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k4 ... */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + + kinetics_comp_ptr->Set_moles(0.3 * rk_moles[j] + - 0.9 * rk_moles[n_reactions + j] + 1.2 * rk_moles[k + j]); +/* + * check for equal_rate ... + */ + if (equal_rate + && fabs(rk_moles[j] - rk_moles[k + j]) > + kinetics_comp_ptr->Get_tol()) + equal_rate = FALSE; + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; +/* + * Quit rk with rk = 3 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 3 && equal_rate) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.5 * rk_moles[j] + - 1.5 * rk_moles[n_reactions + j] + 2 * rk_moles[k + j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0.); + kinetics_comp_ptr->Set_moles(0.); + } + + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate) + kinetics_ptr->Set_rk(1); + + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + goto EQUAL_RATE_OUT; + } +/* + * Continue runge_kutta.. + */ + + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k4 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.6 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k4 in rk_moles */ + k = 3 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + + /* define reaction for calculating k5 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(b51 * rk_moles[j] + + 2.5 * rk_moles[(size_t)n_reactions + j] + + b53 * rk_moles[2 * (size_t)n_reactions + j] + + b54 * rk_moles[(size_t)k + (size_t)j]); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k5 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k5 in rk_moles */ + k = 4 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + + /* define reaction for calculating k6 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(b61 * rk_moles[j] + + b62 * rk_moles[(size_t)n_reactions + j] + + b63 * rk_moles[2 * (size_t)n_reactions + j] + + b64 * rk_moles[3 * (size_t)n_reactions + j] + + b65 * rk_moles[(size_t)k + (size_t)j]); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k6 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.875 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k6 in rk_moles */ + k = 5 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + } + +/* + * Evaluate error + */ + error_max = 0.; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + l_error = fabs(dc1 * rk_moles[j] + + dc3 * rk_moles[2 * (size_t)n_reactions + (size_t)j] + + dc4 * rk_moles[3 * (size_t)n_reactions + (size_t)j] + + dc5 * rk_moles[4 * (size_t)n_reactions + (size_t)j] + + dc6 * rk_moles[5 * (size_t)n_reactions + (size_t)j]); + + /* tol is in moles/l */ + l_error /= kinetics_comp_ptr->Get_tol(); + if (l_error > error_max) + error_max = l_error; + } + +/* + * repeat with smaller step + */ +/* printf("timest %g ; error_max %g\n", h, error_max); */ + if (error_max > 1) + { + h_old = h; + if (step_ok == 0) + h = h * safety / error_max; + else + h = h * safety * pow(error_max, (LDBLE) -0.25); + l_bad = TRUE; + step_bad++; + } + else + { +/* + * OK, calculate result + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(c1 * rk_moles[j] + + c3 * rk_moles[2 * (size_t)n_reactions + (size_t)j] + + c4 * rk_moles[3 * (size_t)n_reactions + (size_t)j] + + c6 * rk_moles[5 * (size_t)n_reactions + (size_t)j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate && kinetics_ptr->Get_rk() < 6) + kinetics_ptr->Set_rk(1); + + saver(); + + step_ok++; + h_sum += h; + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } +/* + * and increase step size ... + */ + if (h_sum < kin_time) + { + if (error_max > 0.000577) + { + h = h * safety * pow(error_max, (LDBLE) -0.2e0); + } + else + { + h *= 4; + } + if (h > (kin_time - h_sum)) + h = (kin_time - h_sum); + } + } + { + char str[MAX_LENGTH]; + sprintf(str, "RK-steps: Bad%4d. OK%5d. Time %3d%%", step_bad, + step_ok, (int) (100 * h_sum / kin_time)); + status(0, str, true); + } + } + + EQUAL_RATE_OUT: + +/* + * Run one more time to get distribution of species + */ + if (state >= REACTION || nsaver != i) + { + set_and_run_wrapper(i, NOMIX, FALSE, nsaver, 0.); + run_reactions_iterations += iterations; + } +/* saver(); */ /* reset for printing */ + if (use_mix == DISP) + { + use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i)); + use.Set_mix_in(true); + use.Set_n_mix_user(i); + } + else if ((use_mix == STAG || use_mix == TRUE) && state == TRANSPORT) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + } + } +/* + * Restore solution i, if necessary + */ + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, save_old, i); + } + rk_moles.clear(); + + rate_sim_time = rate_sim_time_start + kin_time; + use.Set_kinetics_in(true); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_and_run_wrapper(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ + int j, converge, max_try; + int old_diag, old_itmax; + LDBLE old_tol, old_min_value, old_step, old_pe, old_pp_column_scale; + LDBLE small_pe_step, small_step; +#if (__GNUC__ && (__cplusplus >= 201103L)) || (_MSC_VER >= 1600) + std::unique_ptr pp_assemblage_save=NULL; + std::unique_ptr ss_assemblage_save=NULL; + std::unique_ptr kinetics_save=NULL; +#else + std::auto_ptr pp_assemblage_save(NULL); + std::auto_ptr ss_assemblage_save(NULL); + std::auto_ptr kinetics_save(NULL); +#endif + int restart = 0; + + small_pe_step = 5.; + small_step = 10.; + converge = FALSE; + + old_diag = diagonal_scale; + old_itmax = itmax; + old_tol = ineq_tol; + old_step = step_size; + old_pe = pe_step_size; + old_min_value = min_value; + old_pp_column_scale = pp_column_scale; + int old_equi_delay = equi_delay; + + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, use_mix, use_kinetics, i); + } + else if (state == ADVECTION) + { + set_advection(i, use_mix, use_kinetics, i); + } + else if (state == REACTION) + { + set_reaction(i, use_mix, use_kinetics); + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_save.reset(new cxxPPassemblage(*pp_assemblage_ptr)); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + cxxSSassemblage * ss_assemblage_ptr = use.Get_ss_assemblage_ptr(); + ss_assemblage_save.reset(new cxxSSassemblage(*ss_assemblage_ptr)); + } + if (use.Get_kinetics_ptr() != NULL) + { + kinetics_save.reset(new cxxKinetics(*use.Get_kinetics_ptr())); + } + + if (pitzer_model == TRUE || sit_model == TRUE) + { + diagonal_scale = TRUE; + always_full_pitzer = FALSE; + max_try = 14; + } + else + { + max_try = 14; + } + max_try = (max_tries < max_try) ? max_tries : max_try; + /*max_try = 1; */ + +restart: + for (j = 0; j < max_try; j++) + { + if (j == 1) + { + /*always_full_pitzer = TRUE;*/ + if (pe_step_size <= small_pe_step && step_size <= small_step) + continue; + itmax *= 2; + step_size = small_step; + pe_step_size = small_pe_step; + error_string = sformatf( + "Trying smaller step size, pe step size %g, %g ... \n", + (double) step_size, (double) pe_step_size); + warning_msg(error_string); + } + else if (j == 2) + { + itmax *= 2; + ineq_tol /= 10.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 3) + { + itmax *= 2; + ineq_tol *= 10.; + error_string = sformatf( "Trying increased tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 4) + { + always_full_pitzer = TRUE; + itmax *= 2; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + error_string = sformatf( "Trying diagonal scaling ...\n"); + warning_msg(error_string); + } + else if (j == 5) + { + itmax *= 2; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + ineq_tol /= 10.; + error_string = sformatf( + "Trying diagonal scaling and reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 6) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + pp_column_scale = 1e-10; + error_string = sformatf( + "Trying scaling pure_phase columns %g ...\n", + (double) pp_column_scale); + warning_msg(error_string); + } + else if (j == 7) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + pp_column_scale = 1e-10; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + error_string = sformatf( + "Trying scaling pure_phase columns and diagonal scale %g ...\n", + (double) pp_column_scale); + warning_msg(error_string); + } + else if (j == 8) + { + if (use.Get_pp_assemblage_ptr() == NULL) continue; + if (equi_delay > 0) + { + equi_delay = 0; + } + else + { + equi_delay = 1; + } + error_string = sformatf( "Trying delay removal of equilibrium phases %g ...\n", + (double) equi_delay); + warning_msg(error_string); + } + + else if (j == 9) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + min_value *= 10; + error_string = sformatf( "Trying increased scaling %g ...\n", + (double) min_value); + warning_msg(error_string); + } + else if (j == 10) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + aqueous_only = 5; + error_string = sformatf( + "Skipping optimize equations for first %d iterations ...\n", + aqueous_only); + warning_msg(error_string); + } + else if (j == 11) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + negative_concentrations = TRUE; + error_string = sformatf( + "Adding inequality to make concentrations greater than zero.\n"); + warning_msg(error_string); + } + else if (j == 12) + { + itmax *= 2; + ineq_tol /= 100.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 13) + { + itmax *= 2; + ineq_tol /= 1000.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 14 && use.Get_ss_assemblage_in()) + { + //cxxStorageBin error_bin; + //Use2cxxStorageBin(error_bin); + //std::ostringstream error_input; + //error_bin.dump_raw(error_input, 0); + //cxxStorageBin reread; + //std::istringstream is(error_input.str()); + //CParser cp(is); + //cp.set_echo_stream(CParser::EO_NONE); + //reread.read_raw(cp); + //cxxStorageBin2phreeqc(reread); + //error_string = sformatf("Trying restarting ...\n"); + //warning_msg(error_string); + //if (restart < 2) + //{ + // restart++; + // goto restart; + //} + } + if (j > 0) + { + if (pp_assemblage_save.get() != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save.get() != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + if (kinetics_save.get() != NULL) + { + Rxn_kinetics_map[kinetics_save->Get_n_user()] = *kinetics_save; + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, kinetics_save->Get_n_user())); + } + } + if (j == 14) + { + cxxStorageBin error_bin(this->Get_phrq_io()); + Use2cxxStorageBin(error_bin); + std::ostringstream error_input; + error_bin.dump_raw(error_input, 0); + cxxStorageBin reread(this->Get_phrq_io()); + std::istringstream is(error_input.str()); + CParser cp(is); + cp.set_echo_stream(CParser::EO_NONE); + cp.set_echo_file(CParser::EO_NONE); + reread.read_raw(cp); + cxxStorageBin2phreeqc(reread); + error_string = sformatf("Trying restarting ...\n"); + warning_msg(error_string); + + step_size = 1.0 + (small_step - 1.0)/((double) restart + 1.0); + pe_step_size = 1.0 + (small_pe_step - 1)/ ((double)restart + 1.0); + if (restart < 2) + { + restart++; + goto restart; + } + } + set_and_run_attempt = j; + + converge = + set_and_run(i, use_mix, use_kinetics, nsaver, step_fraction); + /* reset values */ + diagonal_scale = old_diag; + itmax = old_itmax; + ineq_tol = old_tol; + step_size = old_step; + pe_step_size = old_pe; + min_value = old_min_value; + pp_column_scale = old_pp_column_scale; + equi_delay = old_equi_delay; + aqueous_only = 0; + negative_concentrations = FALSE; + always_full_pitzer = FALSE; + if (converge == TRUE) + { + break; + } + else if (converge == MASS_BALANCE) + { + break; + } + warning_msg + ("Numerical method failed with this set of convergence parameters.\n"); + } + if (converge == FALSE && use.Get_kinetics_ptr() != NULL + && use.Get_kinetics_ptr()->Get_use_cvode()) + { + error_string = sformatf( + "Numerical method failed on all parameter combinations, retrying integration, cell/soln %d", this->solution_number()); + warning_msg(error_string); + converge = MASS_BALANCE; + } + if (converge == FALSE) + { +/* + * write to error.inp what failed to converge. + */ + std::ofstream error_input("error.inp"); + cxxStorageBin error_bin(this->Get_phrq_io()); + Use2cxxStorageBin(error_bin); + error_bin.dump_raw(error_input, 0); + error_input.close(); + + /* if (state == TRANSPORT && dump_modulus == 0) dump(); */ + check_residuals(); + pr.all = TRUE; + pr.gas_phase = use.Get_gas_phase_in(); + pr.pp_assemblage = use.Get_pp_assemblage_in(); + pr.ss_assemblage = use.Get_ss_assemblage_in(); + pr.surface = use.Get_surface_in(); + pr.exchange = use.Get_exchange_in(); + pr.totals = TRUE; + pr.species = TRUE; + pr.saturation_indices = TRUE; + pr.irrev = use.Get_reaction_in(); + pr.mix = use.Get_mix_in(); + pr.reaction = TRUE; + pr.use = TRUE; + sum_species(); + print_all(); + error_string = sformatf( + "Numerical method failed on all combinations of convergence parameters, cell/soln/mix %d", this->solution_number()); + error_msg(error_string, STOP); + } + numerical_fixed_volume = false; + if (converge == MASS_BALANCE) + { + return (MASS_BALANCE); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_and_run(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + * step_fraction--fraction of irreversible reaction to add + */ + int converge; + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, use_mix, use_kinetics, nsaver); + } + else if (state == ADVECTION) + { + set_advection(i, use_mix, use_kinetics, nsaver); + } + else if (state == REACTION) + { + set_reaction(i, use_mix, use_kinetics); + } + cell = i; +/* + * Take step + */ + if (state >= REACTION) + { + if (step(step_fraction) == MASS_BALANCE) + { + return (MASS_BALANCE); + } +/* + * Always use solution, exchange, and surface -1 + */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, -1)); + /* new */ + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, -1)); + } + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, -1)); + } + +/* + * Adjust the total pressure to the gas pressure + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* + * Fixed-pressure Gas phase and solution will react + * Change total pressure of current simulation to pressure + * of gas phase + */ + patm_x = gas_phase_ptr->Get_total_p(); + } + /* fixed volume gas phase is handled in calc_gas_pressures */ + + } + } + /* end new */ + if (use.Get_surface_ptr() != NULL) + { + dl_type_x = use.Get_surface_ptr()->Get_dl_type(); + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + converge = surface_model(); + } + else + { + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(FALSE); + converge = model(); + } + sum_species(); + viscosity(); + return (converge); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_transport(int i, int use_mix, int use_kinetics, int nsaver) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX, MIX_BS + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + */ + cell = i; + reaction_step = 1; +/* + * Find mixture or solution + */ + + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + if (use_mix == DISP) + { + use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i)); + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + } + else if ((use_mix == STAG && multi_Dflag != TRUE) || use_mix == MIX_BS) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.", + i, use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.", + i, use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + save.solution = TRUE; + save.n_solution_user = nsaver; + save.n_solution_user_end = nsaver; +/* + * Find pure phase assemblage + */ + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() != NULL) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + use.Set_pp_assemblage_in(false); + save.pp_assemblage = FALSE; + } +/* + * Find irreversible reaction + */ + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() != NULL) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(i); + } + else + { + use.Set_reaction_in(false); + } +/* + * Find exchange + */ + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + use.Set_exchange_in(false); + save.exchange = FALSE; + } + +/* + * Find surface + */ + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_in(true); + use.Set_n_surface_user(i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + use.Set_surface_in(false); + save.surface = FALSE; + dl_type_x = cxxSurface::NO_DL; + } +/* + * Find temperature; temp retardation is done in step + */ + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() != NULL) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(i); + } + else + { + use.Set_temperature_in(false); + } +/* + * Find pressure + */ + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() != NULL) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(i); + } + else + { + use.Set_pressure_in(false); + } +/* + * Find gas + */ + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() != NULL) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + use.Set_gas_phase_in(false); + save.gas_phase = FALSE; + } +/* + * Find ss_assemblage + */ + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() != NULL) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + use.Set_ss_assemblage_in(false); + save.ss_assemblage = FALSE; + } +/* + * Find kinetics + */ + use.Set_kinetics_ptr(NULL); + use.Set_kinetics_in(false); + save.kinetics = FALSE; + if (use_kinetics == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction(int i, int use_mix, int use_kinetics) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + */ +/* + * Find mixture or solution + */ + use.Set_mix_ptr(NULL); + use.Set_solution_ptr(NULL); + if (use_mix == TRUE && use.Get_mix_in() == TRUE) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() == NULL) + { + error_string = sformatf( "MIX %d not found.", i); + error_msg(error_string, STOP); + } + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() == NULL) + { + error_string = sformatf( "PP_ASSEMBLAGE %d not found.", i); + error_msg(error_string, STOP); + } + } + +/* + * Find irreversible reaction + */ + if (use.Get_reaction_in() == TRUE) + { + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() == NULL) + { + error_string = sformatf( "REACTION %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() == NULL) + { + error_string = sformatf( "EXCHANGE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find surface + */ + if (use.Get_surface_in() && use.Get_kinetics_in() && use.Get_kinetics_ptr() && !use.Get_kinetics_ptr()->Get_use_cvode() && reaction_step > 1) + { + // use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + // appt: we may come here with zero kinetic reaction, but surface may have to keep DONNAN_DL + } + else + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() == NULL) + { + error_string = sformatf( "SURFACE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find temperature; temp retardation is done in step + */ + if (use.Get_temperature_in() == TRUE) + { + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() == NULL) + { + error_string = sformatf( "TEMPERATURE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() == NULL) + { + error_string = sformatf( "PRESSURE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() == NULL) + { + error_string = sformatf( "GAS_PHASE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find ss_assemblage + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() == NULL) + { + error_string = sformatf( "Solid-solution Assemblage %d not found.", + i); + error_msg(error_string, STOP); + } + } +/* + * Find kinetics + */ + if (use_kinetics == TRUE && use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() == NULL) + { + error_string = sformatf( "KINETICS %d not found.", i); + error_msg(error_string, STOP); + } + } + else + { + use.Set_kinetics_ptr(NULL); + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Kinetics calculations + * Rates and moles of each reaction are calculated in calc_kinetic_reaction + * Total number of moles in reaction is stored in kinetics[i].totals + */ + //int increase_tol = 0; // appt + int converge, m_iter; + int pr_all_save; + int nsaver; + cxxKinetics *kinetics_ptr; + cxxPPassemblage *pp_assemblage_ptr; + cxxSSassemblage *ss_assemblage_ptr; + cxxUse use_save; + int save_old, n_reactions /*, nok, nbad */ ; + + /* CVODE definitions */ + realtype ropt[OPT_SIZE], reltol, t, tout, tout1, sum_t; + long int iopt[OPT_SIZE]; + int flag; +/* + * Set nsaver + */ + run_reactions_iterations = 0; + overall_iterations = 0; + kin_time_x = kin_time; + rate_kin_time = kin_time; + nsaver = i; + if (state == TRANSPORT || state == PHAST) + { + if (use_mix == DISP) + { + nsaver = -2; + } + else if (use_mix == STAG) + { + nsaver = -2 - i; + } + } + if (state == ADVECTION) + { + nsaver = -2; + } +/* + * Check that reaction exists for this cell .. + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kin_time <= 0 || + (kinetics_ptr && kinetics_ptr->Get_kinetics_comps().size() == 0) || + (state == REACTION && use.Get_kinetics_in() == FALSE) || + (state == TRANSPORT && kinetics_ptr == NULL) || + (state == PHAST && kinetics_ptr == NULL) || + (state == ADVECTION && kinetics_ptr == NULL)) + { + converge = + set_and_run_wrapper(i, use_mix, FALSE, nsaver, step_fraction); + if (converge == MASS_BALANCE) + { + error_string = sformatf("Negative concentration in solution %d. Stopping calculation.", cell_no); + error_msg(error_string, STOP); + } + run_reactions_iterations += iterations; + } + else + { +/* + * Save moles of kinetic reactants for printout... + */ + size_t count_comps = kinetics_ptr->Get_kinetics_comps().size(); + m_temp.resize(count_comps); + m_original.resize(count_comps); + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + m_original[j] = kinetics_comp_ptr->Get_m(); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } +/* +* Start the loop for timestepping ... + * Use either Runge-Kutta-Fehlberg, or final result extrapolation + */ + pr_all_save = pr.all; + pr.all = FALSE; +/* + * This condition makes output equal for incremental_reactions TRUE/FALSE... + * (if (incremental_reactions == FALSE || reaction_step == 1) + */ + store_get_equi_reactants(i, FALSE); + if (!kinetics_ptr->Get_use_cvode()) + { +/* in case dispersivity is not wanted.. + if (multi_Dflag) + rk_kinetics(i, kin_time, NOMIX, nsaver, step_fraction); + else + */ + rk_kinetics(i, kin_time, use_mix, nsaver, step_fraction); + } + else + { + save_old = -2 - (count_cells * (1 + stag_data.count_stag) + 2); + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + } + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } +/* + * Do mix first + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + cvode_n_user = i; + cvode_kinetics_ptr = (void *) kinetics_ptr; + cvode_n_reactions = n_reactions; + cvode_rate_sim_time_start = rate_sim_time_start; + cvode_rate_sim_time = rate_sim_time; + + if (multi_Dflag) + converge = set_and_run_wrapper(i, NOMIX, FALSE, i, step_fraction); + else + converge = set_and_run_wrapper(i, use_mix, FALSE, i, step_fraction); + if (converge == MASS_BALANCE) + { + error_string = sformatf("Negative concentration in solution %d. Stopping calculation.", cell_no); + error_msg(error_string, STOP); + } + saver(); + pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i); + ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i); + if (pp_assemblage_ptr != NULL) + { + cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (ss_assemblage_ptr != NULL) + { + cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + /* allocate space for CVODE */ + kinetics_machEnv = M_EnvInit_Serial(n_reactions); + kinetics_machEnv->phreeqc_ptr = this; + kinetics_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (kinetics_y == NULL) + malloc_error(); + cvode_last_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_last_good_y == NULL) + malloc_error(); + cvode_prev_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_prev_good_y == NULL) + malloc_error(); + kinetics_abstol = N_VNew(n_reactions, kinetics_machEnv); + if (kinetics_abstol == NULL) + malloc_error(); + for (int j = 0; j < n_reactions; j++) + { + Ith(cvode_last_good_y, j + 1) = 0.0; + Ith(cvode_prev_good_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = 0.0; + } +/* + * Set y to 0.0 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.0); + Ith(kinetics_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = kinetics_comp_ptr->Get_tol(); + /*Ith(abstol,j+1) = 1e-8; */ + /* m_temp[j] = kinetics_ptr->comps[j].m; */ + } + reltol = 0.0; + + /* Call CVodeMalloc to initialize CVODE: + + NEQ is the problem size = number of equations + f is the user's right hand side function in y'=f(t,y) + T0 is the initial time + y is the initial dependent variable vector + BDF specifies the Backward Differentiation Formula + NEWTON specifies a Newton iteration + SV specifies scalar relative and vector absolute tolerances + &reltol is a pointer to the scalar relative tolerance + abstol is the absolute tolerance vector + FALSE indicates there are no optional inputs in iopt and ropt + iopt is an array used to communicate optional integer input and output + ropt is an array used to communicate optional real input and output + + A pointer to CVODE problem memory is returned and stored in cvode_mem. */ + /* Don`t know what this does */ + /* + iopt[SLDET] = TRUE; + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, BDF, NEWTON, SV, &reltol, abstol, NULL, NULL, TRUE, iopt, ropt, machEnv); + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, ADAMS, FUNCTIONAL, SV, &reltol, abstol, NULL, NULL, FALSE, iopt, ropt, machEnv); + iopt[MXSTEP] is maximum number of steps that CVODE tries. + */ + //iopt[SLDET] = TRUE; // appt + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, SV, + &reltol, kinetics_abstol, this, NULL, TRUE, iopt, + ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + t = 0; + tout = kin_time; + /*ropt[HMAX] = tout/10.; */ + /*ropt[HMIN] = 1e-17; */ + use_save = use; + flag = CVode(kinetics_cvode_mem, tout, kinetics_y, &t, NORMAL); + rate_sim_time = rate_sim_time_start + t; + /* + printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n", + t, Ith(y,1), Ith(y,2), Ith(y,3)); + */ + m_iter = 0; + sum_t = 0; + RESTART: + while (flag != SUCCESS) + { + sum_t += cvode_last_good_time; + { + error_string = sformatf("CV_ODE: Time: %8.2e s. Delta t: %8.2e s. Calls: %d.", (double)(sum_t), (double) cvode_last_good_time, m_iter); + status(0, error_string, true); + } + //if (state != TRANSPORT) + //{ + // error_string = sformatf( + // "CVode incomplete at cvode_steps %d. Cell: %d. Time: %8.2e s. Cvode calls: %d, continuing...\n", + // (int)iopt[NST], cell_no, (double)sum_t, m_iter + 1); + // warning_msg(error_string); + //} +#ifdef DEBUG_KINETICS + if (m_iter > 5) + dump_kinetics_stderr(cell_no); +#endif + + //if (m_iter > 0.5 * kinetics_ptr->Get_bad_step_max() && + // (cvode_last_good_time < 1e-6 || cvode_last_good_time < 1e-6 * tout)) // appt + //{ + // if (increase_tol < 3) + // { + // increase_tol += 1; + // for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + // { + // cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + // LDBLE tr = kinetics_comp_ptr->Get_tol() * 10.0; + // kinetics_comp_ptr->Set_tol(tr); + // tr += 0; + // } + // } + //} + cvode_last_good_time = 0; + if (++m_iter >= kinetics_ptr->Get_bad_step_max()) + { + m_temp.clear(); + m_original.clear(); + error_string = sformatf( + "CVode is at maximum calls: %d. Cell: %d. Time: %8.2e s\nERROR: Please increase the maximum calls with -bad_step_max.", + m_iter, cell_no, (double)sum_t); + error_msg(error_string, STOP); + } + tout1 = tout - sum_t; + t = 0; + N_VScale(1.0, cvode_last_good_y, kinetics_y); + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } + CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */ + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, + SV, &reltol, kinetics_abstol, this, NULL, + TRUE, iopt, ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + flag = + CVode(kinetics_cvode_mem, tout1, kinetics_y, &t, NORMAL); + /* + error_string = sformatf( "CVode failed, flag=%d.\n", flag); + error_msg(error_string, STOP); + */ + } + /* + odeint(&ystart[-1], n_reactions, 0, kin_time, kinetics_ptr->comps[0].tol, kin_time/kinetics_ptr->step_divide, 0.0, &nok, &nbad, i, nsaver ); + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(Ith(kinetics_y, j + 1)); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_moles(m_original[j]); + kinetics_comp_ptr->Set_m(0.0); + } + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user())); + } + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 0) == + MASS_BALANCE) + { + /*error_msg("FAIL 2 after successful integration in CVode", CONTINUE); */ + warning_msg("FAIL 2 after successful integration in CVode"); + flag = -1; + goto RESTART; + } + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + } +/* + * Restore solution i, if necessary + */ + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, save_old, i); + } + free_cvode(); + use.Set_mix_in(use_save.Get_mix_in()); + use.Set_mix_ptr(use_save.Get_mix_ptr()); + + error_string = sformatf("CV_ODE: Final Delta t: %8.2e s. Calls: %d. ", (double)cvode_last_good_time, m_iter); + status(0, error_string, true); + + //status(0, NULL); + } + + rate_sim_time = rate_sim_time_start + kin_time; + store_get_equi_reactants(i, TRUE); + pr.all = pr_all_save; + + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(m_original[j] - kinetics_comp_ptr->Get_m()); +/* if (kinetics_ptr->comps[j].moles < 1.e-15) kinetics_ptr->comps[j].moles = 0.0; + */ } + m_temp.clear(); + m_original.clear(); + } + iterations = run_reactions_iterations; + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = NULL; + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_cvode(void) +/* ---------------------------------------------------------------------- */ +{ + if (kinetics_y != NULL) + N_VFree(kinetics_y); /* Free vector */ + kinetics_y = NULL; + if (cvode_last_good_y != NULL) + N_VFree(cvode_last_good_y); /* Free vector */ + cvode_last_good_y = NULL; + if (cvode_prev_good_y != NULL) + N_VFree(cvode_prev_good_y); /* Free vector */ + cvode_prev_good_y = NULL; + if (kinetics_abstol != NULL) + N_VFree(kinetics_abstol); /* Free vector */ + kinetics_abstol = NULL; + if (kinetics_cvode_mem != NULL) + CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */ + kinetics_cvode_mem = NULL; + if (kinetics_machEnv != NULL) + M_EnvFree_Serial(kinetics_machEnv); /* Free the machine environment memory */ + kinetics_machEnv = NULL; + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = NULL; + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_advection(int i, int use_mix, int use_kinetics, int nsaver) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + state == ADVECTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + */ + + cell = i; + reaction_step = 1; +/* + * Find mixture or solution + */ + + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use_mix == TRUE && use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + use.Set_n_solution_user(i); + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", + use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + save.solution = TRUE; + save.n_solution_user = nsaver; + save.n_solution_user_end = nsaver; +/* + * Find pure phase assemblage + */ + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() != NULL) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + use.Set_pp_assemblage_in(false); + save.pp_assemblage = FALSE; + } +/* + * Find irreversible reaction + */ + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() != NULL) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(i); + } + else + { + use.Set_reaction_in(false); + } +/* + * Find exchange + */ + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + use.Set_exchange_in(false); + save.exchange = FALSE; + } + +/* + * Find surface + */ + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_in(true); + use.Set_n_surface_user(i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + use.Set_surface_in(false); + save.surface = FALSE; + dl_type_x = cxxSurface::NO_DL; + } +/* + * Find temperature; temp retardation is done in step + */ + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() != NULL) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(i); + } + else + { + use.Set_temperature_in(false); + } +/* + * Find pressure + */ + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() != NULL) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(i); + } + else + { + use.Set_pressure_in(false); + } +/* + * Find gas + */ + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() != NULL) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + use.Set_gas_phase_in(false); + save.gas_phase = FALSE; + } +/* + * Find ss_assemblage + */ + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() != NULL) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + use.Set_ss_assemblage_in(false); + save.ss_assemblage = FALSE; + } +/* + * Find kinetics + */ + use.Set_kinetics_ptr(NULL); + use.Set_kinetics_in(false); + save.kinetics = FALSE; + if (use_kinetics == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_get_equi_reactants(int l, int kin_end) +/* ---------------------------------------------------------------------- */ +{ + int i, k; + + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, l)); + } + else + use.Set_pp_assemblage_ptr(NULL); + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, l)); + } + else + use.Set_gas_phase_ptr(NULL); + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, l)); + } + else + use.Set_ss_assemblage_ptr(NULL); + + if (kin_end == FALSE) + { + count_pp = count_ss = count_pg = 0; + if (use.Get_pp_assemblage_ptr() != NULL) + count_pp = (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size(); + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + count_pg = (int) gas_phase_ptr->Get_gas_comps().size(); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + count_ss += (int) ss_ptr->Get_ss_comps().size(); + } + } + k = count_pp + count_ss + count_pg; + + if (k == 0) + return (OK); + x0_moles.resize(k); + for (i = 0; i < k; i++) x0_moles[i] = 0.0; + k = -1; + if (pp_assemblage_ptr) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + x0_moles[++k] = it->second.Get_moles(); + } + } + + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr) + { + for (size_t l = 0; l < gas_phase_ptr->Get_gas_comps().size(); l++) + { + x0_moles[++k] += gas_phase_ptr->Get_gas_comps()[l].Get_moles(); + } + } + } + if (count_ss != 0) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + x0_moles[++k] = comp_ptr->Get_moles(); + } +/*!!!! also miscibility gap comps ?? + */ + } + } + } + else + { + k = -1; + if (pp_assemblage_ptr && count_pp > 0) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + it->second.Set_moles(x0_moles[++k]); + it->second.Set_delta(0.0); + } + } + + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr && count_pg) + { + std::vector temp_comps(gas_phase_ptr->Get_gas_comps()); + for (size_t l = 0; l < temp_comps.size(); l++) + { + temp_comps[l].Set_moles(x0_moles[++k]); + } + gas_phase_ptr->Set_gas_comps(temp_comps); + } + } + if (count_ss != 0) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_initial_moles(x0_moles[++k]); + } +/*!!!! also miscibility gap comps ?? + */ + } + } +/* + * This condition makes output equal for incremental_reactions TRUE/FALSE... + * if (incremental_reactions == FALSE || reaction_step == count_total_steps) + */ + x0_moles.clear(); + } + return (OK); +} +void Phreeqc:: +f(integertype N, realtype t, N_Vector y, N_Vector ydot, + void *f_data) +{ + int n_user; + //LDBLE step_fraction; + cxxKinetics *kinetics_ptr; + Phreeqc *pThis = (Phreeqc *) f_data; + + pThis->cvode_error = FALSE; + n_user = pThis->cvode_n_user; + kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr; + //step_fraction = pThis->cvode_step_fraction; + pThis->rate_sim_time = pThis->cvode_rate_sim_time; + + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + kinetics_ptr->comps[i].moles = y[i + 1]; + kinetics_ptr->comps[i].m = m_original[i] - y[i + 1]; + */ + kinetics_comp_ptr->Set_moles(Ith(y, i + 1)); + kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1)); + if (kinetics_comp_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + //if (kinetics_ptr->Get_use_cvode()) + //{ + // pThis->cvode_error = TRUE; + // return; + //} + kinetics_comp_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_ptr->Set_m(0.0); + } + } + pThis->calc_final_kinetic_reaction(kinetics_ptr); + /* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */ + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->use.Get_ss_assemblage_ptr() != NULL) + { + pThis->Rxn_ss_assemblage_map[pThis->cvode_ss_assemblage_save->Get_n_user()] = *pThis->cvode_ss_assemblage_save; + pThis->use.Set_ss_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_ss_assemblage_map, pThis->cvode_ss_assemblage_save->Get_n_user())); + } + + if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE) + { + pThis->run_reactions_iterations += pThis->iterations; + pThis->cvode_error = TRUE; + /* + error_msg("Mass balance error in f", CONTINUE); + */ + return; + } + if (pThis->cvode_test == TRUE) + { + return; + } + pThis->run_reactions_iterations += pThis->iterations; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + dydx[i + 1] = kinetics_ptr->comps[i].moles; + */ + Ith(ydot, i + 1) = kinetics_comp_ptr->Get_moles(); + } + return; +} + +/* +static void Jac(integertype N, DenseMat J, RhsFn f, void *f_data, realtype t, + N_Vector y, N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void *jac_data, long int *nfePtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); +*/ +void Phreeqc:: +Jac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype t, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, N_Vector vtemp2, + N_Vector vtemp3) +{ + int count_cvode_errors; + int n_reactions, n_user; + LDBLE del; + std::vector initial_rates; + cxxKinetics *kinetics_ptr; + LDBLE step_fraction; + + Phreeqc *pThis = (Phreeqc *) f_data; + + pThis->cvode_error = FALSE; + n_reactions = pThis->cvode_n_reactions; + n_user = pThis->cvode_n_user; + kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr; + step_fraction = pThis->cvode_step_fraction; + pThis->rate_sim_time = pThis->cvode_rate_sim_time; + + initial_rates.resize(n_reactions); + + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + kinetics_ptr->comps[i].moles = y[i + 1]; + kinetics_ptr->comps[i].m = m_original[i] - y[i + 1]; + */ + kinetics_comp_ptr->Set_moles(Ith(y, i + 1)); + kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1)); + if (kinetics_comp_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + kinetics_comp_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_ptr->Set_m(0.0); + } + } + pThis->calc_final_kinetic_reaction(kinetics_ptr); + /* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */ + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE) + { + pThis->run_reactions_iterations += pThis->iterations; + pThis->cvode_error = TRUE; + /* + error_msg("Mass balance error in jacobian", CONTINUE); + */ + initial_rates.clear(); + return; + } + pThis->run_reactions_iterations += pThis->iterations; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + initial_rates[i] = kinetics_comp_ptr->Get_moles(); + } + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_i_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* calculate reaction up to current time */ + del = 1e-12; + pThis->cvode_error = TRUE; + count_cvode_errors = 0; + while (pThis->cvode_error == TRUE) + { + del /= 10.; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_j_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + /* + kinetics_ptr->comps[j].moles = y[j + 1]; + kinetics_ptr->comps[j].m = m_original[j] - y[j + 1]; + */ + kinetics_comp_j_ptr->Set_moles(Ith(y, j + 1)); + kinetics_comp_j_ptr->Set_m(pThis->m_original[j] - Ith(y, j + 1)); + if (kinetics_comp_i_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + kinetics_comp_i_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_i_ptr->Set_m(0.0); + } + } + + /* Add small amount of ith reaction */ + kinetics_comp_i_ptr->Set_m(kinetics_comp_i_ptr->Get_m() - del); + if (kinetics_comp_i_ptr->Get_m() < 0) + { + kinetics_comp_i_ptr->Set_m(0); + } + kinetics_comp_i_ptr->Set_moles(kinetics_comp_i_ptr->Get_moles() + del); + pThis->calc_final_kinetic_reaction(kinetics_ptr); + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->set_and_run_wrapper + (n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) + { + count_cvode_errors++; + pThis->cvode_error = TRUE; + if (count_cvode_errors > 30) + { + initial_rates.clear(); + return; + } + pThis->run_reactions_iterations += pThis->iterations; + continue; + } + pThis->cvode_error = FALSE; + pThis->run_reactions_iterations += pThis->iterations; + /*kinetics_ptr->comps[i].moles -= del; */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + + /* calculate new rates for df/dy[i] */ + /* dfdx[i + 1] = 0.0; */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + IJth(J, j + 1, i + 1) = + (kinetics_comp_ptr->Get_moles() - initial_rates[j]) / del; + } + } + } + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0); + } + initial_rates.clear(); + return; +} + +void Phreeqc:: +cvode_init(void) +{ + cvode_kinetics_ptr = NULL; + cvode_test = 0; + cvode_error = 0; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save = NULL; + cvode_ss_assemblage_save = NULL; + return; +} +bool Phreeqc:: +cvode_update_reactants(int i, int nsaver, bool save_it) +{ + cxxKinetics *kinetics_ptr = use.Get_kinetics_ptr(); + int n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + + // Adds reaction defined by last_good_y + kinetics_comp_ptr->Set_moles(Ith(cvode_last_good_y, j + 1)); + // Reduces m + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + // Don't update until after calc_final_reaction + //m_original[j] = kinetics_comp_ptr->Get_m(); + //m_temp[j] = kinetics_comp_ptr->Get_m(); + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_moles(m_original[j]); + kinetics_comp_ptr->Set_m(0.0); + } + } + // calculates net reaction + calc_final_kinetic_reaction(kinetics_ptr); + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user())); + } + // runs previous solution plus net reaction + if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 1.0) == MASS_BALANCE) + { + error_msg("CVODE step was bad", STOP); + return false; + } + + // saves result to reactants defined by saver + if (save_it) + { + saver(); + + cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, nsaver); + cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, nsaver); + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + + for (int j = 0; j < n_reactions; j++) + { + Ith(cvode_last_good_y, j + 1) = 0.0; + Ith(cvode_prev_good_y, j + 1) = 0.0; + + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + m_original[j] = kinetics_comp_ptr->Get_m(); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } + } + return true; +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +limit_rates(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * determine rates and + * a list of elements and amounts in + * the reaction. + */ + + // check if any small concentrations with negative rates + if (!use_kinetics_limiter) + { + return false; + } + std::vector negative_rate; + cxxNameDouble::iterator it = kinetics_ptr->Get_totals().begin(); + for ( ; it != kinetics_ptr->Get_totals().end(); it++) + { + if (total(it->first.c_str()) < 1e-10 && it->second < -1e-20) + { + //if (total(it->first.c_str()) > fabs(it->second)) + // continue; + negative_rate.push_back(it->first); + } + } + if (negative_rate.size() == 0) return false; + + for (size_t j = 0; j < negative_rate.size(); j++) + { + std::string elt = negative_rate[j]; + LDBLE positive_rates = 0; + LDBLE negative_rates = 0; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt); + if (it != kinetics_comp_ptr->Get_moles_of_reaction().end()) + { + if (it->second >= 0.0) + { + positive_rates += it->second; + } + else + { + negative_rates += it->second; + } + } + } + + // factor to reduce precipitation to equal dissolution + LDBLE limiter_fraction = 1.0; + if (negative_rates < 0.0) + { + limiter_fraction = fabs(positive_rates / negative_rates); + //limiter_fraction = fabs((0.9*total(elt.c_str()) + positive_rates) / negative_rates); + } + else + { + assert(false); + } + + // Now limit precipitation + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt); + if (it != kinetics_comp_ptr->Get_moles_of_reaction().end()) + { + if (it->second < 0.0) + { + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * limiter_fraction); + } + } + } + } + + return true; +} diff --git a/phreeqcpp/mainsubs.cpp b/phreeqcpp/mainsubs.cpp new file mode 100644 index 00000000..0f1c714a --- /dev/null +++ b/phreeqcpp/mainsubs.cpp @@ -0,0 +1,2301 @@ +#include +#include +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "PBasic.h" +#include "Temperature.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +#if defined(WINDOWS) || defined(_WINDOWS) +#include +#endif + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +initialize(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialize global variables + */ + moles_per_kilogram_string = "Mol/kgw"; +/* + * Allocate space + */ + cell_data.resize((size_t)count_cells + 2); // initialized by global_structures.h + + count_inverse = 0; + space((void **) ((void *) &line), INIT, &max_line, sizeof(char)); + + space((void **) ((void *) &line_save), INIT, &max_line, sizeof(char)); + + // one stag_data in phreeqc.h, initialized in global_structures + + // user_print + user_print = new class rate; + user_print->name = string_hsave("User_print"); + user_print->commands.clear(); + user_print->linebase = NULL; + user_print->varbase = NULL; + user_print->loopbase = NULL; + /* + Initialize llnl aqueous model parameters + */ + a_llnl = b_llnl = 0.0; + // new PBasic + if (basic_interpreter != NULL) + { + basic_free(); + } + basic_interpreter = new PBasic(this, phrq_io); + // allocate one change_surf + change_surf = + (struct Change_Surf *) + PHRQ_malloc((size_t) (2 * sizeof(struct Change_Surf))); + if (change_surf == NULL) + malloc_error(); + change_surf[0].cell_no = -99; + change_surf[0].next = TRUE; + change_surf[1].cell_no = -99; + change_surf[1].next = FALSE; + /* + * define constant named log_k + */ + class logk* logk_ptr = logk_store("XconstantX", TRUE); + read_log_k_only("1.0", &logk_ptr->log_k[0]); + +#ifdef PHREEQCI_GUI + g_spread_sheet.heading = NULL; + g_spread_sheet.units = NULL; + g_spread_sheet.defaults.units = NULL; + g_spread_sheet.defaults.redox = NULL; + assert(g_spread_sheet.rows.empty()); + assert(g_spread_sheet.defaults.iso.empty()); +#endif + + // Initialize cvode + cvode_init(); + + // Allocate space for pitzer + pitzer_init(); + + // Allocate space for sit + sit_init(); + + use_kinetics_limiter = false; + + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_use(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Structure "use" has list of solution, ex, surf, pp_assemblage, + * gas_phase and solid solution to use in current calculations, + * also mix, irrev, and temp. + * This routine searches for the user numbers in each list + * (solution, ex, ...) and sets a pointer in structure use + */ + +/* + * Initial solution case + */ + use.Set_pp_assemblage_ptr(NULL); + use.Set_mix_ptr(NULL); + use.Set_reaction_ptr(NULL); + use.Set_exchange_ptr(NULL); + use.Set_kinetics_ptr(NULL); + use.Set_surface_ptr(NULL); + use.Set_temperature_ptr(NULL); + use.Set_pressure_ptr(NULL); + use.Set_gas_phase_ptr(NULL); + use.Set_ss_assemblage_ptr(NULL); + + if (state < REACTION) + { + return (OK); + } +/* + * Reaction case + */ + if (use.Get_pp_assemblage_in() == FALSE && + use.Get_reaction_in() == FALSE && + use.Get_mix_in() == FALSE && + use.Get_exchange_in() == FALSE && + use.Get_kinetics_in() == FALSE && + use.Get_surface_in() == FALSE && + use.Get_temperature_in() == FALSE && + use.Get_pressure_in() == FALSE && + use.Get_gas_phase_in() == FALSE && use.Get_ss_assemblage_in() == FALSE) + { + return (FALSE); + } + if (use.Get_solution_in() == FALSE && use.Get_mix_in() == FALSE) + return (FALSE); +/* + * Find solution + */ + if (use.Get_solution_in()) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user())); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", + use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + } +/* + * Find mixture + */ + if (use.Get_mix_in() == TRUE) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user())); + use.Set_n_mix_user_orig(use.Get_n_mix_user()); + if (use.Get_mix_ptr() == NULL) + { + error_string = sformatf( "Mix %d not found.", + use.Get_n_mix_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_mix_ptr(NULL); + } +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user())); + if (use.Get_pp_assemblage_ptr() == NULL) + { + error_string = sformatf( "Pure phase assemblage %d not found.", + use.Get_n_pp_assemblage_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_pp_assemblage_ptr(NULL); + } +/* + * Find irrev reaction + */ + if (use.Get_reaction_in() == TRUE) + { + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user())); + if (use.Get_reaction_ptr() == NULL) + { + error_string = sformatf( "Reaction %d not found.", + use.Get_n_reaction_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_reaction_ptr(NULL); + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user())); + if (use.Get_exchange_ptr() == NULL) + { + error_string = sformatf( "Exchange %d not found.", + use.Get_n_exchange_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_exchange_ptr(NULL); + } +/* + * Find kinetics + */ + if (use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + if (use.Get_kinetics_ptr() == NULL) + { + error_string = sformatf( "Kinetics %d not found.", + use.Get_n_kinetics_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_kinetics_ptr(NULL); + } +/* + * Find surface + */ + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user())); + if (use.Get_surface_ptr() == NULL) + { + error_string = sformatf( "Surface %d not found.", + use.Get_n_surface_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_surface_ptr(NULL); + } +/* + * Find temperature + */ + if (use.Get_temperature_in() == TRUE) + { + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user())); + if (use.Get_temperature_ptr() == NULL) + { + error_string = sformatf( "Temperature %d not found.", + use.Get_n_temperature_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_temperature_ptr(NULL); + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user())); + if (use.Get_pressure_ptr() == NULL) + { + error_string = sformatf( "Pressure %d not found.", use.Get_n_pressure_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_pressure_ptr(NULL); + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user())); + if (use.Get_gas_phase_ptr() == NULL) + { + error_string = sformatf( "Gas_phase %d not found.", + use.Get_n_gas_phase_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_gas_phase_ptr(NULL); + } +/* + * Find ss_assemblage + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user())); + if (use.Get_ss_assemblage_ptr() == NULL) + { + error_string = sformatf( "ss_assemblage %d not found.", + use.Get_n_ss_assemblage_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_ss_assemblage_ptr(NULL); + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_solutions(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of solutions, make initial solution calculations + * for any marked "new". + */ + int converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + + state = INITIAL_SOLUTION; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_solution_map.begin(); + //for ( ; it != Rxn_solution_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_solution.size(); nn++) + for (std::set::const_iterator nit = Rxn_new_solution.begin(); nit != Rxn_new_solution.end(); nit++) + { + std::map::iterator it = Rxn_solution_map.find(*nit); + if (it == Rxn_solution_map.end()) + { + assert(false); + } + cxxSolution &solution_ref = it->second; + initial_solution_isotopes = FALSE; + if (solution_ref.Get_new_def()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial solution calculations.", + TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Initial solution %d.\t%.350s", + solution_ref.Get_n_user(), solution_ref.Get_description().c_str()); + dup_print(token, FALSE); + } + use.Set_solution_ptr(&solution_ref); + LDBLE d0 = solution_ref.Get_density(); + //LDBLE d1 = 0; + bool diag = (diagonal_scale == TRUE) ? true : false; + int count_iterations = 0; + std::string input_units = solution_ref.Get_initial_data()->Get_units(); + cxxISolution *initial_data_ptr = solution_ref.Get_initial_data(); + density_iterations = 0; + for (;;) + { + prep(); + k_temp(solution_ref.Get_tc(), solution_ref.Get_patm()); + set(TRUE); + always_full_pitzer = FALSE; + + diagonal_scale = TRUE; + converge = model(); + if (converge == ERROR /*&& diagonal_scale == FALSE*/) + { + diagonal_scale = TRUE; + always_full_pitzer = TRUE; + set(TRUE); + converge = model(); + } + density_iterations++; + if (solution_ref.Get_initial_data()->Get_calc_density()) + { + solution_ref.Set_density(calc_dens()); + if (!equal(d0, solution_ref.Get_density(), 1e-8)) + { + initial_data_ptr->Set_units(input_units); + d0 = solution_ref.Get_density(); + if (count_iterations++ < 20) + { + diag = (diagonal_scale == TRUE) ? true : false; + continue; + } + else + { + error_msg(sformatf("%s %d.", "Density calculation failed for initial solution ", solution_ref.Get_n_user()), + STOP); + } + } + } + break; + } + diagonal_scale = (diag) ? TRUE : FALSE; + converge1 = check_residuals(); + sum_species(); + viscosity(); + add_isotopes(solution_ref); + punch_all(); + print_all(); + density_iterations = 0; + /* free_model_allocs(); */ +// remove pr_in + for (size_t i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + x[i]->phase->pr_in = false; + } + + if (converge == ERROR || converge1 == ERROR) + { + error_msg(sformatf("%s %d.", "Model failed to converge for initial solution ", solution_ref.Get_n_user()), + STOP); + } + n_user = solution_ref.Get_n_user(); + last = solution_ref.Get_n_user_end(); + /* copy isotope data */ + if (solution_ref.Get_isotopes().size() > 0) + { + isotopes_x = solution_ref.Get_isotopes(); + } + else + { + isotopes_x.clear(); + } + xsolution_save(n_user); + Utilities::Rxn_copies(Rxn_solution_map, n_user, last); + } + } + initial_solution_isotopes = FALSE; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_exchangers(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of exchangers, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int i, converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + + state = INITIAL_EXCHANGE; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + if (!it->second.Get_new_def()) + continue; + cxxExchange *exchange_ptr = &(it->second); + n_user = exchange_ptr->Get_n_user(); + last = exchange_ptr->Get_n_user_end(); + exchange_ptr->Set_n_user_end(n_user); + exchange_ptr->Set_new_def(false); + if (exchange_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial exchange" + "-composition calculations.", TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Exchange %d.\t%.350s", + exchange_ptr->Get_n_user(), exchange_ptr->Get_description().c_str()); + dup_print(token, FALSE); + } + use.Set_exchange_ptr(exchange_ptr); + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, exchange_ptr->Get_n_solution())); + if (use.Get_solution_ptr() == NULL) + { + error_msg + ("Solution not found for initial exchange calculation", + STOP); + } + + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(TRUE); + converge = model(); + converge1 = check_residuals(); + sum_species(); + viscosity(); + species_list_sort(); + print_exchange(); + xexchange_save(n_user); + punch_all(); + /* free_model_allocs(); */ + if (converge == ERROR || converge1 == ERROR) + { + error_msg + ("Model failed to converge for initial exchange calculation.", + STOP); + } + } + for (i = n_user + 1; i <= last; i++) + { + Utilities::Rxn_copy(Rxn_exchange_map, n_user, i); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_gas_phases(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of gas_phases, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + class phase *phase_ptr; + class rxn_token *rxn_ptr; + LDBLE lp; + bool PR = false; + + state = INITIAL_GAS_PHASE; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_gas_phase_map.begin(); + //for ( ; it != Rxn_gas_phase_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_gas_phase.size(); nn++) + //{ + //std::map::iterator it = Rxn_gas_phase_map.find(Rxn_new_gas_phase[nn]); + for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) + { + std::map::iterator it = Rxn_gas_phase_map.find(*nit); + if (it == Rxn_gas_phase_map.end()) + { + assert(false); + } + cxxGasPhase *gas_phase_ptr = &it->second; + if (!gas_phase_ptr->Get_new_def()) + continue; + n_user = gas_phase_ptr->Get_n_user(); + last = gas_phase_ptr->Get_n_user_end(); + gas_phase_ptr->Set_n_user_end(n_user); + gas_phase_ptr->Set_new_def(false); + if (gas_phase_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial gas_phase" + "-composition calculations.", TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Gas_Phase %d.\t%.350s", + gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_description().c_str()); + dup_print(token, FALSE); + } + + /* Try to obtain a solution pointer */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, gas_phase_ptr->Get_n_solution())); + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(TRUE); + converge = model(); + converge1 = check_residuals(); + if (converge == ERROR || converge1 == ERROR) + { + /* free_model_allocs(); */ + error_msg + ("Model failed to converge for initial gas phase calculation.", + STOP); + } + use.Set_gas_phase_ptr(gas_phase_ptr); + gas_phase_ptr->Set_total_p(0); + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp * gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(lp * LOG_10); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gc_ptr->Set_moles(phase_ptr->moles_x); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + if (phase_ptr->p_c || phase_ptr->t_c) + PR = true; + } + else + { + phase_ptr->moles_x = 0; + } + } + if (fabs(gas_phase_ptr->Get_total_p() - use.Get_solution_ptr()->Get_patm()) > 5) + { + sprintf(token, + "WARNING: While initializing gas phase composition by equilibrating:\n%s (%.2f atm) %s (%.2f atm).\n%s.", + " Gas phase pressure", + (double) gas_phase_ptr->Get_total_p(), + "is not equal to solution-pressure", + (double) use.Get_solution_ptr()->Get_patm(), + " Pressure effects on solubility may be incorrect"); + dup_print(token, FALSE); + } + + print_gas_phase(); + if (PR /*&& use.Get_gas_phase_ptr()->total_p > 1.0*/) + warning_msg("While initializing gas phase composition by equilibrating:\n" + " Found definitions of gas` critical temperature and pressure.\n" + " Going to use Peng-Robinson in subsequent calculations.\n"); + xgas_save(n_user); + punch_all(); + /* free_model_allocs(); */ + } + Utilities::Rxn_copies(Rxn_gas_phase_map, n_user, last); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_surfaces(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of surfaces, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int last, n_user, print1; + + state = INITIAL_SURFACE; + set_use(); + print1 = TRUE; + + //std::map::iterator it = Rxn_surface_map.begin(); + //for ( ; it != Rxn_surface_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + //std::map::iterator it = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator it = Rxn_surface_map.find(*nit); + if (it == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface * surface_ptr = &it->second; + if (!surface_ptr->Get_new_def()) + continue; + n_user = surface_ptr->Get_n_user(); + last = surface_ptr->Get_n_user_end(); + surface_ptr->Set_n_user_end(n_user); + if (surface_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print + ("Beginning of initial surface-composition calculations.", + TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + std::ostringstream msg; + msg << "Surface " << n_user << ".\t" << surface_ptr->Get_description().c_str(); + dup_print(msg.str().c_str(), FALSE); + } + use.Set_surface_ptr(surface_ptr); + dl_type_x = use.Get_surface_ptr()->Get_dl_type(); + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, surface_ptr->Get_n_solution())); + if (use.Get_solution_ptr() == NULL) + { + error_msg + ("Solution not found for initial surface calculation", + STOP); + } + set_and_run_wrapper(-1, FALSE, FALSE, -1, 0.0); + species_list_sort(); + print_surface(); + /*print_all(); */ + punch_all(); + xsurface_save(n_user); + /* free_model_allocs(); */ + } + Utilities::Rxn_copies(Rxn_surface_map, n_user, last); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make all reaction calculation which could include: + * equilibrium with a pure-phase assemblage, + * equilibrium with an exchanger, + * equilibrium with an surface, + * equilibrium with a gas phase, + * equilibrium with a solid solution assemblage, + * kinetics, + * change of temperature, + * mixture, + * or irreversible reaction. + */ + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + class save save_data; + LDBLE kin_time; + cxxKinetics *kinetics_ptr; + + state = REACTION; + /* last_model.force_prep = TRUE; */ + if (set_use() == FALSE) + return (OK); +/* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (use.Get_reaction_in() == TRUE && use.Get_reaction_ptr() != NULL) + { + cxxReaction *reaction_ptr = use.Get_reaction_ptr(); + if (reaction_ptr->Get_reaction_steps() > count_steps) + count_steps = reaction_ptr->Get_reaction_steps(); + } + if (use.Get_kinetics_in() == TRUE && use.Get_kinetics_ptr() != NULL) + { + if (use.Get_kinetics_ptr()->Get_reaction_steps() > count_steps) + count_steps = use.Get_kinetics_ptr()->Get_reaction_steps(); + } + if (use.Get_temperature_in() == TRUE && use.Get_temperature_ptr() != NULL) + { + int count = use.Get_temperature_ptr()->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (use.Get_pressure_in() == TRUE && use.Get_pressure_ptr() != NULL) + { + int count = use.Get_pressure_ptr()->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + count_total_steps = count_steps; +/* + * save data for saving solutions + */ + // memcpy(&save_data, &save, sizeof(class save)); + save_data = save; + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + overall_iterations = 0; + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); +/* + * Determine time step for kinetics + */ + kin_time = 0.0; + + if (use.Get_kinetics_in() == TRUE) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } +/* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + if (state != ADVECTION) + { + punch_all(); + print_all(); + } + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } +/* + * save end of reaction + */ + // memcpy(&save, &save_data, sizeof(class save)); + save = save_data; + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + + /* free_model_allocs(); */ +//// set pr_in to false for following steps... +// if (use.Get_pp_assemblage_in()) +// { +// for (int i = 0; i < count_unknowns; i++) +// { +// if (x[i]->type == PP) +// x[i]->phase->pr_in = false; +// } +// } +// if (use.Get_gas_phase_in()) +// { +// cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); +// for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) +// { +// cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); +// int k; +// class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); +// assert(phase_ptr); +// phase_ptr->pr_in = false; +// } +// } + /* last_model.force_prep = TRUE; */ + rate_sim_time = 0; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +saver(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save results of calcuations (data in variables with _x, + * in unknown structure x, in master, or s) into structure + * arrays. Structure "save" has info on whether to save + * data for each entity (solution, ex, surf, pp, gas, or s_s). + * Initial calculation may be saved into multiple "n_user" + * slots. + */ + int i, n; + char token[MAX_LENGTH]; + + if (save.solution == TRUE) + { + sprintf(token, "Solution after simulation %d.", simulation); + description_x = token; + n = save.n_solution_user; + xsolution_save(n); + for (i = save.n_solution_user + 1; i <= save.n_solution_user_end; i++) + { + Utilities::Rxn_copy(Rxn_solution_map, n, i); + } + } + if (save.pp_assemblage == TRUE) + { + n = save.n_pp_assemblage_user; + xpp_assemblage_save(n); + Utilities::Rxn_copies(Rxn_pp_assemblage_map, save.n_pp_assemblage_user, save.n_pp_assemblage_user_end); + } + if (save.exchange == TRUE) + { + n = save.n_exchange_user; + xexchange_save(n); + for (i = save.n_exchange_user + 1; i <= save.n_exchange_user_end; i++) + { + Utilities::Rxn_copy(Rxn_exchange_map, n, i); + } + } + if (save.surface == TRUE) + { + n = save.n_surface_user; + xsurface_save(n); + Utilities::Rxn_copies(Rxn_surface_map, n, save.n_surface_user_end); + } + if (save.gas_phase == TRUE) + { + n = save.n_gas_phase_user; + xgas_save(n); + for (i = save.n_gas_phase_user + 1; i <= save.n_gas_phase_user_end; + i++) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, n, i); + } + } + if (save.ss_assemblage == TRUE) + { + n = save.n_ss_assemblage_user; + xss_assemblage_save(n); + Utilities::Rxn_copies(Rxn_ss_assemblage_map, save.n_ss_assemblage_user, save.n_ss_assemblage_user_end); + } + if (save.kinetics == TRUE && use.Get_kinetics_in() == TRUE + /*&& use.Get_kinetics_ptr() != NULL */) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else if (use.Get_kinetics_in() != FALSE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } + if (use.Get_kinetics_ptr() != NULL) + { + n = use.Get_kinetics_ptr()->Get_n_user(); + for (i = save.n_kinetics_user; i <= save.n_kinetics_user_end; i++) + { + Utilities::Rxn_copy(Rxn_kinetics_map, n, i); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xexchange_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save exchanger assemblage into structure exchange with user + * number n_user. + */ + size_t i, j; + char token[MAX_LENGTH]; + + LDBLE charge; + if (use.Get_exchange_ptr() == NULL) + return (OK); + + cxxExchange temp_exchange = *use.Get_exchange_ptr(); +/* + * Store data for structure exchange + */ + temp_exchange.Set_n_user(n_user); + temp_exchange.Set_n_user_end(n_user); + temp_exchange.Set_new_def(false); + sprintf(token, "Exchange assemblage after simulation %d.", simulation); + temp_exchange.Set_description(token); + temp_exchange.Set_solution_equilibria(false); + temp_exchange.Set_n_solution(-999); + temp_exchange.Get_exchange_comps().clear(); + +/* + * Write exch_comp structure for each exchange component + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == EXCH) + { + const cxxExchComp *comp_ptr = use.Get_exchange_ptr()->Find_comp(x[i]->exch_comp); + if (!comp_ptr) + { + assert(false); + } + cxxExchComp xcomp = *comp_ptr; + xcomp.Set_la(x[i]->master[0]->s->la); +/* + * Save element concentrations on exchanger + */ + count_elts = 0; + paren_count = 0; + charge = 0.0; + for (j = 0; j < species_list.size(); j++) + { + if (species_list[j].master_s == x[i]->master[0]->s) + { + add_elt_list(species_list[j].s->next_elt, + species_list[j].s->moles); + charge += species_list[j].s->moles * species_list[j].s->z; + } + } +/* + * Keep exchanger related to phase even if none currently in solution + */ + if (xcomp.Get_phase_name().size() != 0 && count_elts == 0) + { + add_elt_list(x[i]->master[0]->s->next_elt, 1e-20); + } +/* + * Store list + */ + xcomp.Set_charge_balance(charge); + + xcomp.Set_totals(elt_list_NameDouble()); +/* debug + output_msg(sformatf( "Exchange charge_balance: %e\n", charge)); + */ + /* update unknown pointer */ + temp_exchange.Get_exchange_comps().push_back(xcomp); + } + } +/* + * Finish up + */ + Rxn_exchange_map[n_user] = temp_exchange; + + use.Set_exchange_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xgas_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ + /* + * Save gas composition into structure gas_phase with user + * number n_user. + */ + char token[MAX_LENGTH]; + + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + cxxGasPhase temp_gas_phase(*gas_phase_ptr); + /* + * Store in gas_phase + */ + temp_gas_phase.Set_n_user(n_user); + temp_gas_phase.Set_n_user_end(n_user); + sprintf(token, "Gas phase after simulation %d.", simulation); + temp_gas_phase.Set_description(token); + temp_gas_phase.Set_new_def(false); + temp_gas_phase.Set_solution_equilibria(false); + temp_gas_phase.Set_n_solution(-99); + /* + * Update amounts + */ + bool PR = false; + if (gas_phase_ptr->Get_v_m() >= 0.01) PR = true; + for (size_t i = 0; i < temp_gas_phase.Get_gas_comps().size(); i++) + { + cxxGasComp* gc_ptr = &(temp_gas_phase.Get_gas_comps()[i]); + int k; + class phase* phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + assert(phase_ptr); + if (PR) + { + gc_ptr->Set_moles(phase_ptr->moles_x); + gc_ptr->Set_p(phase_ptr->p_soln_x); + gc_ptr->Set_phi(phase_ptr->pr_phi); + gc_ptr->Set_f(phase_ptr->p_soln_x * phase_ptr->pr_phi); + } + else + { + gc_ptr->Set_moles(phase_ptr->moles_x); + gc_ptr->Set_p(phase_ptr->p_soln_x); + gc_ptr->Set_phi(1.0); + gc_ptr->Set_f(phase_ptr->p_soln_x); + } + } + Rxn_gas_phase_map[n_user] = temp_gas_phase; + + use.Set_gas_phase_ptr(NULL); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xss_assemblage_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save ss_assemblage composition into structure ss_assemblage with user + * number n_user. + */ + cxxSSassemblage temp_ss_assemblage(this->phrq_io); + + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); +/* + * Set ss_assemblage + */ + temp_ss_assemblage.Set_n_user(n_user); + temp_ss_assemblage.Set_n_user_end(n_user); + std::ostringstream msg; + msg << "Solid solution assemblage after simulation " << simulation; + temp_ss_assemblage.Set_description(msg.str().c_str()); + temp_ss_assemblage.Set_new_def(false); + temp_ss_assemblage.Set_SSs(use.Get_ss_assemblage_ptr()->Get_SSs()); + + std::vector ss_ptrs = temp_ss_assemblage.Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + /* set initial moles for quick setup */ + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_initial_moles(comp_ptr->Get_moles()); + } + } +/* + * Finish up + */ + Rxn_ss_assemblage_map[n_user] = temp_ss_assemblage; + + use.Set_ss_assemblage_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xpp_assemblage_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save pure_phase assemblage into instance of cxxPPassemblage with user + * number n_user. + */ + std::string token; + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + + cxxPPassemblage temp_pp_assemblage(*pp_assemblage_ptr); + + temp_pp_assemblage.Set_n_user(n_user); + temp_pp_assemblage.Set_n_user_end(n_user); + std::ostringstream desc; + desc << "Pure-phase assemblage after simulation " << simulation << "."; + temp_pp_assemblage.Set_description(desc.str().c_str()); + temp_pp_assemblage.Set_new_def(false); +/* + * Update amounts + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + cxxPPassemblageComp *comp = temp_pp_assemblage.Find(x[j]->pp_assemblage_comp_name); + comp->Set_moles(x[j]->moles); + comp->Set_delta(0.0); + } +/* + * Finish up + */ + + Rxn_pp_assemblage_map[n_user] = temp_pp_assemblage; + use.Set_pp_assemblage_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsolution_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save solution composition into structure solution with user number + * n_user. + * + * input: n_user is user solution number of target + */ + class master *master_i_ptr, *master_ptr; +/* + * Malloc space for solution data + */ + cxxSolution temp_solution; + temp_solution.Set_n_user_both(n_user); + temp_solution.Set_new_def(false); + temp_solution.Set_description(description_x); + temp_solution.Set_tc(tc_x); + temp_solution.Set_patm(patm_x); + temp_solution.Set_potV(potV_x); + temp_solution.Set_ph(ph_x); + temp_solution.Set_pe(solution_pe_x); + temp_solution.Set_mu(mu_x); + temp_solution.Set_ah2o(ah2o_x); + //temp_solution.Set_density(density_x); + temp_solution.Set_density(calc_dens()); + temp_solution.Set_total_h(total_h_x); + temp_solution.Set_total_o(total_o_x); + temp_solution.Set_cb(cb_x); /* cb_x does not include surface charge sfter sum_species */ + /* does include surface charge after step */ + temp_solution.Set_mass_water(mass_water_aq_x); + temp_solution.Set_total_alkalinity(total_alkalinity); + temp_solution.Set_soln_vol(this->calc_solution_volume()); +/* + * Copy pe data + */ + /* + * Add in minor isotopes if initial solution calculation + */ + if (initial_solution_isotopes == TRUE) + { + for (int i = 0; i < (int)master_isotope.size(); i++) + { + if (master_isotope[i]->moles > 0) + { + master_i_ptr = master_bsearch(master_isotope[i]->name); + master_ptr = master_isotope[i]->elt->master; + if (master_isotope[i]->minor_isotope == TRUE) + { + master_i_ptr->total = master_isotope[i]->moles; + if (master_ptr->total > 0) + { + master_i_ptr->s->la = + master_ptr->s->la + + log10(master_i_ptr->total / master_ptr->total); + } + else + { + master_i_ptr->s->la = master_ptr->s->la; + } + } + else if (master_isotope[i]->minor_isotope == FALSE + && master_ptr->s != s_hplus + && master_ptr->s != s_h2o) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr->s->secondary->total = + master_isotope[i]->moles; + } + else + { + master_ptr->s->primary->total = + master_isotope[i]->moles; + } + } + } + } + } +/* + * Copy totals data + */ + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->s->type == EX || + master[i]->s->type == SURF || master[i]->s->type == SURF_PSI) + continue; + if (master[i]->s == s_hplus) + continue; + if (master[i]->s == s_h2o) + continue; +/* + * Save list of log activities + */ + if (master[i]->in != FALSE) + { + temp_solution.Get_master_activity()[master[i]->elt->name] = master[i]->s->la; + } + if (master[i]->total <= MIN_TOTAL) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + continue; + } +/* + * Save list of concentrations + */ + temp_solution.Get_totals()[master[i]->elt->name] = master[i]->total; + } + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (int j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->lg != 0.0) + { + temp_solution.Get_species_gamma()[s_x[j]->name] = s_x[j]->lg; + } + } + } +/* + * Save isotope data + */ + temp_solution.Set_isotopes(isotopes_x); + std::map< std::string, cxxSolutionIsotope >::iterator it; + for (it = temp_solution.Get_isotopes().begin(); it != temp_solution.Get_isotopes().end(); it++) + { + class master *iso_master_ptr = master_bsearch(it->second.Get_elt_name().c_str()); + if (iso_master_ptr != NULL) + { + it->second.Set_total(iso_master_ptr->total); + if (iso_master_ptr == s_hplus->secondary) + { + it->second.Set_total(2 * mass_water_aq_x / gfw_water); + } + if (iso_master_ptr == s_h2o->secondary) + { + it->second.Set_total(mass_water_aq_x / gfw_water); + } + } + else + { + error_string = sformatf("Ignoring failed attempt to interpret %s as an isotope of element %s.", + it->second.Get_isotope_name().c_str(), it->second.Get_elt_name().c_str()); + warning_msg(error_string); + } + } + if (this->save_species) + { + // saves mol/L + temp_solution.Get_species_map().clear(); + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type <= H2O) + { + temp_solution.Get_species_map()[s_x[i]->number] = s_x[i]->moles / temp_solution.Get_soln_vol(); + } + } + // saves gamma + temp_solution.Get_log_gamma_map().clear(); + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type <= H2O) + { + temp_solution.Get_log_gamma_map()[s_x[i]->number] = s_x[i]->lg; + } + } + // saves molalities + temp_solution.Get_log_molalities_map().clear(); + for (int i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type <= H2O) + { + temp_solution.Get_log_molalities_map()[s_x[i]->number] = s_x[i]->lm; + } + } + } +/* + * Save solution + */ + Rxn_solution_map[n_user] = temp_solution; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsurface_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save surface data into structure surface with user + * number n_user. + */ + LDBLE charge; + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Store data for structure surface + */ + cxxSurface temp_surface = *use.Get_surface_ptr(); + temp_surface.Set_n_user(n_user); + temp_surface.Set_n_user_end(n_user); + temp_surface.Set_new_def(false); + temp_surface.Set_dl_type(dl_type_x); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_n_solution(-999); + + if (temp_surface.Get_type() == cxxSurface::NO_EDL) + { + temp_surface.Get_surface_charges().clear(); + } +/* + * Write surface_comp structure for each surf component into comps_ptr + */ + /* + * Initial entry of surface sites is random + * Charge balance numbering follows the initial entry + * Surface sites are then sorted alphabetically + * Now when we save, the site order differs from the charge order + * last_charge sets up logic to renumber charge balance equations. + */ + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = temp_surface.Find_comp(x[i]->surface_comp); + if (comp_ptr == NULL) + continue; // appt in transport with different mobile and stagnant surfaces + assert(comp_ptr); + comp_ptr->Set_la(x[i]->master[0]->s->la); + comp_ptr->Set_moles(0.); +/* + * Save element concentrations on surface + */ + count_elts = 0; + paren_count = 0; + charge = 0.0; + for (int j = 0; j < (int)species_list.size(); j++) + { + if (species_list[j].master_s == x[i]->master[0]->s) + { + add_elt_list(species_list[j].s->next_elt, + species_list[j].s->moles); + //add_elt_list_multi_surf(species_list[j].s->next_elt, + // species_list[j].s->moles, x[i]->master[0]->elt); + charge += species_list[j].s->moles * species_list[j].s->z; + } + } + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + comp_ptr->Set_charge_balance(charge); + } + else if (x[i]->type == SURFACE_CB && (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)) + { + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(x[i]->surface_charge); + if (charge_ptr == NULL) + continue; // appt in transport with different mobile and stagnant surfaces + assert(charge_ptr); + charge_ptr->Set_charge_balance(x[i]->f); + charge_ptr->Set_la_psi(x[i]->master[0]->s->la); +/* + * Store moles from diffuse_layer + */ + if (dl_type_x != cxxSurface::NO_DL) + { + sum_diffuse_layer(charge_ptr); + cxxNameDouble nd = elt_list_NameDouble(); + charge_ptr->Set_diffuse_layer_totals(nd); + } + } + else if (x[i]->type == SURFACE_CB + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(x[i]->surface_charge); + assert(charge_ptr); + if (dl_type_x != cxxSurface::NO_DL) + { + charge_ptr->Set_charge_balance( + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2() + + charge_ptr->Get_sigmaddl()) + * (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL); + } + else + { + charge_ptr->Set_charge_balance( + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2()) + * (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL); + } + charge_ptr->Set_la_psi(x[i]->master[0]->s->la); +/* + * Store moles from diffuse_layer + */ + if (dl_type_x != cxxSurface::NO_DL) + { + sum_diffuse_layer(charge_ptr); + cxxNameDouble nd = elt_list_NameDouble(); + charge_ptr->Set_diffuse_layer_totals(nd); + } + } + } + if (!(dl_type_x == cxxSurface::NO_DL)) + { + cxxSurface *surface_ptr = &temp_surface; + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge & charge_ref = surface_ptr->Get_surface_charges()[i]; + double mass_water_surface = charge_ref.Get_mass_water(); + for (int j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type > H2O) + continue; + double molality = under(s_x[j]->lm); + double moles_excess = mass_water_aq_x * molality * charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + double moles_surface = mass_water_surface * molality + moles_excess; + charge_ref.Get_dl_species_map()[s_x[j]->number] = moles_surface/mass_water_surface; + double g = charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + //double moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl + + // mass_water_surface / + // mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + + //LDBLE g = charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + if (s_x[j]->erm_ddl != 1) + { + LDBLE ratio_aq = mass_water_surface / mass_water_aq_x; + LDBLE g2 = g / ratio_aq + 1; + g = ratio_aq * (g2 * s_x[j]->erm_ddl - 1); + } + moles_excess = mass_water_aq_x * molality * g; + double c = (mass_water_surface * molality + moles_excess) / mass_water_surface; + charge_ref.Get_dl_species_map()[s_x[j]->number] = c; + } + //charge_ref.Get_dl_species_map()[s_h2o->number] = 0.0; + charge_ref.Get_dl_species_map()[s_h2o->number] = 1.0/gfw_water; + } + } + +/* + * Finish up + */ + Rxn_surface_map[n_user] = temp_surface; + use.Set_surface_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_use(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find mixture + */ + if (use.Get_mix_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_mix_map, use.Get_n_mix_user(), i); + } +/* + * Find solution + */ + if (use.Get_solution_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_solution_map, use.Get_n_solution_user(), i); + } +/* + * Always save solution to i, mixing or not + */ + save.solution = TRUE; + save.n_solution_user = i; + save.n_solution_user_end = i; +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user(), i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + save.pp_assemblage = FALSE; + } +/* + * Find irrev reaction + */ + if (use.Get_reaction_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_reaction_map, use.Get_n_reaction_user(), i); + save.reaction = TRUE; + save.n_reaction_user = i; + save.n_reaction_user_end = i; + } + else + { + save.reaction = FALSE; + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_exchange_map, use.Get_n_exchange_user(), i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + save.exchange = FALSE; + } +/* + * Find kinetics + */ + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, use.Get_n_kinetics_user(), i); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + else + { + save.kinetics = FALSE; + } +/* + * Find surface + */ + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_surface_map, use.Get_n_surface_user(), i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + save.surface = FALSE; + } +/* + * Find temperature + */ + if (use.Get_temperature_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_temperature_map, use.Get_n_temperature_user(), i); + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_pressure_map, use.Get_n_pressure_user(), i); + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, use.Get_n_gas_phase_user(), i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + save.gas_phase = FALSE; + } +/* + * Find solid solution + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user(), i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + save.ss_assemblage = FALSE; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step_save_exch(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save exchange composition + * + * input: n_user is user exchange number of target + */ + + if (use.Get_exchange_ptr() == NULL) + return (OK); + + cxxExchange *temp_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + assert(temp_ptr); + + // Set all totals to 0.0 + cxxExchange temp_exchange = *temp_ptr; + { + for (size_t i = 0; i < temp_exchange.Get_exchange_comps().size(); i++) + { + temp_exchange.Get_exchange_comps()[i].Get_totals().multiply(0.0); + } + } + + // Set exchange total in one component + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->s->type != EX) + continue; + std::string e(master[i]->elt->name); + for (size_t j = 0; j < temp_exchange.Get_exchange_comps().size(); j++) + { + cxxNameDouble *nd = &(temp_exchange.Get_exchange_comps()[j].Get_totals()); + cxxNameDouble::iterator nd_it = nd->find(e); + if (nd_it != nd->end()) + { + LDBLE coef; + if (master[i]->total <= MIN_TOTAL) + { + coef = MIN_TOTAL; + } + else + { + coef = master[i]->total; + } + nd->insert(nd_it->first.c_str(), coef); + break; + } + } + } + + Rxn_exchange_map[n_user] = temp_exchange; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step_save_surf(int n_user) +/* ---------------------------------------------------------------------- */ +{ + /* + * Save surface for intermediate calculation + * Amt of surface may have changed due to reaction or surface related + * to kinetic reactant. + * + * input: n_user is user solution number of target + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + Utilities::Rxn_copy(Rxn_surface_map, use.Get_surface_ptr()->Get_n_user(), n_user); + cxxSurface *surface_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user); + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->s->type != SURF) + continue; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp * comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + cxxNameDouble & totals = comp_ptr->Get_totals(); + if (totals.find(master[i]->elt->name) == totals.end()) + { + continue; + } + else + { + LDBLE coef = master[i]->total; + if (master[i]->total <= MIN_TOTAL) + { + coef = MIN_TOTAL; + } + totals[master[i]->elt->name] = coef; + break; + } + } + } + /* + * Update grams + */ + if ((surface_ptr->Get_type() == cxxSurface::DDL || surface_ptr->Get_type() == cxxSurface::CCM || surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + && surface_ptr->Get_related_rate() && use.Get_kinetics_ptr() != NULL) + { + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_rate_name().size() > 0) + { + cxxKinetics *kinetics_ptr = use.Get_kinetics_ptr(); + for (size_t m = 0; m < kinetics_ptr->Get_kinetics_comps().size(); m++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[m]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + surface_comp_ptr->Get_rate_name().c_str()) != 0) + continue; + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); + charge_ptr->Set_grams(kinetics_comp_ptr->Get_m()); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_entities(void) +/* ---------------------------------------------------------------------- */ +{ + int return_value; + return_value = OK; + for (size_t j = 0; j < copy_solution.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_solution_map, copy_solution.n_user[j]) != NULL) + { + for (size_t i = copy_solution.start[j]; i <= copy_solution.end[j]; i++) + { + if (i == copy_solution.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_solution_map, copy_solution.n_user[j], (int)i); + } + } + } + copier_clear(©_solution); + + for (size_t j = 0; j < copy_pp_assemblage.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_pp_assemblage_map, copy_pp_assemblage.n_user[j]) != NULL) + { + for (size_t i = copy_pp_assemblage.start[j]; i <= copy_pp_assemblage.end[j]; i++) + { + if (i == copy_pp_assemblage.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_pp_assemblage_map, copy_pp_assemblage.n_user[j], (int)i); + } + } + } + copier_clear(©_pp_assemblage); + + for (size_t j = 0; j < copy_reaction.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_reaction_map, copy_reaction.n_user[j]) != NULL) + { + for (size_t i = copy_reaction.start[j]; i <= copy_reaction.end[j]; i++) + { + if (i == copy_reaction.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_reaction_map, copy_reaction.n_user[j], (int)i); + } + } + } + copier_clear(©_reaction); + + for (size_t j = 0; j < copy_mix.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_mix_map, copy_mix.n_user[j]) != NULL) + { + for (size_t i = copy_mix.start[j]; i <= copy_mix.end[j]; i++) + { + if (i != copy_mix.n_user[j]) + { + Utilities::Rxn_copy(Rxn_mix_map, copy_mix.n_user[j], (int)i); + } + } + } + } + copier_clear(©_mix); + + for (size_t j = 0; j < copy_exchange.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_exchange_map, copy_exchange.n_user[j]) != NULL) + { + for (size_t i = copy_exchange.start[j]; i <= copy_exchange.end[j]; i++) + { + if (i == copy_exchange.n_user[j]) continue; + Utilities::Rxn_copy(Rxn_exchange_map, copy_exchange.n_user[j], (int)i); + } + } + } + copier_clear(©_exchange); + + for (size_t j = 0; j < copy_surface.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_surface_map, copy_surface.n_user[j]) != NULL) + { + for (size_t i = copy_surface.start[j]; i <= copy_surface.end[j]; i++) + { + if (i == copy_surface.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_surface_map, copy_surface.n_user[j], (int)i); + } + } + } + copier_clear(©_surface); + + for (size_t j = 0; j < copy_temperature.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_temperature_map, copy_temperature.n_user[j]) != NULL) + { + for (size_t i = copy_temperature.start[j]; i <= copy_temperature.end[j]; i++) + { + if (i != copy_temperature.n_user[j]) + { + Utilities::Rxn_copy(Rxn_temperature_map, copy_temperature.n_user[j], (int)i); + } + } + } + } + copier_clear(©_temperature); + + for (size_t j = 0; j < copy_pressure.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_pressure_map, copy_pressure.n_user[j]) != NULL) + { + for (size_t i = copy_pressure.start[j]; i <= copy_pressure.end[j]; i++) + { + if (i != copy_pressure.n_user[j]) + { + Utilities::Rxn_copy(Rxn_pressure_map, copy_pressure.n_user[j], (int)i); + } + } + } + } + copier_clear(©_pressure); + + for (size_t j = 0; j < copy_gas_phase.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_gas_phase_map, copy_gas_phase.n_user[j]) != NULL) + { + for (size_t i = copy_gas_phase.start[j]; i <= copy_gas_phase.end[j]; i++) + { + if (i == copy_gas_phase.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_gas_phase_map, copy_gas_phase.n_user[j], (int)i); + } + } + } + copier_clear(©_gas_phase); + + for (size_t j = 0; j < copy_kinetics.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_kinetics_map, copy_kinetics.n_user[j]) != NULL) + { + for (size_t i = copy_kinetics.start[j]; i <= copy_kinetics.end[j]; i++) + { + if (i == copy_kinetics.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_kinetics_map, copy_kinetics.n_user[j], (int)i); + } + } + } + copier_clear(©_kinetics); + + for (size_t j = 0; j < copy_ss_assemblage.n_user.size(); j++) + { + if (Utilities::Rxn_find(Rxn_ss_assemblage_map, copy_ss_assemblage.n_user[j]) != NULL) + { + for (size_t i = copy_ss_assemblage.start[j]; i <= copy_ss_assemblage.end[j]; i++) + { + if (i == copy_ss_assemblage.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_ss_assemblage_map, copy_ss_assemblage.n_user[j], (int)i); + } + } + } + copier_clear(©_ss_assemblage); + + new_copy = FALSE; + return return_value; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_database(void) +/* ---------------------------------------------------------------------- */ +{ + simulation = 0; + +/* + * Prepare error handling + */ + try + { + set_reading_database(TRUE); + dup_print("Reading data base.", TRUE); + read_input(); + tidy_model(); + status(0, NULL); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + set_reading_database(FALSE); + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_simulations(void) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; +#if defined(_MSC_VER) && (_MSC_VER < 1900) // removed in vs2015 + unsigned int old_exponent_format; + old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +/* + * Prepare error handling + */ + try + { +/* + * Read input data for simulation + */ + for (simulation = 1;; simulation++) + { +#ifdef TEST_COPY_OPERATOR + { + //int simulation_save = simulation; + Phreeqc phreeqc_new; + phreeqc_new = *this; + PHRQ_io *temp_io = this->phrq_io; + std::vector so_ostreams; + { + std::map::iterator so_it = this->SelectedOutput_map.begin(); + for (; so_it != this->SelectedOutput_map.end(); so_it++) + { + so_ostreams.push_back(so_it->second.Get_punch_ostream()); + so_it->second.Set_punch_ostream(NULL); + } + } + this->clean_up(); + this->init(); + this->initialize(); + this->phrq_io = temp_io; + this->InternalCopy(&phreeqc_new); + { + size_t i = 0; + std::map::iterator so_it = this->SelectedOutput_map.begin(); + for (; so_it != this->SelectedOutput_map.end(); so_it++) + { + so_it->second.Set_punch_ostream(so_ostreams[i++]); + } + } + //this->simulation = simulation_save; + //delete phreeqc_new.Get_phrq_io(); + } +#endif +#if defined PHREEQ98 + AddSeries = !connect_simulations; +#endif + +#if defined PHREEQCI_GUI + sprintf(token, "\nSimulation %d\n", simulation); + screen_msg(token); +#endif + sprintf(token, "Reading input data for simulation %d.", simulation); + + dup_print(token, TRUE); + if (read_input() == EOF) + break; + + if (title_x.size() > 0) + { + sprintf(token, "TITLE"); + dup_print(token, TRUE); + if (pr.headings == TRUE) + { + output_msg(sformatf("%s\n\n", title_x.c_str())); + } + } + tidy_model(); +/* + * Calculate distribution of species for initial solutions + */ + if (new_solution) + { + initial_solutions(TRUE); + } + +/* + * Calculate distribution for exchangers + */ + if (new_exchange) + initial_exchangers(TRUE); +/* + * Calculate distribution for surfaces + */ + if (new_surface) + initial_surfaces(TRUE); +/* + * Calculate initial gas composition + */ + if (new_gas_phase) + initial_gas_phases(TRUE); +/* + * Calculate reactions + */ + reactions(); +/* + * Calculate inverse models + */ + inverse_models(); +/* + * Calculate advection + */ + if (use.Get_advect_in()) + { + dup_print("Beginning of advection calculations.", TRUE); + advection(); + } +/* + * Calculate transport + */ + if (use.Get_trans_in()) + { + dup_print("Beginning of transport calculations.", TRUE); + transport(); + } +/* + * run + */ + run_as_cells(); +/* + * Calculate mixes + */ + do_mixes(); + +/* + * Copy + */ + if (new_copy) copy_entities(); +/* + * dump + */ + dump_entities(); +/* + * delete + */ + delete_entities(); +/* + * End of simulation + */ + dup_print("End of simulation.", TRUE); + output_flush(); + error_flush(); + } + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +do_initialize(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prepare error handling + */ + try { + + state = INITIALIZE; + + initialize(); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +do_status(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prepare error handling + */ + try { + + if (pr.status == TRUE) + { + status(0, "\nDone."); + screen_msg("\n"); + } + //pr.headings = TRUE; // set in class_main; not set for IPhreeqc +#ifndef TESTING + LDBLE ext = (double)clock() / CLOCKS_PER_SEC; + dup_print(sformatf("End of Run after %g Seconds.", ext), TRUE); + screen_msg(sformatf("\nEnd of Run after %g Seconds.\n", ext)); +#endif +// appt this gives output when the charts are active... + phrq_io->output_flush(); + phrq_io->error_flush(); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} +void Phreeqc:: +save_init(int i) +{ + save.solution = i; + save.n_solution_user = i; + save.n_solution_user_end = i; + save.mix = i; + save.n_mix_user = i; + save.n_mix_user_end = i; + save.reaction = i; + save.n_reaction_user = i; + save.n_reaction_user_end = i; + save.pp_assemblage = i; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + save.exchange = i; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + save.kinetics = i; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + save.surface = i; + save.n_surface_user = i; + save.n_surface_user_end = i; + save.gas_phase = i; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + save.ss_assemblage = i; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; +} +void +Phreeqc::do_mixes(void) +{ + bool surf, exch, kin, min; + surf = (Rxn_surface_mix_map.size() > 0); + exch = (Rxn_exchange_mix_map.size() > 0); + kin = (Rxn_kinetics_mix_map.size() > 0); + min = (Rxn_pp_assemblage_mix_map.size() > 0); + Utilities::Rxn_mix(Rxn_solution_mix_map, Rxn_solution_map, this); + Utilities::Rxn_mix(Rxn_exchange_mix_map, Rxn_exchange_map, this); + Utilities::Rxn_mix(Rxn_gas_phase_mix_map, Rxn_gas_phase_map, this); + Utilities::Rxn_mix(Rxn_kinetics_mix_map, Rxn_kinetics_map, this); + Utilities::Rxn_mix(Rxn_pp_assemblage_mix_map, Rxn_pp_assemblage_map, this); + Utilities::Rxn_mix(Rxn_ss_assemblage_mix_map, Rxn_ss_assemblage_map, this); + Utilities::Rxn_mix(Rxn_surface_mix_map, Rxn_surface_map, this); + if (exch || kin) update_kin_exchange(); + if (exch || min) update_min_exchange(); + if (surf || min) update_min_surface(); + if (surf || kin) update_kin_surface(); +} diff --git a/phreeqcpp/model.cpp b/phreeqcpp/model.cpp new file mode 100644 index 00000000..1d31226a --- /dev/null +++ b/phreeqcpp/model.cpp @@ -0,0 +1,5846 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "Solution.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + + set_inert_moles(); +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + if (pitzer_model == TRUE && sit_model == TRUE) + { + input_error++; + error_msg("Cannot use PITZER and SIT data blocks in same run (database + input file).", STOP); + } + if ((pitzer_model == TRUE || sit_model == TRUE) && llnl_temp.size() > 0) + { + input_error++; + error_msg("Cannot use LLNL_AQUEOUS_MODEL_PARAMETERS with PITZER or SIT data blocks in same run (database + input file).", STOP); + } + if (pitzer_model == TRUE) + { + + l_kode = model_pz(); + unset_inert_moles(); + return l_kode; + } + if (sit_model == TRUE) + { + + l_kode = model_sit(); + unset_inert_moles(); + return l_kode; + } + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + overall_iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } +/* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } +/* + * Calculate jacobian + */ + if (state >= REACTION && numerical_deriv) + { + //jacobian_sums(); + numerical_jacobian(); + } + else /* hmm */ + { + jacobian_sums(); + numerical_jacobian(); + + } +/* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf("Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + gammas(mu_x); + if (molalities(FALSE) == ERROR) + { + revise_guesses(); +/* adjust_step_size(); */ + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + + if (switch_bases() == TRUE) + { + count_basis_change++; + reprep(); + gammas(mu_x); + molalities(TRUE); + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + revise_guesses(); + mb_sums(); + mb_gases(); + mb_ss(); + } +/* debug + species_list_sort(); + sum_species(); + print_species(); + print_exchange(); + print_surface(); + */ + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n\n", iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + unset_inert_moles(); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_residuals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks for convergence of all equations, prints any nonconvergence + * Sets variable remove_unstable_phases if a phase is present, + * but undersaturated (i.e. aragonite in calcite-saturated solution). + */ + int i, return_value; + LDBLE epsilon; + epsilon = convergence_tolerance; + + return_value = OK; + if (stop_program == TRUE) + { + warning_msg + ("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:"); + /*error_msg("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:", CONTINUE); */ + } + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == ALK) + { + if (fabs(residual[i]) >= epsilon * x[i]->moles && fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL) + && x[i]->moles > MIN_TOTAL /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s has not converged. Total: %e\tCalculated: " + "%e\tResidual: %e\n", x[i]->description, + (double) x[i]->moles, (double) x[i]->f, + (double) residual[i]); + error_msg(error_string, CONTINUE); + if (x[i]->type == ALK) + { + error_msg("Is non-carbonate alkalinity " + "greater than total alkalinity?\n", CONTINUE); + } + return_value = ERROR; + } + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + if (fabs(residual[i]) >= epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s solution phase boundary has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == CB) + { + if (fabs(residual[i]) >= + epsilon * mu_x * + mass_water_aq_x /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Charge balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == MU /*&& pitzer_model == FALSE && sit_model == FALSE*/) + { + if (fabs(residual[i]) >= + epsilon * mu_x * + mass_water_aq_x /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Ionic strength has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == AH2O && pitzer_model == FALSE && sit_model == FALSE) + { + if (fabs(residual[i]) >= epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Activity of water has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if ((x[i]->type == MH + && (pitzer_model == FALSE || pitzer_pe == TRUE))) + { +#define COMBINE + /*#define COMBINE_CHARGE */ +#ifdef COMBINE +#ifndef COMBINE_CHARGE + if (fabs(residual[i]) > + epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles)) +#else + if (fabs(residual[i]) > + epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles + + charge_balance_unknown->moles)) +#endif +#else + if (fabs(residual[i]) >= + epsilon * x[i]->moles /* || stop_program == TRUE */ ) +#endif + { + error_string = sformatf( + "%20s Mass of hydrogen has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + continue; + if (fabs(residual[i]) >= + 0.01 * epsilon * x[i]->moles /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Mass of oxygen has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == PP) + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_add_formula().size() == 0) + { + if (x[i]->dissolve_only == TRUE) + { + if ((residual[i] > epsilon && x[i]->moles > 0.0) + || + ((residual[i] < -epsilon + && (comp_ptr->Get_initial_moles() - x[i]->moles) > + 0))) + { + log_msg(sformatf( + "%20s Dissolve_only pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + } + } + else + { + if ((residual[i] >= epsilon * 100 + && x[i]->moles > 0.0) /* || stop_program == TRUE */ ) + { + remove_unstable_phases = TRUE; + log_msg(sformatf( + "%20s Pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + } + else if (residual[i] <= -epsilon) + { + error_string = sformatf( + "%20s Pure phase has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + } + else + { + if ((fabs(residual[i]) >= epsilon + && x[i]->moles > 0.0) /* || stop_program == TRUE */ ) + { + log_msg(sformatf( + "%s, Pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + error_string = sformatf( + "%s, Pure phase with add formula has not converged.\n\t SI may be a local minimum." + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + warning_msg(error_string); + } + } + } + else if (x[i]->type == EXCH) + { + if ( /* stop_program == TRUE || */ + (x[i]->moles <= MIN_RELATED_SURFACE + && fabs(residual[i]) > epsilon) + || (x[i]->moles > MIN_RELATED_SURFACE + && (fabs(residual[i]) > epsilon * x[i]->moles))) + { + error_string = sformatf( + "%20s Exchanger mass balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == SURFACE) + { + if (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles) continue; + if ( /* stop_program == TRUE || */ + (x[i]->moles <= MIN_RELATED_SURFACE + && fabs(residual[i]) > epsilon) + || (x[i]->moles > MIN_RELATED_SURFACE + && (fabs(residual[i]) > epsilon * x[i]->moles))) + { + error_string = sformatf( + "%20s Surface mass balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if ((charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > + epsilon) /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Surface charge/potential has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == GAS_MOLES) + { + if (gas_in == FALSE) + continue; + if (residual[i] >= epsilon + || residual[i] <= -epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Total moles in gas phase has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == PITZER_GAMMA) + { + if (fabs(residual[i]) > epsilon) + { + error_string = sformatf( + "%20s log gamma not converged.\tResidual: %e\n", + x[i]->description, (double) residual[i]); + } + } + else if (x[i]->type == SS_MOLES) + { + if (x[i]->ss_in == FALSE) + continue; + //if (x[i]->moles <= 1e2*MIN_TOTAL) + // continue; + if (residual[i] >= epsilon + || residual[i] <= -epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Total moles in solid solution has not converged. " + "\tResidual: %e %e\n", x[i]->description, + (double) residual[i], x[i]->moles); + error_msg(error_string, CONTINUE); + } + } + } + if (remove_unstable_phases == TRUE) + { + log_msg(sformatf( "%20sRemoving unstable phases, iteration %d.", + " ", iterations)); + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas(LDBLE mu) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates gammas and [moles * d(ln gamma)/d mu] for all aqueous + * species. + */ + int i, j; + int ifirst, ilast; + LDBLE f, log_g_co2, dln_g_co2, c2_llnl; + + LDBLE c1, c2, a, b; + LDBLE muhalf, equiv; + /* Initialize */ + if (mu <= 0) mu = 1e-10; + if (pitzer_model == TRUE) + return gammas_pz(true); + if (sit_model == TRUE) + return gammas_sit(); + a_llnl = b_llnl = bdot_llnl = log_g_co2 = dln_g_co2 = c2_llnl = 0; +/* + * compute temperature dependence of a and b for debye-huckel + */ + // a and b are calc'd in calc_dielectrics(tc_x, patm_x); + k_temp(tc_x, patm_x); + a = DH_A; + b = DH_B; + + /* + * LLNL temperature dependence + */ + if (llnl_temp.size() > 0) + { + ifirst = 0; + ilast = (int)llnl_temp.size(); + if (tc_x < llnl_temp[0] || tc_x > llnl_temp[llnl_temp.size() - 1]) + { + error_msg + ("Temperature out of range of LLNL_AQUEOUS_MODEL parameters", + STOP); + } + for (i = 0; i < (int)llnl_temp.size(); i++) + { + if (tc_x >= llnl_temp[i]) + ifirst = i; + if (tc_x <= llnl_temp[i]) + { + ilast = i; + break; + } + } + if (ilast == ifirst) + { + f = 1; + } + else + { + f = (tc_x - llnl_temp[ifirst]) / (llnl_temp[ilast] - + llnl_temp[ifirst]); + } + a_llnl = (1 - f) * llnl_adh[ifirst] + f * llnl_adh[ilast]; + b_llnl = (1 - f) * llnl_bdh[ifirst] + f * llnl_bdh[ilast]; + bdot_llnl = (1 - f) * llnl_bdot[ifirst] + f * llnl_bdot[ilast]; + /* + * CO2 activity coefficient + */ + log_g_co2 = + (llnl_co2_coefs[0] + llnl_co2_coefs[1] * tk_x + + llnl_co2_coefs[2] / tk_x) * mu - (llnl_co2_coefs[3] + + llnl_co2_coefs[4] * tk_x) * + (mu / (mu + 1)); + log_g_co2 /= LOG_10; + dln_g_co2 = + (llnl_co2_coefs[0] + llnl_co2_coefs[1] * tk_x + + llnl_co2_coefs[2] / tk_x) - (llnl_co2_coefs[3] + + llnl_co2_coefs[4] * tk_x) * (1 / + ((mu + + 1) * + (mu + + 1))); + } + +/* + * constants for equations + */ + muhalf = sqrt(mu); + c1 = (-a) * LOG_10 * (1.0 / + (2 * muhalf * (muhalf + 1.0) * (muhalf + 1.0)) - + 0.3); + c2 = -a / (2 * muhalf); + if (llnl_temp.size() > 0) + { + c2_llnl = -a_llnl / (2 * muhalf); + } + +/* + * Calculate activity coefficients + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + s_x[i]->lg = s_x[i]->dhb * mu; + s_x[i]->dg = s_x[i]->dhb * LOG_10 * s_x[i]->moles; + break; + case 1: /* Davies */ + s_x[i]->lg = -s_x[i]->z * s_x[i]->z * a * + (muhalf / (1.0 + muhalf) - 0.3 * mu); + s_x[i]->dg = c1 * s_x[i]->z * s_x[i]->z * s_x[i]->moles; + break; + case 2: /* Extended D-H, WATEQ D-H */ + s_x[i]->lg = -a * muhalf * s_x[i]->z * s_x[i]->z / + (1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu; + s_x[i]->dg = (c2 * s_x[i]->z * s_x[i]->z / + ((1.0 + s_x[i]->dha * b * muhalf) * (1.0 + + s_x[i]->dha * + b * muhalf)) + + s_x[i]->dhb) * LOG_10 * s_x[i]->moles; +/* if (mu_x < 1e-6) s_x[i]->dg = 0.0; */ + break; + case 3: /* Always 1.0 */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + break; + case 4: /* Exchange */ +/* + * Find CEC + * z contains charge of cation for exchange species, alk contains cec + * correct activity for Gapon-type exchange eqns: Ca0.5X uses (gamma_Ca)^0.5 + */ + if (calculating_deriv) + continue; + { + LDBLE coef = 0, z = 0; + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + { + s_x[i]->alk = + s_x[i]->rxn_x.token[j].s->primary->unknown->moles; + //break; + } + else if (s_x[i]->rxn_x.token[j].s->type <= HPLUS) + { + coef = s_x[i]->rxn_x.token[j].coef; + z = s_x[i]->rxn_x.token[j].s->z; + } + } + if (!use.Get_exchange_ptr()->Get_pitzer_exchange_gammas()) + { + if (s_x[i]->primary != NULL) + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + else + { + if (s_x[i]->alk <= 0) + s_x[i]->lg = 0.0; + else + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + } + else if (s_x[i]->exch_gflag == 1 && s_x[i]->alk > 0) + { + /* Davies */ + s_x[i]->lg = -coef * z * z * a * + (muhalf / (1.0 + muhalf) - 0.3 * mu) + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = + c1 * coef * z * z * s_x[i]->moles; + } + else if (s_x[i]->exch_gflag == 2 && s_x[i]->alk > 0) + { + /* Extended D-H, WATEQ D-H */ + s_x[i]->lg = coef * (-a * muhalf * z * z / + (1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu) + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = coef * (c2 * z * z / + ((1.0 + s_x[i]->dha * b * muhalf) * (1.0 + s_x[i]->dha * b * muhalf)) + + s_x[i]->dhb) * LOG_10 * s_x[i]->moles; + } + else if (s_x[i]->exch_gflag == 7 && s_x[i]->alk > 0) + { + if (llnl_temp.size() > 0) + { + s_x[i]->lg = + coef * (-a_llnl * muhalf * z * z / + (1.0 + s_x[i]->dha * b_llnl * muhalf) + + bdot_llnl * mu) + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = + coef * (c2_llnl * z * z / + ((1.0 + s_x[i]->dha * b_llnl * muhalf) * (1.0 + s_x[i]->dha * b_llnl * muhalf)) + + bdot_llnl) * LOG_10 * s_x[i]->moles; + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", + STOP); + } + } + else + { + /* + * Master species is a dummy variable with meaningless activity and mass + */ + if (s_x[i]->primary != NULL) + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + else + { + if (s_x[i]->alk <= 0) + s_x[i]->lg = 0.0; + else + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + } + if (s_x[i]->a_f && s_x[i]->primary == NULL && s_x[i]->moles) + gammas_a_f(i); // appt + } + break; + case 5: /* Always 1.0 */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x.token[j].s->primary->unknown->moles; + break; + } + } + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + /* mole fraction */ + equiv = 1.0; + } + else + { + equiv = s_x[i]->equiv; + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + if (llnl_temp.size() > 0) + { + if (s_x[i]->z == 0) + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = -a_llnl * muhalf * s_x[i]->z * s_x[i]->z / + (1.0 + s_x[i]->dha * b_llnl * muhalf) + + bdot_llnl * mu; + s_x[i]->dg = + (c2_llnl * s_x[i]->z * s_x[i]->z / + ((1.0 + s_x[i]->dha * b_llnl * muhalf) * (1.0 + + s_x[i]-> + dha * + b_llnl * + muhalf)) + + bdot_llnl) * LOG_10 * s_x[i]->moles; + break; + } + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP); + } + break; + case 8: /* LLNL CO2 */ + if (llnl_temp.size() > 0) + { + s_x[i]->lg = log_g_co2; + s_x[i]->dg = dln_g_co2 * s_x[i]->moles; + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP); + } + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + return (OK); +} +/* ------------------------------------------------------------------------------- */ +int Phreeqc::gammas_a_f(int i1) +/* ------------------------------------------------------------------------------- */ +{ + int i, j; + //LDBLE d2, d3, coef = 0, sum = 0; + LDBLE d2, d3, sum = 0; + //char name[MAX_LENGTH]; + std::string name; + //class master *m_ptr; + + i = i1; + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + { + //strcpy(name, s_x[i]->rxn_x.token[j].s->name); + name = s_x[i]->rxn_x.token[j].s->name; + //m_ptr = s_x[i]->rxn_x.token[j].s->primary->elt->master; // appt debug + break; + } + } + + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->gflag != 4 || s_x[i]->primary) + continue; + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + { + if (!strcmp(name.c_str(), s_x[i]->rxn_x.token[j].s->name)) + sum += s_x[i]->moles * s_x[i]->equiv; + break; + } + } + } + i = i1; + d2 = s_x[i]->moles * s_x[i]->equiv / sum; + if (d2 > 1) d2 = 1; + //if (iterations > 19) + // i += 0; // appt debug + + d3 = 0.5; + if (s_x[i]->a_f > 2) + { + d3 += (s_x[i]->a_f - 2) / 10; if (d3 > 0.8) d3 = 0.8; + } + d2 = s_x[i]->dw_a * d3 + (1 - d3) * d2; + s_x[i]->lg -= s_x[i]->a_f * (1 - d2); + s_x[i]->dw_a = d2; + return 0; +} +/* ------------------------------------------------------------------------------- */ +int Phreeqc:: +ineq(int in_kode) +/* ------------------------------------------------------------------------------- */ +{ +/* + * Sets up equations and inequalities for Cl1. + * Scales columns if necessary. + * Eliminates equations that are not necessary because + * gas_phase, s_s, or phase equation is not needed + * Mallocs space + * Calls Cl1 + * Rescales results if necessary + */ + int i, j; + int return_code; + int l_count_rows; + int l_count_optimize, count_equal; + int k, l, m, n; + int l_klmd, l_nklmd, l_n2d; + int l_iter; + LDBLE l_error; + LDBLE max; + int l_kode; + LDBLE min; +#ifdef SLNQ + LDBLE *slnq_array; + LDBLE *slnq_delta1; +#endif +/* Debug + if (debug_model == TRUE) { + output_msg(sformatf( "\narray:\n\n"); + array_print(array, count_unknowns, count_unknowns + 1, count_unknowns + 1)); + } + */ +/* + * Special case for removing unstable phases + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + cxxPPassemblageComp * comp_ptr; + + if (remove_unstable_phases == TRUE) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nSolution vector for removing unstable phases:\n")); + } + for (i = 0; i < count_unknowns; i++) + { + + if (x[i]->type == PP) + { + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + //assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end()); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (residual[i] > 0e-8 && x[i]->moles > 0 && + //it->second.Get_add_formula().size() == 0 + comp_ptr->Get_add_formula().size() == 0 + && x[i]->dissolve_only == FALSE) + { + /* + * Set mass transfer to all of phase + */ + delta[i] = x[i]->moles; + } + else + { + delta[i] = 0.0; + } + if (debug_model == TRUE) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[i]->description, (double) delta[i])); + } + } + } + remove_unstable_phases = FALSE; + return (OK); + } +/* + * Pitzer model does not have activity of water //or mu + */ + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == AH2O && full_pitzer == FALSE) || + (x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE) || + /*x[i]->type == MU ||*/ + (x[i]->type == PITZER_GAMMA && full_pitzer == FALSE)) + { + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + i] = 0.0; + } + for (j = 0; j < count_unknowns + 1; j++) + { + my_array[(size_t)i * (count_unknowns + 1) + (size_t)j] = 0.0; + } + } + } + } +/* + * Normalize column + */ + normal.resize(count_unknowns); + std::fill(normal.begin(), normal.end(), 1.0); + + for (i = 0; i < count_unknowns; i++) + { + max = 0.0; + + if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH + || x[i]->type == SURFACE || x[i]->type == SURFACE_CB + || x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2) + { + if (x[i]->moles <= MIN_RELATED_SURFACE + && (x[i]->type == EXCH || x[i]->type == SURFACE)) + continue; + for (j = 0; j < count_unknowns; j++) + { + if (x[i]->type == SURFACE && x[j]->type == SURFACE_CB) + continue; + if (x[i]->type == SURFACE_CB1 && x[j]->type == SURFACE_CB2) + continue; + + if (fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]) > max) + { + max = fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]); + if (max > min_value) + break; + } + } + if (diagonal_scale == TRUE) + { + if (fabs(my_array[(size_t)i * (count_unknowns + 1) + (size_t)i]) < min_value) + { + max = fabs(my_array[(size_t)i * (count_unknowns + 1) + (size_t)i]); + } + } + + if (max == 0) + { + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = 1e-5 * x[i]->moles; + max = fabs(1e-5 * x[i]->moles); + } + } + + if (x[i]->type == MH && (pitzer_model == FALSE || pitzer_pe == TRUE)) + { + /* make absolute value of diagonal at least 1e-12 */ + + min = 1e-12; + min = MIN_TOTAL; + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] += min; + if (fabs(my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number]) < min) + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] = min; + max = 0.0; + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != MB && + x[j]->type != SURFACE && + x[j]->type != SURFACE_CB && + x[j]->type != SURFACE_CB1 && + x[j]->type != SURFACE_CB2 && + x[j]->type != EXCH && x[j]->type != MH + && x[j]->type != MH2O) + continue; + if (fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]) > max) + { + max = fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]); + if (max > min_value) + break; + } + } + } + + if (max > 0.0 && max < min_value) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Scaling column for %s, max= %e\n", + x[i]->description, (double) max)); + } + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= min_value / max; + } + normal[i] = min_value / max; + } + } + +/* + * Allocate arrays for inequality solver + */ + max_row_count = 2 * count_unknowns + 2; + max_column_count = count_unknowns + 2; + ineq_array.resize(max_row_count * max_column_count); + back_eq.resize(max_row_count); + zero.resize(max_row_count); + memset(&zero[0], 0, max_row_count * sizeof(double)); + res.resize(max_row_count); + memset(&res[0], 0, max_row_count * sizeof(double)); + delta1.resize(max_column_count); + memset(&delta1[0], 0,max_column_count * sizeof(double)); +/* + * Copy equations to optimize into ineq_array + */ + l_count_rows = 0; + for (i = 0; i < count_unknowns; i++) + { + if (iterations < aqueous_only) + continue; +/* + * Pure phases + */ + if (x[i]->type == PP) + { + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + + /* not in model, ignore */ + if (x[i]->phase->in == FALSE) + continue; + // delay removing phase + if (x[i]->moles > 0.0 || x[i]->f <= 0.0 || iterations == 0 || equi_delay == 0) + { + x[i]->iteration = iterations; + } + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_force_equality()) + if (comp_ptr->Get_force_equality()) + continue; + /* Undersaturated and no mass, ignore */ + if (x[i]->f > 0e-8 && x[i]->moles <= 0 + && iterations >= x[i]->iteration + equi_delay + && comp_ptr->Get_add_formula().size() == 0) + { + continue; + } + else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE + //&& (x[i]->moles - it->second.Get_initial_moles() >= 0)) + && (x[i]->moles - comp_ptr->Get_initial_moles() >= 0)) + { + continue; + } + else + { + /* Copy in saturation index equation (has mass or supersaturated) */ + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + //if (it->second.Get_add_formula().size() == 0 + if (comp_ptr->Get_add_formula().size() == 0 + && x[i]->dissolve_only == FALSE) + { + res[l_count_rows] = 1.0; + } +/* + * If infeasible solution on first attempt, remove constraints on IAP + */ + if (pp_scale != 1) + { + for (j = 0; j < count_unknowns + 1; j++) + { + ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] *= pp_scale; + } + } + + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; + } + } + else if (x[i]->type == ALK || x[i]->type == SOLUTION_PHASE_BOUNDARY) + { +/* + * Alkalinity and solution phase boundary + */ + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + l_count_rows++; +/* + * Gas phase + */ + } + else if (x[i]->type == GAS_MOLES && gas_in == TRUE) + { + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + + res[l_count_rows] = 1.0; + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; +/* + * Solid solution + */ + } + else if (x[i]->type == SS_MOLES && x[i]->ss_in == TRUE) + { + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + res[l_count_rows] = 1.0; + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; + } + } + l_count_optimize = l_count_rows; +/* + * Copy equality equations into ineq_array + */ + for (i = 0; i < count_unknowns; i++) + { + comp_ptr = NULL; + cxxPPassemblageComp *comp_ptr1 = NULL; + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + } + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL) + { + comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name); + } + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[(size_t)i - 1]->phase_unknown != NULL) + { + comp_ptr1 = pp_assemblage_ptr->Find(x[(size_t)i-1]->phase_unknown->phase->name); + } + if (x[i]->type != SOLUTION_PHASE_BOUNDARY && + x[i]->type != ALK && + x[i]->type != GAS_MOLES && x[i]->type != SS_MOLES && + x[i]->type != PITZER_GAMMA + /* && x[i]->type != PP */ + ) + { + if (x[i]->type == PP && !comp_ptr->Get_force_equality()) + continue; + if (x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE) + continue; + + if (mass_water_switch == TRUE && x[i] == mass_oxygen_unknown) + continue; +/* + * Mass balance, CB, MU, AH2O, MH, MH2O, others + */ + if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) + continue; + if (x[i]->type == SURFACE && + x[i]->phase_unknown == NULL + && x[i]->moles <= MIN_RELATED_SURFACE) + continue; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) ) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + continue; + } + } + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL && + x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE && + comp_ptr->Get_add_formula().size() == 0) + continue; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[(size_t)i - 1]->phase_unknown != NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE && + comp_ptr1->Get_add_formula().size() == 0) + { + continue; + } + } + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + if (mass_water_switch == TRUE && x[i] == mass_hydrogen_unknown) + { + k = (int)mass_oxygen_unknown->number; + for (j = 0; j < count_unknowns; j++) + { + ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] -= + 2 * my_array[(size_t)k * (count_unknowns + 1) + (size_t)j]; + } + } + l_count_rows++; + } + else if (x[i]->type == PITZER_GAMMA && full_pitzer == TRUE) + { + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + l_count_rows++; + } + } + count_equal = l_count_rows - l_count_optimize; +/* + * Copy inequality constraints into ineq + */ + if (pure_phase_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + /* not in model, ignore */ + if (x[i]->phase->in == FALSE) + continue; + /* No moles and undersaturated, ignore */ + if (x[i]->moles <= 0.0 && x[i]->f > 0e-8 && + comp_ptr->Get_add_formula().size() == 0) + { + continue; + /* No moles of pure phase present, must precipitate */ + } + else if (x[i]->moles <= 0.0) + { + delta1[i] = -1.0; + } + else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE + && (x[i]->moles - comp_ptr->Get_initial_moles() >= + 0)) + { + continue; + } + else + { + + /* Pure phase is present, force Mass transfer to be <= amount of mineral remaining */ + memset(&ineq_array[(size_t)l_count_rows * max_column_count], 0, ((size_t) count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[(size_t)l_count_rows * max_column_count + i] = 1.0; + ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + /* Pure phase is present and dissolve_only, force ppt to be <= amount of dissolved so far */ + if (x[i]->dissolve_only == TRUE) + { + //memcpy((void *) + // &(ineq_array[(size_t)l_count_rows * max_column_count]), + // (void *) &(zero[0]), + // ((size_t) count_unknowns + 1) * sizeof(LDBLE)); + memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0, (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[(size_t)l_count_rows * max_column_count + i] = -1.0; + ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = + comp_ptr->Get_initial_moles() - x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + } + } + } +/* + * Add inequality for mass of oxygen greater than zero + */ + if (pitzer_model || sit_model) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MH2O) + { + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type < PP) + { + ineq_array[(size_t)l_count_rows * max_column_count + j] = 0.0; + } + else + { + /*ineq_array[l_count_rows*max_column_count + j] = -ineq_array[l_count_rows*max_column_count + j]; */ + } + } + ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = + 0.5 * x[i]->moles; + l_count_rows++; + } + } + } + + +/* + * Hydrogen mass balance is good + */ +/* + * No moles and undersaturated, mass transfer must be zero + */ + if (pure_phase_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if ((x[i]->moles <= 0.0 && x[i]->f > 0e-8 && + comp_ptr->Get_add_formula().size() == 0) + || x[i]->phase->in == FALSE) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + i] = 0.0; + } + } + if (x[i]->dissolve_only == TRUE) + { + if (x[i]->f < 0e-8 + && (x[i]->moles - comp_ptr->Get_initial_moles() >= + 0)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0; + } + } + } + } + } + } +/* + * No moles of exchanger + */ + if (use.Get_exchange_ptr() != NULL + && (use.Get_exchange_ptr()->Get_related_phases() || + use.Get_exchange_ptr()->Get_related_rate())) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == EXCH && x[i]->moles <= 0) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + i] = 0.0; + } + } + } + } +/* + * No moles of surface + */ + if (use.Get_surface_ptr() != NULL + && (use.Get_surface_ptr()->Get_related_phases() + || use.Get_surface_ptr()->Get_related_rate())) + { + for (i = 0; i < count_unknowns; i++) + { + comp_ptr = NULL; + cxxPPassemblageComp * comp_ptr1 = NULL; + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL) + { + comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name); + } + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && (x[(size_t)i - 1]->phase_unknown != NULL)) + { + comp_ptr1 = pp_assemblage_ptr->Find(x[(size_t)i-1]->phase_unknown->phase->name); + } + LDBLE grams = 0; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[(size_t)i - 1]->phase_unknown != NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + grams = charge_ptr->Get_grams(); + } + if ((x[i]->type == SURFACE && x[i]->phase_unknown != NULL && + x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE && + comp_ptr->Get_add_formula().size() == 0) || + ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[(size_t)i - 1]->phase_unknown != NULL && + grams <= MIN_RELATED_SURFACE && + comp_ptr1->Get_add_formula().size() == 0)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0; + } + } + } + } +/* + * No moles of surface + */ + if (use.Get_surface_ptr() != NULL) + { + LDBLE grams = 0;; + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) && x[(size_t)i - 1]->phase_unknown == NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + grams = charge_ptr->Get_grams(); + } + if ((x[i]->type == SURFACE && + x[i]->phase_unknown == NULL && + x[i]->moles <= MIN_RELATED_SURFACE) || + ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[(size_t)i - 1]->phase_unknown == NULL + && grams <= MIN_RELATED_SURFACE)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0; + } + } + } + } +/* + * Moles of gas must be >= zero + */ + if (gas_in == TRUE) + { + for (i = (int)gas_unknown->number; i < (int)count_unknowns; i++) + { + if (x[i]->type == GAS_MOLES) + { + //memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + // (void *) &(zero[0]), + // ((size_t) count_unknowns + 1) * sizeof(LDBLE)); + //std::fill(&(ineq_array[(size_t)l_count_rows * max_column_count]), + // &(ineq_array[l_count_rows * max_column_count + count_unknowns]), + // 0.0e0); + memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0, + (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[(size_t)l_count_rows * max_column_count + (size_t)i] = -1.0; + ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = + x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + else + { + break; + } + } + } + else if (use.Get_gas_phase_ptr() != NULL && gas_in == FALSE) + { +/* + * Moles of gas small and sum p < ptotal + */ + i = (int)gas_unknown->number; + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0; + } + } +/* + * Phase must be "in" and moles of solid solution must be >= zero + */ + + if (ss_unknown != NULL) + { + for (i = (int)ss_unknown->number; i < (int)count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + break; + if (x[i]->phase->in == TRUE && x[i]->ss_in == TRUE) + { + //memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + // (void *) &(zero[0]), + // ((size_t) count_unknowns + 1) * sizeof(LDBLE)); + memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0, + (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[(size_t)l_count_rows * max_column_count + (size_t)i] = 1.0; + ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = + 0.99 * x[i]->moles - MIN_TOTAL_SS; + back_eq[l_count_rows] = i; + l_count_rows++; + } + else + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0; + } + } + } + } +/* + * Add inequality if total moles of element is less than zero + */ + if (negative_concentrations) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB && x[i]->moles < 0.0) + { + memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]), + (void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + ((size_t) count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type < PP) + { + ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] = 0.0; + } + } + l_count_rows++; + } + } + } +/* + * Zero column for mass of water + */ + if (mass_oxygen_unknown != NULL && mass_water_switch == TRUE) + { + k = (int)mass_oxygen_unknown->number; + for (j = 0; j < l_count_rows + 1; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)k] = 0; + } + } +/* + * Scale column for pure phases + */ + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == PP || x[i]->type == SS_MOLES) && x[i]->phase->in == TRUE + && pp_column_scale != 1.0) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[(size_t)j * max_column_count + (size_t)i] *= pp_column_scale; + } + normal[i] = pp_column_scale; + } + + } + if (debug_model == TRUE) + { + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(&ineq_array[0], (int)l_count_rows, (int)count_unknowns + 1, + (int)max_column_count); + } +/* + * Calculate dimensions + */ + k = l_count_optimize; /* rows in A */ + l = count_equal; /* rows in C */ + m = l_count_rows - l - k; /* rows in E */ + if (m < 0) + m = 0; + + if (debug_model == TRUE) + { + output_msg(sformatf( "k, l, m\t%d\t%d\t%d\n", k, l, m)); + } +#define SHRINK_ARRAY +#ifdef SHRINK_ARRAY + if ((sit_model || pitzer_model) && full_pitzer == FALSE) + { + n = (int)count_unknowns - (int)s_list.size(); + for (int i = 0; i < l_count_rows; i++) + { + for (int j = 0; j < n; j++) + { + ineq_array[(size_t)i*((size_t)n+2) + (size_t)j] = ineq_array[(size_t)i*(count_unknowns+2) + (size_t)j]; + } + //if (i > 0) + //{ + // memcpy((void *) &ineq_array[i*(n+2)], (void *) &ineq_array[i*(count_unknowns+2)], (size_t) (n) * sizeof(LDBLE)); + //} + ineq_array[(size_t)i*((size_t)n+2) + (size_t)n] = ineq_array[(size_t)i*(count_unknowns+2) + count_unknowns]; + } + } + else + { + n = (int)count_unknowns; /* columns in A, C, E */ + } +#else + n = count_unknowns; /* columns in A, C, E */ +#endif + l_klmd = (int)max_row_count - 2; + l_nklmd = n + l_klmd; + l_n2d = n + 2; +/* + * Retain constraints on mineral mass transfers, even if infeasible on + * first attempt. + */ + l_kode = 1; + + if (in_kode == 2) + { + l_kode = 1; + } + l_iter = 2*(n + l_count_rows); +/* + * Allocate space for arrays + */ + cu.resize(2 * (size_t)l_nklmd); + iu.resize(2 * (size_t)l_nklmd); + is.resize(l_klmd); + +#ifdef SLNQ + slnq_array = + (LDBLE *) PHRQ_malloc((size_t) count_unknowns * + (count_unknowns + 1) * sizeof(LDBLE)); + if (slnq_array == NULL) + malloc_error(); + for (i = 0; i < k + l; i++) + { + for (j = 0; j <= count_unknowns; j++) + { + slnq_array[i * (count_unknowns + 1) + j] = + ineq_array[i * max_column_count + j]; + } + } + slnq_delta1 = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (slnq_delta1 == NULL) + malloc_error(); + memcpy((void *) &(slnq_delta1[0]), (void *) &(zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); +#endif +/* + * Call CL1 + */ + cl1(k, l, m, n, l_nklmd, l_n2d, &ineq_array[0], + &l_kode, ineq_tol, &l_iter, &delta1[0], &res[0], + &l_error, &cu[0], &iu[0], &is[0], FALSE); +/* Set return_kode */ + if (l_kode == 1) + { + return_code = ERROR; + } + else if (l_kode == 2) + { + return_code = 2; + } + else if (l_kode == 3) + { + return_code = ERROR; + return_code = 2; + warning_msg("Too many iterations in Cl1. Should not have done this."); + } + else + { + return_code = OK; + } +#ifdef SLNQ +/* if (l_kode > 0 && ((k + l) == count_unknowns)) { */ + if (l_kode > 0 && ((k + l) <= count_unknowns)) + { + if (add_trivial_eqns(k + l, count_unknowns, slnq_array) == TRUE) + { + if (debug_model == TRUE) + output_msg(sformatf( "Calling SLNQ, iteration %d\n", + iterations)); + log_msg(sformatf( "Calling SLNQ, iteration %d\n", + iterations)); + if (slnq + (count_unknowns, slnq_array, slnq_delta1, count_unknowns + 1, + debug_model) == OK) + { + memcpy((void *) &(delta1[0]), (void *) &(slnq_delta1[0]), + (size_t) count_unknowns * sizeof(LDBLE)); + if (debug_model == TRUE) + output_msg(sformatf( "Using SLNQ results.\n")); + log_msg(sformatf( "Using SLNQ results.\n")); + return_code = OK; + } + else + { + if (debug_model == TRUE) + output_msg(sformatf( + "Could not use SLNQ results.\n")); + log_msg(sformatf( "Could not use SLNQ results.\n")); + } + } + else + { + log_msg(sformatf( + "Could not call SLNQ, row %d, unknowns %d, iteration %d\n", + k + l, count_unknowns, iterations)); + } + } + else if (l_kode > 0) + { + log_msg(sformatf( "Could not call SLNQ, row %d, unknowns %d\n", + k + l, count_unknowns)); + } +#endif +/* Copy delta1 into delta and scale */ +#ifdef SHRINK_ARRAY + //memcpy((void *) &(delta[0]), (void *) &(zero[0]), + // (size_t) count_unknowns * sizeof(LDBLE)); + memset(&(delta[0]), 0, + count_unknowns * sizeof(LDBLE)); +#endif + memcpy((void *) &(delta[0]), (void *) &(delta1[0]), + (size_t) n * sizeof(LDBLE)); + for (i = 0; i < n; i++) + delta[i] *= normal[i]; +/* + * Rescale columns of array + */ + for (i = 0; i < count_unknowns; i++) + { + if (normal[i] != 1.0) + { + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] /= normal[i]; + } + } + } + + /* adjust unknowns where cl1 failed */ + if (l_kode != 0) + { + for (i = 0; i < l_count_rows; i++) + { + /* + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[back_eq[i]]->description, (double) res[i); + */ + j = back_eq[i]; + if (x[j]->type == MB && delta[j] == 0.0 && fabs(res[i]) > ineq_tol) + { + delta[j] = res[i]/fabs(res[i]) * 1; + } + } + } + +/* + * Debug, write results of ineq + */ + + if (debug_model == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", l_kode, + l_iter, (double) l_error)); + output_msg(sformatf( "\nsolution vector:\n")); + for (i = 0; i < count_unknowns; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + x[i]->description, (double) delta[i])); + if (x[i]->type == PP) + { + output_msg(sformatf( " -SI %10.2e Moles %10.2e", + (double) x[i]->f, (double) x[i]->moles)); + if (x[i]->f < 0e-8 || x[i]->moles > 0.0) + { + output_msg(sformatf( " **")); + } + } + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (i = 0; i < l_count_rows; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[back_eq[i]]->description, (double) res[i])); + } + } +#ifdef SLNQ + slnq_array = (LDBL *) free_check_null(slnq_array); + slnq_delta1 = (LDBL *) free_check_null(slnq_delta1); +#endif + return (return_code); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in jacobian array, uses arrays sum_jacob0, sum_jacob1, and + * sum_jacob2. + */ + int i, j, k; + LDBLE sinh_constant; +/* + * Clear array, note residuals are in array[i, count_unknowns+1] + */ + for (i = 0; i < count_unknowns; i++) + { + my_array[i] = 0.0; + } + for (i = 1; i < count_unknowns; i++) + { + memcpy((void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (void *) &(my_array[0]), (size_t) count_unknowns * sizeof(LDBLE)); + } +/* + * Add constant terms + */ + for (k = 0; k < (int)sum_jacob0.size(); k++) + { + *sum_jacob0[k].target += sum_jacob0[k].coef; + } +/* + * Add terms with coefficients of 1.0 + */ + for (k = 0; k < (int)sum_jacob1.size(); k++) + { + *sum_jacob1[k].target += *sum_jacob1[k].source; + } +/* + * Add terms with coefficients != 1.0 + */ + for (k = 0; k < (int)sum_jacob2.size(); k++) + { + *sum_jacob2[k].target += *sum_jacob2[k].source * sum_jacob2[k].coef; + } +/* + * Make final adustments to jacobian array + */ +/* + * Ionic strength + */ + if (mu_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + // using straight mu equation + my_array[(size_t)mu_unknown->number * (count_unknowns + 1) + i] *= 0.5; + } + my_array[(size_t)mu_unknown->number * (count_unknowns + 1) + + (size_t)mu_unknown->number] -= mass_water_aq_x; + } +/* + * Mass of oxygen + */ + if (mass_oxygen_unknown != NULL && mu_unknown != NULL) + { + my_array[(size_t)mu_unknown->number * (count_unknowns + 1) + + (size_t)mass_oxygen_unknown->number] -= mu_x * mass_water_aq_x; + } +/* + * Activity of water + */ + if (ah2o_unknown != NULL) + { + if (dampen_ah2o) + { + // factors for tanh attenuation to make ah2o positive + LDBLE y_sum = ah2o_unknown->f; + LDBLE x_h2o = mass_water_aq_x; + LDBLE a = AH2O_FACTOR; + LDBLE lim = 95.0 - 100.0*a*y_sum/x_h2o; + LDBLE factor = -a*(x_h2o*(-0.5*tanh(lim) - 0.5) + (50.0*a*y_sum - 47.5 * x_h2o) / (cosh(lim)*cosh(lim))) / + (x_h2o*x_h2o); + + for (i = 0; i < count_unknowns; i++) + { + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)i] *= factor; + } + // activity of water term + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + + (size_t)ah2o_unknown->number] -= exp(s_h2o->la * LOG_10); + + // mass of water term + if (mass_oxygen_unknown != NULL) + { + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)mass_oxygen_unknown->number] -= + a*y_sum*(x_h2o*(0.5*tanh(lim) + 0.5) + (47.5*x_h2o - 50.0*a*y_sum)/(cosh(lim)*cosh(lim))) / + (x_h2o*x_h2o*x_h2o); + } + } + else + { + for (i = 0; i < count_unknowns; i++) + { + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + i] *= -AH2O_FACTOR; + } + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)ah2o_unknown->number] -= + mass_water_aq_x * exp(s_h2o->la * LOG_10); + if (mass_oxygen_unknown != NULL) + { + my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)mass_oxygen_unknown->number] -= + (exp(s_h2o->la * LOG_10) - 1) * mass_water_aq_x; + } + } + } + +/* + * Surface charge balance + */ + if (surface_unknown != NULL && dl_type_x == cxxSurface::NO_DL) + { + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + { + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x * + // 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x * + 1000); + for (i = 0; i < count_unknowns; i++) + { + cxxSurfaceCharge *charge_ptr = NULL; + if (x[i]->type == SURFACE_CB) + { + charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + } + if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0) + { + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)j] *= + F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams()); + } + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] -= + sinh_constant * sqrt(mu_x) * cosh(x[i]->master[0]->s->la * LOG_10); + if (mu_unknown != NULL) + { + my_array[(size_t)x[i]->number * (count_unknowns + 1) + + (size_t)mu_unknown->number] -= 0.5 * sinh_constant / sqrt(mu_x) * + sinh(x[i]->master[0]->s->la * LOG_10); + } + } + } + } + else + { + for (i = 0; i < count_unknowns; i++) + { + cxxSurfaceCharge *charge_ptr = NULL; + if (x[i]->type == SURFACE_CB) + { + charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + } + if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0) + { + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)j] *= + F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams()); + } + my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] -= + charge_ptr->Get_capacitance0() * 2 * R_KJ_DEG_MOL * tk_x * LOG_10 / F_KJ_V_EQ; + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates sums of species for calculation of mass balances, charge + * balance. Also calculates saturation indices for solution_phase_boundaries + * and pure_phases. After this routine total calcium calculated from all + * calcium species in solution is stored in x[i]->f. Also calculates + * x[i]->sum for some types of unknowns. Uses arrays sum_mb1 and + * sum_mb1, which are generated in prep and reprep. + */ + int k; +/* + * Clear functions in unknowns + */ + for (k = 0; k < count_unknowns; k++) + { + x[k]->f = 0.0; + x[k]->sum = 0.0; + } +/* + * Add terms with coefficients of 1.0 + */ + for (k = 0; k < (int)sum_mb1.size(); k++) + { + *sum_mb1[k].target += *sum_mb1[k].source; + } +/* + * Add terms with coefficients != 1.0 + */ + for (k = 0; k < (int)sum_mb2.size(); k++) + { + *sum_mb2[k].target += *sum_mb2[k].source * sum_mb2[k].coef; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_gases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determines whether gas_phase equation is needed + */ + gas_in = FALSE; + if (gas_unknown == NULL || use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown->f > gas_phase_ptr->Get_total_p() + 1e-7 || + gas_unknown->moles > MIN_TOTAL) + { + gas_in = TRUE; + //patm_x = gas_phase_ptr->Get_total_p(); + + } + } + else + { + if (numerical_fixed_volume && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume)) + { + gas_in = TRUE; + } + else + { + gas_in = FALSE; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_ss(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lp, log10_iap, total_moles; + LDBLE iapc, iapb, l_kc, l_kb, lc, lb, xcaq, xbaq, xb, xc; + LDBLE sigmapi_aq, sigmapi_solid; + LDBLE total_p; + class rxn_token *rxn_ptr; +/* + * Determines whether solid solution equation is needed + */ + if (ss_unknown == NULL || use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + total_moles = 0; + //bool ss_in = true; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + int l; + class phase *phase_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[j].Get_name().c_str(), &l, FALSE); + if (phase_ptr->in == FALSE) + { + continue; + } + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + total_moles += comp_ptr->Get_moles(); + + } + if (total_moles > 1.e10*MIN_TOTAL) + { + ss_ptr->Set_ss_in(true); + } + else if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0) + { + int l; + class phase *phase0_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[0].Get_name().c_str(), &l, FALSE); + class phase *phase1_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[1].Get_name().c_str(), &l, FALSE); + /* + * Calculate IAPc and IAPb + */ + if (phase0_ptr->in == TRUE && phase0_ptr->rxn_x.token.size() != 0) + { + log10_iap = 0; + for (rxn_ptr = &phase0_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + log10_iap += rxn_ptr->s->la * rxn_ptr->coef; + } + iapc = exp(log10_iap * LOG_10); + } + else + { + iapc = 1e-99; + } + if (phase1_ptr->in == TRUE && phase1_ptr->rxn_x.token.size() != 0) + { + log10_iap = 0; + for (rxn_ptr = &phase1_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + log10_iap += rxn_ptr->s->la * rxn_ptr->coef; + } + iapb = exp(log10_iap * LOG_10); + } + else + { + iapb = 1e-99; + } + /* + * Calculate sigma pi, aq + */ + sigmapi_aq = iapc + iapb; + /* + * Calculate xc,aq and xb, aq + */ + xcaq = iapc / (iapb + iapc); + xbaq = iapb / (iapb + iapc); + /* + * Get Kc and Kb + */ + l_kc = exp(phase0_ptr->lk * LOG_10); + l_kb = exp(phase1_ptr->lk * LOG_10); + /* + * Solve for xb + */ + xb = ss_root(ss_ptr->Get_a0(), ss_ptr->Get_a1(), l_kc, l_kb, xcaq, xbaq); + /* + * Calculate lambdac and lambdab + */ + xc = 1 - xb; + lc = exp((ss_ptr->Get_a0() - ss_ptr->Get_a1() * (-4 * xb + 3)) * xb * xb); + lb = exp((ss_ptr->Get_a0() + ss_ptr->Get_a1() * (4 * xb - 1)) * xc * xc); + /* + * Calculate sigma pi, solid + */ + sigmapi_solid = xb * lb * l_kb + xc * lc * l_kc; + /* + * If Sigma pi, solid < sigma pi, aq, then use eqns + */ + if (sigmapi_solid < sigmapi_aq) + { + ss_ptr->Set_ss_in(true); + } + else + { + ss_ptr->Set_ss_in(false); + } + } + else + { + /* + * Calculate total mole fraction from solution activities + */ + total_p = 0; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + total_p += exp(lp * LOG_10); + } + } + if (total_p > 1.0) + { + ss_ptr->Set_ss_in(true); + } + else + { + ss_ptr->Set_ss_in(false); + } + } + } + for (int i = (int)ss_unknown->number; i < (int)count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + break; + cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr; + x[i]->ss_in = FALSE; + if (x[i]->phase->in == TRUE && ss_ptr->Get_ss_in()) + { + x[i]->ss_in = TRUE; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +molalities(int allow_overflow) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates la for master species + * Calculates lm and moles from lk, lg, and la's of master species + * Adjusts lm of h2 and o2. + */ + int i, j; + LDBLE total_g; + class rxn_token *rxn_ptr; +/* + * la for master species + */ + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->in == REWRITE) + { + master[i]->s->la = master[i]->s->lm + master[i]->s->lg; + } + } + if (dl_type_x != cxxSurface::NO_DL) + { + //s_h2o->tot_g_moles = s_h2o->moles; + //s_h2o->tot_dh2o_moles = 0.0; + + if (calculating_deriv && use.Get_surface_ptr() != NULL) // DL_pitz + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + s_h2o->moles = mass_water_bulk_x / gfw_water; + s_h2o->tot_g_moles = s_h2o->moles; + s_h2o->tot_dh2o_moles = 0.0; + } + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type > HPLUS && s_x[i]->type != EX + && s_x[i]->type != SURF) + continue; +/* + * lm and moles for all aqueous species + */ + s_x[i]->lm = s_x[i]->lk - s_x[i]->lg; + for (rxn_ptr = &s_x[i]->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + s_x[i]->lm += rxn_ptr->s->la * rxn_ptr->coef; + /* + if (isnan(rxn_ptr->s->la)) + { + fprintf(stderr,"molalities la %s %e\n", rxn_ptr->s->name, rxn_ptr->s->la); + } + */ + } + if (s_x[i]->type == EX) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else if (s_x[i]->type == SURF) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else + { + s_x[i]->moles = under(s_x[i]->lm) * mass_water_aq_x; + if (s_x[i]->moles / mass_water_aq_x > 100) + { + log_msg(sformatf( "Overflow: %s\t%e\t%e\t%d\n", + s_x[i]->name, + (double) (s_x[i]->moles / mass_water_aq_x), + (double) s_x[i]->lm, iterations)); + + if (iterations >= 0 && allow_overflow == FALSE) + { + return (ERROR); + } + } + + } + } +/* + * other terms for diffuse layer model + */ + if (use.Get_surface_ptr() != NULL + && dl_type_x != cxxSurface::NO_DL + && (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC + || pitzer_model || calculating_deriv)) // DL_pitz + { + calc_all_donnan(); + } + + class species *s_ptr = NULL; + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_ptr = s_x[i]; + if (s_ptr->type > HPLUS && s_ptr->type != EX && s_ptr->type != SURF) + continue; + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL && s_ptr->type <= HPLUS) + { + total_g = 0.0; + s_ptr->tot_dh2o_moles = 0.0; + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge & charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + cxxSpeciesDL & dl_ref = s_diff_layer[s_ptr->number][charge_ref.Get_name()]; + cxxSurfDL & surf_dl_ref = charge_ref.Get_g_map()[s_ptr->z]; + + //s_diff_layer[is][charge_ref.Get_name()] = dl_ref + //charge_ref.Get_g_map()[s_ptr->z] = surf_dl +/* + * partially corrected formulation assumes mass of water in diffuse layer + * is insignificant. Excess is calculated on the basis of moles_water_aq_x + * instead of moles_water_bulk. + */ + /* revised eq. 61 */ + dl_ref.Set_g_moles(s_ptr->moles * s_ptr->erm_ddl * + (surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x)); + if (s_ptr->moles > 1e-30) + { + dl_ref.Set_dg_g_moles(s_ptr->dg * dl_ref.Get_g_moles() / s_ptr->moles); + } + + /* + * first term of 63 is summed for all surfaces in + * s_ptr->tot_g_moles. This sum is then used in + * the jacobian for species i + */ + total_g += surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x; + /* revised eq. 63, second term */ + /* g.dg is dg/dx(-2y**2) or dg/d(ln y) */ + dl_ref.Set_dx_moles(s_ptr->moles * s_ptr->erm_ddl * surf_dl_ref.Get_dg()); + /* revised eq. 63, third term */ + dl_ref.Set_dh2o_moles(-s_ptr->moles * s_ptr->erm_ddl * + charge_ref.Get_mass_water() / mass_water_aq_x); + s_ptr->tot_dh2o_moles += dl_ref.Get_dh2o_moles(); + + /* surface related to phase */ + dl_ref.Set_drelated_moles(s_ptr->moles * s_ptr->erm_ddl * charge_ref.Get_specific_area() * + use.Get_surface_ptr()->Get_thickness() / mass_water_aq_x); + } + s_ptr->tot_g_moles = s_ptr->moles * (1 + total_g * s_ptr->erm_ddl); + + /* note that dg is for cb, act water, mu eqns */ + /* dg_total_g for mole balance eqns */ + /* dg_g_moles for surface cb */ + + if (s_ptr->moles > 1e-30) + { + s_ptr->dg_total_g = s_ptr->dg * s_ptr->tot_g_moles / s_ptr->moles; + } + else + { + s_ptr->dg_total_g = 0.0; + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( "%s\t%e\t%e\n", s_ptr->name, + (double) s_ptr->moles, + (double) s_ptr->tot_g_moles)); + output_msg(sformatf( "\tg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_ptr->z].Get_g())); + } + output_msg(sformatf( "\n\tg_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_ptr->number; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_g_moles())); + } + output_msg(sformatf( "\n\tdg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_ptr->z].Get_dg())); + } + output_msg(sformatf( "\n\tdx_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + int is = s_ptr->number; + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_dx_moles())); + } + output_msg(sformatf( "\n\tdh2o_moles\t%e\n", + (double) s_ptr->tot_dh2o_moles)); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_ptr->number; + output_msg(sformatf( "\t%e", + s_diff_layer[is][charge_ref.Get_name()].Get_dh2o_moles())); + } + output_msg(sformatf( "\n")); + } + } + } + calc_gas_pressures(); + calc_ss_fractions(); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_gas_pressures(void) +/* ---------------------------------------------------------------------- */ +{ + int n_g = 0; + LDBLE lp, V_m = 0; + class rxn_token *rxn_ptr; + std::vector phase_ptrs; + bool PR = false, pr_done = false; +/* + * moles and partial pressures for gases + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + if (iterations > 2) + return calc_fixed_volume_gas_pressures(); + else + return OK; + } + if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_moles(0); + } + + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp * gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + class phase *phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + phase_ptrs.push_back(phase_ptr); + if (!PR && phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + n_g++; + } + if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (PR /*&& gas_unknown->gas_phase->total_p > 1 */ && iterations > 0) + { + calc_PR(phase_ptrs, gas_phase_ptr->Get_total_p(), tk_x, 0); + } + } else + { + if (PR) + { + if (gas_phase_ptr->Get_total_moles() > 0) + { + V_m = gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles(); + if (V_m < 0.016) + { + V_m = 0.016; + } else if (V_m > 1e4) + { + V_m = 1e4; + } + if (V_m < 0.02) + V_m = (8. * gas_phase_ptr->Get_v_m() + V_m) / 9; + else if (V_m < 0.03) + V_m = (6. * gas_phase_ptr->Get_v_m() + V_m) / 7; + else if (V_m < 0.05) + V_m = (4. * gas_phase_ptr->Get_v_m() + V_m) / 5; + else if (V_m < 0.07) + V_m = (2. * gas_phase_ptr->Get_v_m() + V_m) / 3; + else + V_m = (1. * gas_phase_ptr->Get_v_m() + V_m) / 2; + if ((pitzer_model || iterations > 99) && numerical_fixed_volume == false) + { + //V_m *= 1; /* debug */ + numerical_fixed_volume = true; + //switch_numerical = true; + if (!pitzer_model) warning_msg("Numerical method failed, switching to numerical derivatives."); + prep(); + //switch_numerical = false; + } + } else + V_m = 1.0; + calc_PR(phase_ptrs, 0, tk_x, V_m); + pr_done = true; + } else + { + gas_phase_ptr->Set_total_p(0); + } + } + + gas_phase_ptr->Set_total_moles(0); + std::vector gas_comps; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + class phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(LOG_10 * (lp - phase_ptr->pr_si_f)); + //if (!strcmp(phase_ptr->name, "H2O(g)") && phase_ptr->p_soln_x > 90) + // phase_ptr->p_soln_x = 90; + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_unknown->moles / gas_phase_ptr->Get_total_p(); + phase_ptr->fraction_x = + phase_ptr->moles_x / gas_unknown->moles; + } + else + { + if (pr_done) + { + lp = phase_ptr->p_soln_x / gas_phase_ptr->Get_total_p() * + gas_phase_ptr->Get_volume() / V_m; + if (lp > 0) + phase_ptr->moles_x = lp; + if (iterations > 50) + { + lp *= 1.0; /* debug */ + } + } else + { + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + + phase_ptr->moles_x); + } + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } + } + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && !PR) + { + /* + * Fixed-volume gas phase reacting with a solution + * Change pressure used in logK to pressure of gas phase + */ + if (gas_phase_ptr->Get_total_p() > MAX_P_NONLLNL && llnl_temp.size() == 0) + { + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + class phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + phase_ptr->moles_x *= MAX_P_NONLLNL / gas_phase_ptr->Get_total_p(); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + + phase_ptr->moles_x); + } + } + gas_phase_ptr->Set_total_p(MAX_P_NONLLNL); + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_ss_fractions(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE moles, n_tot; +/* + * moles and lambdas for solid solutions + */ + if (ss_unknown == NULL) + return (OK); +/* + * Calculate mole fractions and log lambda and derivative factors + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + n_tot = 0; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles < 0) + { + moles = MIN_TOTAL_SS; + comp_ptr->Set_initial_moles(moles); + } + n_tot += moles; + } + ss_ptr->Set_total_moles(n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + moles = comp_ptr->Get_moles(); + if (moles < 0) + { + moles = MIN_TOTAL_SS; + } + comp_ptr->Set_fraction_x(moles / n_tot); + comp_ptr->Set_log10_fraction_x(log10(moles / n_tot)); + + /* all mb and jacobian items must be in x or phase to be static between models */ + phase_ptr->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + } + if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0) + { + ss_binary(ss_ptr); + } + else + { + ss_ideal(ss_ptr); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_binary(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1; + //LDBLE xb2, xb3, xb4, xc2, xc3; + LDBLE xb2, xc2; + LDBLE xb1, xc1; +/* + * component 0 is major component + * component 1 is minor component + * xb is the mole fraction of second component (formerly trace) + * xc is the mole fraction of first component (formerly major) +*/ +/* + * Calculate mole fractions and log lambda and derivative factors + */ + n_tot = ss_ptr->Get_total_moles(); + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + int l; + class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &l, FALSE); + class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &l, FALSE); + + nc = comp0_ptr->Get_moles(); + xc = nc / n_tot; + nb = comp1_ptr->Get_moles(); + xb = nb / n_tot; +/* + * In miscibility gap + */ + l_a0 = ss_ptr->Get_a0(); + l_a1 = ss_ptr->Get_a1(); + if (ss_ptr->Get_miscibility() && xb > ss_ptr->Get_xb1() + && xb < ss_ptr->Get_xb2()) + { + xb1 = ss_ptr->Get_xb1(); + xc1 = 1.0 - xb1; + comp0_ptr->Set_fraction_x(xc1); + comp0_ptr->Set_log10_fraction_x(log10(xc1)); + phase0_ptr->log10_fraction_x = + comp0_ptr->Get_log10_fraction_x(); + + comp1_ptr->Set_fraction_x(xb1); + comp1_ptr->Set_log10_fraction_x(log10(xb1)); + phase1_ptr->log10_fraction_x = + comp1_ptr->Get_log10_fraction_x(); + + comp0_ptr->Set_log10_lambda( + xb1 * xb1 * (l_a0 - l_a1 * (3 - 4 * xb1)) / LOG_10); + phase0_ptr->log10_lambda = + comp0_ptr->Get_log10_lambda(); + + comp1_ptr->Set_log10_lambda( + xc1 * xc1 * (l_a0 + l_a1 * (4 * xb1 - 1)) / LOG_10); + phase1_ptr->log10_lambda = + comp1_ptr->Get_log10_lambda(); + + comp0_ptr->Set_dnb(0); + comp0_ptr->Set_dnc(0); + comp1_ptr->Set_dnb(0); + comp1_ptr->Set_dnc(0); + phase0_ptr->dnb = 0; + phase0_ptr->dnc = 0; + phase1_ptr->dnb = 0; + phase1_ptr->dnc = 0; + } + else + { +/* + * Not in miscibility gap + */ + comp0_ptr->Set_fraction_x(xc); + comp0_ptr->Set_log10_fraction_x(log10(xc)); + phase0_ptr->log10_fraction_x = + comp0_ptr->Get_log10_fraction_x(); + + comp1_ptr->Set_fraction_x(xb); + comp1_ptr->Set_log10_fraction_x(log10(xb)); + phase1_ptr->log10_fraction_x = + comp1_ptr->Get_log10_fraction_x(); + + comp0_ptr->Set_log10_lambda( + xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10); + phase0_ptr->log10_lambda = + comp0_ptr->Get_log10_lambda(); + + comp1_ptr->Set_log10_lambda( + xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10); + phase1_ptr->log10_lambda = + comp1_ptr->Get_log10_lambda(); + + xc2 = xc * xc; + //xc3 = xc2 * xc; + xb2 = xb * xb; + //xb3 = xb2 * xb; + //xb4 = xb3 * xb; + /* xb4 = xb4; */ + /* xc3 = xc3; */ + + /* used derivation that did not substitute x2 = 1-x1 */ + + /* first component, df1/dn1 */ + dnc = 2 * l_a0 * xb2 + 12 * l_a1 * xc * xb2 + 6 * l_a1 * xb2; + phase0_ptr->dnc = -xb / nc + dnc / n_tot; + + + /* first component, df1/dn2 */ + dnb = + 1 - 2 * l_a0 * xb + 2 * l_a0 * xb2 + 8 * l_a1 * xc * xb - + 12 * l_a1 * xc * xb2 - 2 * l_a1 * xb + 2 * l_a1 * xb2; + phase0_ptr->dnb = dnb / n_tot; + + /* second component, df2/dn1 */ + dnc = + 1 - 2 * l_a0 * xc + 2 * l_a0 * xc2 - 8 * l_a1 * xb * xc + + 12 * l_a1 * xb * xc2 + 2 * l_a1 * xc - 2 * l_a1 * xc2; + phase1_ptr->dnc = dnc / n_tot; + + /* second component, df2/dn2 */ + dnb = 2 * l_a0 * xc2 + 12 * l_a1 * xb * xc2 - 6 * l_a1 * xc2; + phase1_ptr->dnb = -xc / nb + dnb / n_tot; + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_ideal(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE n_tot, n_tot1; + +/* + * component 0 is major component + * component 1 is minor component + * xb is the mole fraction of second component (formerly trace) + * xc is the mole fraction of first component (formerly major) +*/ +/* + * Calculate mole fractions and log lambda and derivative factors + */ + n_tot = ss_ptr->Get_total_moles(); + +/* + * Ideal solid solution + */ + ss_ptr->Set_dn(1.0 / n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *compk_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phasek_ptr = phase_bsearch(compk_ptr->Get_name().c_str(), &l, FALSE); + n_tot1 = 0; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *compj_ptr = &(ss_ptr->Get_ss_comps()[j]); + if (j != k) + { + n_tot1 += compj_ptr->Get_moles(); + } + } + compk_ptr->Set_log10_lambda(0); + compk_ptr->Set_log10_lambda(0); + + compk_ptr->Set_dnb(-(n_tot1) / (compk_ptr->Get_moles() * n_tot)); + phasek_ptr->dnb = compk_ptr->Get_dnb(); + + compk_ptr->Set_dn(ss_ptr->Get_dn()); + phasek_ptr->dn = ss_ptr->Get_dn(); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reset(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks deltas (changes to unknowns) to make sure they are reasonable + * Scales deltas if necessary + * Updates unknowns with deltas + */ + + int i; + int converge; + LDBLE up, down; + LDBLE d; + LDBLE factor, f0; + LDBLE sum_deltas; + LDBLE step_up; + LDBLE mu_calc; + LDBLE old_moles; + int warning; +/* + * Calculate interphase mass transfers + */ + cxxPPassemblageComp * comp_ptr; + + step_up = log(step_size_now); + factor = 1.; + + if ((pure_phase_unknown != NULL || ss_unknown != NULL) + && calculating_deriv == FALSE) + { +/* + * Don`t take out more mineral than is present + */ + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == PP || x[i]->type == SS_MOLES) && x[i]->phase->in == TRUE) + { + + if (delta[i] < -1e8) + { + delta[i] = -10.; + } + else if (delta[i] > 1e8) + { + delta[i] = 10; + } + if (x[i]->dissolve_only == TRUE) + { + assert (x[i]->type == PP); + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + assert(comp_ptr); + if ((delta[i] < 0.0) + && (-delta[i] > + (comp_ptr->Get_initial_moles() - x[i]->moles))) + { + if ((comp_ptr->Get_initial_moles() - x[i]->moles) != + 0.0) + { + f0 = fabs(delta[i] / + (comp_ptr->Get_initial_moles() - + x[i]->moles)); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much dissolve_only mineral.\tDelta %e\tCurrent %e\tInitial %e\n", + x[i]->description, + (double) delta[i], + (double) x[i]->moles, + (double) comp_ptr->Get_initial_moles())); + } + factor = f0; + } + } + else + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating dissolve_only mineral.\tDelta %e\n", + x[i]->description, + (double) delta[i])); + } + delta[i] = 0; + } + } + } + if ( /* delta[i] > 0.0 && */ x[i]->moles > 0.0 + && delta[i] > x[i]->moles) + { + f0 = delta[i] / x[i]->moles; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Removing more than total mineral.\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + else if (delta[i] > 0.0 && x[i]->moles <= 0.0) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s\tDelta: %e\tMass: %e " + "Dissolving mineral with 0.0 mass.\n ", + x[i]->description, (double) delta[i], + (double) x[i]->moles)); + } + delta[i] = 0.0; + } + else if (x[i]->ss_comp_name != NULL && delta[i] < -x[i]->phase->delta_max) + // Uses delta_max computed in step + // delta_max is the maximum amount of the mineral that could form based + // on the limiting element in the system + { + f0 = -delta[i] / x[i]->phase->delta_max; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much mineral.\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + else if (x[i]->ss_comp_name != NULL && delta[i] < -pe_step_size*x[i]->moles) + { + f0 = (-delta[i]) / (pe_step_size*x[i]->moles); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much mineral.\t%f\n", + x[i]->description, (double)f0)); + } + factor = f0; + } + } + else if (x[i]->ss_comp_name != NULL && delta[i] > x[i]->moles/ pe_step_size) + { + f0 = (delta[i]) / (x[i]->moles/ pe_step_size); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much mineral.\t%f\n", + x[i]->description, (double)f0)); + } + factor = f0; + } + } + } + } + } +/* + * Calculate change in element concentrations due to pure phases and gases + */ + warning = 0; + for (i = 0; i < count_unknowns; i++) + { + + /*if (isnan(delta[i]))*/ + if (!PHR_ISFINITE((double) delta[i])) + { + warning ++; + delta[i] = 0; + } + + } + if (warning > 0) + { + error_string = sformatf( "%d delta equal NaN\n", warning); + warning_msg(error_string); + } + if (pure_phase_unknown != NULL || gas_unknown != NULL + || ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->delta = 0.0; + } + + for (i = 0; i < (int)sum_delta.size(); i++) + { + *sum_delta[i].target += *sum_delta[i].source * sum_delta[i].coef; + } + +/* + * Apply factor from minerals to deltas + */ + + + for (i = 0; i < count_unknowns; i++) + { + x[i]->delta /= factor; + if (x[i]->type == PP || x[i]->type == SS_MOLES) + delta[i] /= factor; + } + + } + + +/* + * Calc factor for mass balance equations for aqueous unknowns + */ + factor = 1.0; + sum_deltas = 0.0; + for (i = 0; i < count_unknowns; i++) + + { + /* fixes underflow problem on Windows */ + if (delta[i] > 0) + { + sum_deltas += delta[i]; + } + else + { + sum_deltas -= delta[i]; + + } + /*sum_deltas += fabs(delta[i]); */ + if (calculating_deriv == FALSE) + { + up = step_up; + down = up; + if (x[i]->type <= SOLUTION_PHASE_BOUNDARY) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == MU) + { + up = 100 * mu_x; + down = mu_x; + } + else if (x[i]->type == AH2O) + { + down = up; + if (pitzer_model || sit_model) + { + up = 0.05; + down = -0.03; + } + } + else if (x[i]->type == MH) + { + up = log(pe_step_size_now); + down = 1.3 * up; + } + else if (x[i]->type == MH2O) + { + /* ln gH2O + delta; ln(gH2O*delta); */ + /* + up = log(10.); + down = log(4.); + */ + up = log(1.3); + down = log(1.2); + + } + else if (x[i]->type == PP) + { + continue; + } + else if (x[i]->type == GAS_MOLES) + { + up = 1000. * x[i]->moles; + if (up <= 0.0) + up = 1e-1; + if (up >= 1.0) + up = 1.; + down = x[i]->moles; + } + else if (x[i]->type == SS_MOLES) + { + continue; + } + else if (x[i]->type == EXCH) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == SURFACE) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == PITZER_GAMMA) + { + up = step_up; + if (up > 1) + { + up = 0.7; + } + down = 1.3 * up; + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + up = step_up; + down = 1.3 * up; + /* + up = 1.3; + down = 1.2; + */ + } + + if (delta[i] > 0.0) + { + f0 = delta[i] / up; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( "%-10.10s\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + else + { + f0 = delta[i] / (-down); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( "%-10.10s\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + } + } + + /*converge=TRUE; */ + + if (debug_model == TRUE) + { + output_msg(sformatf( "\nSum of deltas: %12.6f\n", + (double) sum_deltas)); + output_msg(sformatf( "Factor: %12.4e\n", (double) factor)); + } + factor = 1.0 / factor; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP && x[i]->type != SS_MOLES) + delta[i] *= factor; + } + + warning = 0; + for (i = 0; i < count_unknowns; i++) + { + + /*if (isnan(delta[i]))*/ + if (!PHR_ISFINITE((double) delta[i])) + { + warning ++; + delta[i] = 0; + } + + } + if (warning > 0) + { + error_string = sformatf( "%d delta equal NaN after scaling\n", warning); + warning_msg(error_string); + } +/* + * Solution mass balances: MB, ALK, CB, SOLUTION_PHASE_BOUNDARY + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH + || x[i]->type == SURFACE) + { + /*if ( fabs(delta[i]) >= epsilon ) converge = FALSE; */ + d = delta[i] / LOG_10; + /* surface */ + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + old_moles = x[i]->moles; + if (x[i]->phase_unknown != NULL) + { + x[i]->moles = comp_ptr->Get_phase_proportion() * + (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number]); + if (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number] <= + MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + if (old_moles <= 0 && x[i]->moles > 0) + { + x[i]->master[0]->s->la = log10(x[i]->moles) - 5.; + } + } + else if (comp_ptr->Get_phase_name().size() > 0) + { + /* probably initial surface calculation */ + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + } + } + /* exch */ + if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e " + "%-8s%10.2e\n", x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) x[i]->master[0]->s->la + (double) d, + "delta", (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; + //if (x[i]->master[0]->s->la < (double) (DBL_MIN_10_EXP + 10)) + // x[i]->master[0]->s->la = (double) (DBL_MIN_10_EXP + 10); + +/* + * Surface charge balance + */ + + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + d = delta[i] / LOG_10; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (x[i]->phase_unknown != NULL) + { + charge_ptr->Set_grams( + (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number])); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + charge_ptr->Set_grams(0.0); + } + } + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + charge_ptr->Set_grams(0.0); + } + x[i]->related_moles = charge_ptr->Get_grams(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n", + x[i]->description, "old f*psi", + (double) x[i]->master[0]->s->la, "new f*psi", + (double) x[i]->master[0]->s->la + (double) d, + "delta", (double) d)); + } + + x[i]->master[0]->s->la += d; + + /* recalculate g's for component */ + if (dl_type_x != cxxSurface::NO_DL + && (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM + || (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC + && x[i]->type == SURFACE_CB2))) + { + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\ncharge, old g, new g, dg*delta," + " dg, delta\n")); + } + std::map::iterator jit; + for (jit = charge_ptr->Get_g_map().begin(); jit != charge_ptr->Get_g_map().end(); jit++) + { + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\t%12.4e\t%12.4e\t%12.4e\t%12.4e\t%12.4e\n", + + (double) jit->second.Get_g(), + (double) jit->second.Get_g() + + (double) (jit->second.Get_dg() * + delta[i]), + (double) (jit->second.Get_dg() * + delta[i]), + (double) jit->second.Get_dg(), + (double) delta[i])); + } + if (use.Get_surface_ptr()->Get_dl_type() != cxxSurface::DONNAN_DL) + { + jit->second.Set_g(jit->second.Get_g() + + jit->second.Get_dg() * delta[i]); + } + } + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + calc_all_donnan(); + } + } + +/* Solution phase boundary */ + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; +/* Charge balance */ + } + else if (x[i]->type == CB) + { + /*if (fabs(delta[i]) > epsilon * mu_x * mass_water_aq_x ) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; +/* Ionic strength */ + } + else if (x[i]->type == MU) + { + // using straight ionic strength equation + { + mu_calc = 0.5 * mu_unknown->f / mass_water_aq_x; + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Calculated mu: %e\n", + (double) mu_calc)); + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n", + x[i]->description, "old mu", (double) mu_x, + "new mu", (double) (mu_x + delta[i]), "delta", + (double) delta[i])); + } + d = mu_x + delta[i]; + if (d < 1e-11) + { + delta[i] = sqrt(mu_calc * mu_x) - mu_x; + mu_x = sqrt(mu_calc * mu_x); + } + else + { + mu_x += delta[i]; + } + //if (mu_x <= 1e-8) + //{ + // mu_x = 1e-8; + //} +/* Activity of water */ + } + else if (x[i]->type == AH2O) + { + /*if (pitzer_model == TRUE && full_pitzer == FALSE) continue; */ + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + s_h2o->la += d; + ah2o_x = exp(s_h2o->la * LOG_10); +/* pe */ + } + else if (x[i]->type == MH) + { + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old pe", + (double) x[i]->master[0]->s->la, "new pe", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + s_eminus->la += d; +/* Mass of water */ + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + { + continue; + } + /*if (fabs(delta[i]) > epsilon * mass_water_aq_x) converge=FALSE; */ + /* ln(gh2o) + delta, log(gh2o) + d, gh2o * 10**d */ + d = exp(delta[i]); + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old MH2O", + (double) mass_water_aq_x, "new MH2O", + (double) (mass_water_aq_x * d), "delta", + (double) delta[i], "10**d/c", (double) d)); + } + mass_water_aq_x *= d; + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + if (debug_model == TRUE && dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf( + "mass_water bulk: %e\taq: %e\tsurfaces: %e\n", + (double) mass_water_bulk_x, + (double) mass_water_aq_x, + (double) mass_water_surfaces_x)); + } + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + x[i]->master[0]->s->moles = mass_water_bulk_x / gfw_water; + } + + if (mass_water_aq_x < 1e-10) + { + error_string = sformatf( + "Mass of water is less than 1e-10 kilogram.\n" + "The aqueous phase may not be stable relative to given masses of minerals."); + warning_msg(error_string); + stop_program = TRUE; + return (TRUE); + } +/* Pure phases */ + } + else if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mass", + (double) x[i]->moles, "new mass", + (double) (x[i]->moles - delta[i]), "delta", + (double) delta[i])); + } + if (equal(x[i]->moles, delta[i], ineq_tol)) + { + x[i]->moles = 0.0; + } + else + { + x[i]->moles -= delta[i]; + } + + if (x[i]->dissolve_only == TRUE) + { + if (equal + (x[i]->moles, comp_ptr->Get_initial_moles(), ineq_tol)) + x[i]->moles = comp_ptr->Get_initial_moles(); + } + /*if (fabs(x[i]->moles) < MIN_RELATED_SURFACE) x[i]->moles = 0.0; */ + } + else if (x[i]->type == GAS_MOLES) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + /*if (gas_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mol", + (double) x[i]->moles, "new mol", + (double) (x[i]->moles + delta[i]), "delta", + (double) delta[i])); + } + x[i]->moles += delta[i]; + if (x[i]->moles < MIN_TOTAL) + x[i]->moles = MIN_TOTAL; + if (x[i] == gas_unknown && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + !calculating_deriv) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + "Pressure", "old P", + (double) last_patm_x, "new P", + (double) gas_phase_ptr->Get_total_p(), "iter P", + (double) patm_x)); + } + patm_x = gas_phase_ptr->Get_total_p(); + //if (patm_x < 1e-10 && patm_x < p_sat) + //{ + // patm_x = ( 1 * patm_x + p_sat) / 2.0; + //} + if (llnl_temp.size() == 0) + { + if (patm_x > MAX_P_NONLLNL) + patm_x = MAX_P_NONLLNL; + } + } + last_patm_x = patm_x; + } + else if (x[i]->type == SS_MOLES && x[i]->ss_in == TRUE) + { + + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + /*if (x[i]->ss_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mol", + (double) x[i]->moles, "new mol", + (double) (x[i]->moles - delta[i]), "delta", + (double) delta[i])); + } + x[i]->moles -= delta[i]; + if (x[i]->moles < MIN_TOTAL_SS && calculating_deriv == FALSE) + x[i]->moles = MIN_TOTAL_SS; + cxxSScomp *comp_ptr = (cxxSScomp *) x[i]->ss_comp_ptr; + comp_ptr->Set_moles(x[i]->moles); +/* Pitzer gamma */ + } + else if (x[i]->type == PITZER_GAMMA) + { + if (full_pitzer == FALSE) + continue; + d = delta[i]; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old lg", (double) x[i]->s->lg, + "new lg", (double) (x[i]->s->lg + d), "delta", + (double) delta[i], "delta", (double) d)); + } + x[i]->s->lg += d; + } + } +/* + * Reset total molalities in mass balance equations + */ + if (pure_phase_unknown != NULL || gas_unknown != NULL + || ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == MH || + x[i]->type == MH2O || + x[i]->type == CB || x[i]->type == EXCH + || x[i]->type == SURFACE) + { + /*if (fabs(x[i]->delta) > epsilon*x[i]->moles) converge = FALSE; */ + if (x[i]->type == SURFACE) + x[i]->delta = 0.0; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mole", + (double) x[i]->moles, "new mole", + (double) (x[i]->moles + x[i]->delta), "delta", + (double) x[i]->delta)); + } + x[i]->moles += x[i]->delta; + } + } + } + converge = FALSE; + return (converge); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +residuals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates residuals for all equations + */ + int i, j; + int converge; + + LDBLE l_toler; + LDBLE sum_residual; + LDBLE sinh_constant; + LDBLE sum, sum1; + class master *master_ptr, *master_ptr1, *master_ptr2; + LDBLE sigmaddl, negfpsirt; + int print_fail; + std::vector cd_psi; + print_fail = FALSE; + sum_residual = 0.0; + sigmaddl = 0; + sum = 0; +/* + * Calculate residuals + */ + converge = TRUE; + l_toler = convergence_tolerance; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB) + { + residual[i] = x[i]->moles - x[i]->f; + if ((fabs(residual[i]) > l_toler * x[i]->moles && + fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL) && + x[i]->moles > MIN_TOTAL) + || + x[i]->moles < 0) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == ALK) + { + residual[i] = x[i]->moles - x[i]->f; + if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + residual[i] = x[i]->f * LOG_10; + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == CB) + { + residual[i] = -x[i]->f; + if (ph_unknown == charge_balance_unknown) + { + residual[i] += x[i]->moles; + } + if (fabs(residual[i]) >= l_toler * mu_x * mass_water_aq_x) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == MU /*&& pitzer_model == FALSE && sit_model == FALSE*/) + { + // Using straight ionic strength equation + { + residual[i] = mass_water_aq_x * mu_x - 0.5 * x[i]->f; + } + if (fabs(residual[i]) > l_toler * mu_x * mass_water_aq_x) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == AH2O) + { + if (dampen_ah2o) + { + // a = 0.017; Y = sum(m(i)); X = Mw (mass of water) + // Aw = (1 - a y/x) 0.5 (tanh(100 (0.95 - ay/x)) + 1) + 0.05 (0.5 (1 - tanh(100(0.95 - ay/x)))) + //residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - 0.017 * x[i]->f/mass_water_aq_x) * + // 0.5*(tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)) + 1) + + // 0.05*0.5*(1.0 - tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)))) ; + residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - AH2O_FACTOR * x[i]->f/mass_water_aq_x) * + 0.5*(tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)) + 1) + + 0.05*0.5*(1.0 - tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)))) ; + } + else + { + //residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x + + // 0.017 * x[i]->f; + residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x + + AH2O_FACTOR * x[i]->f; + } + + if (pitzer_model || sit_model) + { + residual[i] = pow((LDBLE) 10.0, s_h2o->la) - AW; + if (full_pitzer == FALSE) + { + residual[i] = 0.0; + } + } + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == MH + && (pitzer_model == FALSE || pitzer_pe == TRUE)) + { +#ifdef COMBINE + residual[i] = x[i]->moles - x[i]->f; +#else + residual[i] = (x[i]->moles - 2 * s_h2o->moles) - x[i]->f; + x[i]->f += 2 * s_h2o->moles; +#endif + if (mass_water_switch == TRUE) + { + residual[i] -= + 2 * (mass_oxygen_unknown->moles - mass_oxygen_unknown->f); + } +#ifdef COMBINE +#ifndef COMBINE_CHARGE + if (fabs(residual[i]) > + l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#else + if (fabs(residual[i]) > + l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles + + charge_balance_unknown->moles)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#endif +#else + if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#endif + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + continue; +#ifdef COMBINE + residual[i] = x[i]->moles - x[i]->f; +#else + residual[i] = (x[i]->moles - s_h2o->moles) - x[i]->f; + x[i]->f += s_h2o->moles; +#endif + if (fabs(residual[i]) > 0.01 * l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == PP) + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + residual[i] = x[i]->f * LOG_10; + if (comp_ptr->Get_add_formula().size() == 0) + { + if (x[i]->dissolve_only == TRUE) + { + if ((residual[i] > l_toler && x[i]->moles > 0.0) + || (residual[i] < -l_toler + && (comp_ptr->Get_initial_moles() - + x[i]->moles) > 0)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else + { + if (residual[i] < -l_toler || iterations < 1) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + } + else + { + /* if (x[i]->moles > 0.0 && fabs(residual[i]) > l_toler) converge = FALSE; */ + if (residual[i] < -l_toler || iterations < 1) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + } + else if (x[i]->type == GAS_MOLES) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + residual[i] = x[i]->moles - x[i]->phase->moles_x; + } + else + { + residual[i] = gas_phase_ptr->Get_total_p() - x[i]->f; + } + + if (fabs(residual[i]) > l_toler && gas_in == TRUE) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (fabs(last_patm_x - patm_x) > 0.001 || fabs(last_patm_x - gas_phase_ptr->Get_total_p()) > 0.001) + && !calculating_deriv) + { + if (print_fail) + output_msg(sformatf("Failed pressure test %d: %e %e %e\n", iterations, last_patm_x, patm_x, + gas_phase_ptr->Get_total_p())); + converge = FALSE; + } + } + else if (x[i]->type == SS_MOLES) + { + if (x[i]->ss_in == FALSE) + continue; + residual[i] = x[i]->f * LOG_10; + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == EXCH) + { + residual[i] = x[i]->moles - x[i]->f; + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE) + { + residual[i] = x[i]->moles - x[i]->f; + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else if (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles) + { + } + else if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == PITZER_GAMMA) + { + if (full_pitzer == FALSE) + continue; + residual[i] = x[i]->s->lg - x[i]->s->lg_pitzer; + if (fabs(residual[i]) > l_toler) + { + /* + fprintf(stderr,"Residuals %d: %s %d %e\n", iterations, x[i]->description, i, residual[i]); + */ + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::DDL) + { + /*sinh_constant = 0.1174; */ + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + // tk_x * 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + tk_x * 1000); +/* if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) { */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + residual[i] = -x[i]->f; + } + else + { + residual[i] = sinh_constant * sqrt(mu_x) * sinh(x[i]->master[0]->s->la * LOG_10) - + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams()); + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Charge/Potential\n")); + if (charge_ptr->Get_grams() > 0) + { + if (dl_type_x != cxxSurface::NO_DL) + output_msg(sformatf("\tSum of surface + diffuse layer charge %e eq\n", (double)(x[i]->f))); + else + output_msg(sformatf( + "\tSum of surface charge %e eq\n", + (double)(x[i]->f + /* F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) */ + ))); + } + else + { + output_msg(sformatf( "\tResidual %e\n", + (double) x[i]->f)); + } + output_msg(sformatf( "\t grams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCharge from potential %e eq\n", + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() / F_C_MOL * + sinh_constant * sqrt(mu_x) * + sinh(x[i]->master[0]->s->la * LOG_10)))); + output_msg(sformatf( "\t FPsi/2RT %e\n", + (double) (x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Sinh(FPsi/2RT) %e\n", + sinh(x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Cosh(FPsi/2RT) %e\n", + cosh(x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Sqrt(mu_x) %e\n", + sqrt(mu_x))); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + cd_psi.clear(); + cd_psi.push_back(0.0); + cd_psi.push_back(0.0); + cd_psi.push_back(0.0); + } + else + { + /* sum is in moles of charge */ + /*psi = pow(10, x[i]->surface_charge->psi_master->s->la); */ /* = exp(-Fpsi/RT) */ + master_ptr = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + + cd_psi.clear(); + + cd_psi.push_back(-(master_ptr->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + cd_psi.push_back(-(master_ptr1->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + cd_psi.push_back(-(master_ptr2->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + sum = 0; + for (size_t j = 0; j < x[i]->comp_unknowns.size(); j++) + { + sum += + x[i]->comp_unknowns[j]->moles * + x[i]->comp_unknowns[j]->master[0]->s->z; + } + charge_ptr->Set_sigma0( + (x[i]->f + + sum) * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + /* f is in moles */ + /* eqns A-3 */ + residual[i] = + charge_ptr->Get_sigma0() - + charge_ptr->Get_capacitance0() * + (cd_psi[0] - cd_psi[1]); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual A %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + residual[i] = -x[i]->f; + } + else + { + residual[i] = + charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ - + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()); + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Charge/Potential\n")); + if (charge_ptr->Get_grams() > 0) + { + if (dl_type_x != cxxSurface::NO_DL) + output_msg(sformatf("\tSum of surface + diffuse layer charge %e eq\n", (double)(x[i]->f))); + else + output_msg(sformatf( + "\tSum of surface charge %e eq\n", + (double) (x[i]->f + /* F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) */ + ))); + } + else + { + output_msg(sformatf( "\tResidual %e\n", + (double) x[i]->f)); + } + output_msg(sformatf( "\t grams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCharge from potential %e eq\n", + (double) (charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + output_msg(sformatf( "\t Psi %e\n", + (double) (x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB1) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else + { + /* eqns A-4 */ + /*psi = pow(10, x[i]->surface_charge->psi_master1->s->la); */ /* = exp(-Fpsi/RT) */ + charge_ptr->Set_sigma1( + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + residual[i] = + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1()) - + charge_ptr->Get_capacitance1() * + (cd_psi[1] - cd_psi[2]); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual B %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + sum = 0; + sum1 = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type == SURF) + { + sum += under(s_x[j]->lm) * s_x[j]->dz[2]; + } + if (s_x[j]->type < H2O) + { + int is = s_x[j]->number; + sum1 += s_x[j]->z * s_x[j]->z * s_diff_layer[is][charge_ptr->Get_name()].Get_g_moles(); + } + } + charge_ptr->Set_sigma2( + sum * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + charge_ptr->Set_sigmaddl( + (x[i]->f - + sum) * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + + residual[i] = + x[i]->f + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1()) * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL; + /* residual[i] = sum + (x[i]->surface_charge->sigma0 + x[i]->surface_charge->sigma1) * (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) / F_C_MOL */ + } + else + { + /* eqns A-6 and A-7 */ + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + // tk_x * 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + tk_x * 1000); + /* + * sinh_constant is (8 e e0 R T 1000)**1/2 + * = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*t_x*1000) + * ~ 0.1174 at 25C + */ + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + negfpsirt = master_ptr2->s->la * LOG_10; + sum = 0; + sum1 = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type < H2O) + { + sum += + under(s_x[j]->lm) * + (exp(s_x[j]->z * negfpsirt) - 1); + sum1 += under(s_x[j]->lm) * s_x[j]->z; + } + } + + /* add fictitious monovalent ion that balances charge */ + //sum += fabs(sum1) * (exp(-sum1 / fabs(sum1) * negfpsirt) - 1); + if (sum1 >= 0) + { + sum += fabs(sum1) * (exp(-negfpsirt) - 1); + } + else + { + sum += fabs(sum1) * (exp(negfpsirt) - 1); + } + + if (sum < 0) + { + sum = -sum; + { + if (print_fail) + output_msg(sformatf( + "Failed Residual C %d: %s %d %e %e\n", + iterations, x[i]->description, i, sum, + l_toler)); + converge = FALSE; + } + /*output_msg(sformatf( "Negative sum, iteration %d\n", iterations)); */ + } + charge_ptr->Set_sigma2( + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + if ((negfpsirt) < 0) + { + sigmaddl = -0.5 * sinh_constant * sqrt(sum); + } + else + { + sigmaddl = 0.5 * sinh_constant * sqrt(sum); + } + charge_ptr->Set_sigmaddl(sigmaddl); + residual[i] = + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2()) + sigmaddl; + } + if (debug_model == TRUE) + { + master_ptr = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + output_msg(sformatf( "CD_music Charge/Potential 2\n")); + output_msg(sformatf( "\tgrams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCapacitances %g\t%g\n", + (double) charge_ptr->Get_capacitance0(), + charge_ptr->Get_capacitance1())); + output_msg(sformatf( "\t-F/(RT) %g\n", + (double) -F_KJ_V_EQ / (R_KJ_DEG_MOL * tk_x))); + output_msg(sformatf( "\tResidual 0 %14e\n", + (double) residual[master_ptr->unknown->number])); + output_msg(sformatf( "\tResidual 1 %14e\n", + (double) residual[master_ptr1->unknown->number])); + output_msg(sformatf( "\tResidual 2 %14e\n", + (double) residual[master_ptr2->unknown->number])); + output_msg(sformatf( "\texp(-FPsi0/RT) %14e", + (double) pow((LDBLE) 10., master_ptr->s->la))); + output_msg(sformatf( "\tPsi0 %14e\n", + (double) cd_psi[0])); + output_msg(sformatf( "\texp(-FPsi1/RT) %14e", + (double) pow((LDBLE) 10., master_ptr1->s->la))); + output_msg(sformatf( "\tPsi1 %14e\n", + (double) cd_psi[1])); + output_msg(sformatf( "\texp(-FPsi2/RT) %14e", + (double) pow((LDBLE) 10., master_ptr2->s->la))); + output_msg(sformatf( "\tPsi2 %14e\n", + (double) cd_psi[2])); + output_msg(sformatf( "\tf 0 %14e", + (double) master_ptr->unknown->f)); + output_msg(sformatf( "\tsigma 0 %14e\n", + (double) charge_ptr->Get_sigma0())); + output_msg(sformatf( "\tf 1 %14e", + (double) master_ptr1->unknown->f)); + output_msg(sformatf( "\tsigma 1 %14e\n", + (double) charge_ptr->Get_sigma1())); + output_msg(sformatf( "\tf 2 %14e", + (double) master_ptr2->unknown->f)); + output_msg(sformatf( "\tsigma 2 %14e\n", + (double) charge_ptr->Get_sigma2())); + output_msg(sformatf( "\tsigma ddl %14e\n", + (double) sigmaddl)); + output_msg(sformatf( "\texp sum %14e\n", + (double) sum)); + + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual D %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } +/* + * Store residuals in array + */ + my_array[((size_t)i + 1) * (count_unknowns + 1) - 1] = residual[i]; + sum_residual += fabs(residual[i]); + } +/* + * Return + */ + if ((pitzer_model == TRUE || sit_model == TRUE) && iterations < 1) + return (OK); + if (converge == TRUE) + { + return (CONVERGED); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + if (pitzer_model == TRUE) + return (set_pz(initial)); + if (sit_model == TRUE) + return (set_sit(initial)); + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg = 0.0; + } +/* + * Set master species activities + */ + + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + potV_x = solution_ptr->Get_potV(); + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) + initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) + initial_surface_water(); + revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + // mu_x is reset here, but the real, already calculated mu_x must be used for INITIAL_EXCHANGE & _SURFACE appt + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise la's of master species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + LDBLE d; + + max_iter = 10; + gammas(mu_x); + l_iter = 0; + repeat = TRUE; + fail = FALSE;; + while (repeat == TRUE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } +/* debug + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || +/* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + + f = fabs(x[i]->sum); + /*if (isnan(f) || !_finite(f))*/ + if (!PHR_ISFINITE((double) f)) + { + f = 0; + } + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += 5; + if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + else if (f > 1.5 * fabs(x[i]->moles) + || f < 1e-5 * fabs(x[i]->moles)) + { + weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + d = 0; + + // avoid underflows and overflows + if (x[i]->moles > 1e101 || x[i]->moles < 1e-101 || + x[i]->sum > 1e101 || x[i]->sum < 1e-101) + { + LDBLE d1 = log10(x[i]->moles); + LDBLE d2 = log10(x[i]->sum); + LDBLE d3 = d1 - d2; + if (d3 > DBL_MAX_10_EXP/2) + { + d = pow(10.0, DBL_MAX_10_EXP/2.); + } + else if (d3 < DBL_MIN_10_EXP/2.) + { + d = pow(10.0, DBL_MIN_10_EXP/2.); + } + } + else + { + d = fabs(x[i]->moles / x[i]->sum); + } + LDBLE d1; + if (d > 0) + { + d1 = weight * log10(d); + if (PHR_ISFINITE((double) d1) /*&& d1 < 5.0*/) + { + x[i]->master[0]->s->la += d1; + } + else + { + warning_msg("Adjustment to la in revise_guesses was NaN\n"); + x[i]->master[0]->s->la += 5.0; + } + } + else + { + x[i]->master[0]->s->la += 5.0; + } + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1e-5 * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in revise_guesses: %d\n", l_iter)); + mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + gammas(mu_x); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sum_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates total alk, total carbon, total co2, electrical balance, + * total hydrogen, and total oxygen. + * + * Sorts species for summing and printing based on valence state and + * concentrations. + * + * Sums total valence states and stores in master[i]->total. + */ + int i; + class master *master_ptr; +/* + * Set global variables + */ + ph_x = -s_hplus->la; + solution_pe_x = -s_eminus->la; + ah2o_x = exp(s_h2o->la * LOG_10); + + density_x = 1.0; + if (s_o2 != NULL) + s_o2->moles = under(s_o2->lm) * mass_water_aq_x; + if (s_h2 != NULL) + s_h2->moles = under(s_h2->lm) * mass_water_aq_x; + +/* + * Calculate sums + */ + total_alkalinity = 0.0; + total_carbon = 0.0; + total_co2 = 0.0; + cb_x = 0.0; + total_ions_x = 0.0; + total_o_x = 0.0; + total_h_x = 0.0; + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type == EX) + continue; + if (s_x[i]->type == SURF) + continue; + cb_x += s_x[i]->z * s_x[i]->moles; + total_ions_x += fabs(s_x[i]->z * s_x[i]->moles); + total_alkalinity += s_x[i]->alk * s_x[i]->moles; + total_carbon += s_x[i]->carbon * s_x[i]->moles; + total_co2 += s_x[i]->co2 * s_x[i]->moles; + + total_h_x += s_x[i]->h * s_x[i]->moles; + total_o_x += s_x[i]->o * s_x[i]->moles; + + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_debye_lengths() > 0 && state >= REACTION + && s_x[i]->type == H2O) + { + total_h_x -= 2 * mass_water_surfaces_x / gfw_water; + total_o_x -= mass_water_surfaces_x / gfw_water; + } + } + } +/* + * Sum valence states, put in master->total + */ + for (i = 0; i < (int)master.size(); i++) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + } + for (i = 0; i < (int)species_list.size(); i++) + { + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + } + else + { + master_ptr = species_list[i].master_s->primary; + } + master_ptr->total += species_list[i].s->moles * species_list[i].coef; + } +/* + * Calculate mass-balance sums + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || + x[i]->type == SURFACE || + (x[i]->type == CB && x[i] != ph_unknown && x[i] != pe_unknown)) + { + x[i]->sum = 0.0; + for (size_t j = 0; j < x[i]->master.size(); j++) + { + x[i]->sum += x[i]->master[j]->total; + } + } + else if (x[i]->type == ALK) + { + x[i]->sum = total_co2; + } + } +/* + * Calculate total element concentrations + */ + for (i = 0; i < (int)master.size(); i++) + { + master[i]->elt->primary->total_primary += master[i]->total; + } + /* + * Calculate isotope ratios + */ + calculate_values(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +surface_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Use extra iterative loop to converge on g_factors + */ + int i, debug_diffuse_layer_save, debug_model_save; + cxxSolution *solution_ptr; + LDBLE prev_aq_x; +/* + * Allocate space for g factors for diffuse layer in surface complexation + */ + debug_diffuse_layer_save = debug_diffuse_layer; + debug_model_save = debug_model; + if (last_model.force_prep) + { + same_model = FALSE; + } + else + { + same_model = check_same_model(); + } + if (dl_type_x != cxxSurface::NO_DL && same_model == FALSE) + { + s_diff_layer.clear(); + for (i = 0; i < (int)s.size(); i++) + { + std::map < std::string, cxxSpeciesDL > dl; + s_diff_layer.push_back(dl); + for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSpeciesDL species_dl; + std::string name = use.Get_surface_ptr()->Get_surface_charges()[j].Get_name(); + s_diff_layer.back()[name] = species_dl; + } + } + } + if (state >= REACTION && dl_type_x != cxxSurface::NO_DL) + { + if (use.Get_mix_ptr() != NULL) + { + mass_water_bulk_x = 0.0; + std::map::const_iterator cit; + for (cit = use.Get_mix_ptr()->Get_mixComps().begin(); cit != use.Get_mix_ptr()->Get_mixComps().end(); cit++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, cit->first); + mass_water_bulk_x += solution_ptr->Get_mass_water() * cit->second; + } + } + else + { + mass_water_bulk_x = use.Get_solution_ptr()->Get_mass_water(); + } + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(use.Get_surface_ptr()->Get_surface_charges()[i]); + mass_water_bulk_x += charge_ptr->Get_mass_water(); + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + charge_ptr->Set_mass_water(0.0); + } + } + prep(); + k_temp(tc_x, patm_x); + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + initial_surface_water(); + calc_init_donnan(); + } + else + { + calc_init_g(); + } + if (state >= REACTION && !use.Get_surface_ptr()->Get_new_def()) + { + set(FALSE); + } + else + { + set(TRUE); + } + if (model() == ERROR) + return (ERROR); + + g_iterations = 0; + + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + do + { + g_iterations++; + prev_aq_x = mass_water_aq_x; + k_temp(tc_x, patm_x); + gammas(mu_x); + molalities(TRUE); + mb_sums(); + if (model() == ERROR) + return (ERROR); + + if (!use.Get_surface_ptr()->Get_related_phases() + && !use.Get_surface_ptr()->Get_related_rate()) + initial_surface_water(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "Surface_model (Donnan approximation): %d g_iterations, %d model iterations\n", + g_iterations, iterations)); + } + } + while ((calc_all_donnan() == FALSE + || fabs(1 - prev_aq_x / mass_water_aq_x) > 1e-6) + && g_iterations < itmax); + } + else + { + do + { + g_iterations++; + if (g_iterations > itmax - 10) + { + debug_model = TRUE; + debug_diffuse_layer = TRUE; + } + + gammas(mu_x); + molalities(TRUE); + mb_sums(); + if (model() == ERROR) + return (ERROR); + if (!use.Get_surface_ptr()->Get_related_phases() + && !use.Get_surface_ptr()->Get_related_rate()) + initial_surface_water(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "Surface_model (full integration): %d g_iterations, %d iterations\n", + g_iterations, iterations)); + } + } + while (calc_all_g() == FALSE && g_iterations < itmax); + } + if (g_iterations >= itmax) + { + pr.all = TRUE; + pr.surface = TRUE; + pr.species = TRUE; + pr.use = TRUE; + print_all(); + error_msg("Did not converge on g (diffuse layer excess)", STOP); + } + debug_diffuse_layer = debug_diffuse_layer_save; + debug_model = debug_model_save; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_model_allocs(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * free space allocated in model + */ + int i; + for (i = 0; i < (int)x.size(); i++) + { + unknown_free(x[i]); + } + x.clear(); + count_unknowns = 0; + max_unknowns = 0; + my_array.clear(); + delta.clear(); + residual.clear(); + s_x.clear(); + sum_mb1.clear(); + sum_mb2.clear(); + sum_jacob0.clear(); + sum_jacob1.clear(); + sum_jacob2.clear(); + sum_delta.clear(); + return (OK); +} + +#ifdef SLNQ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_trivial_eqns(int rows, int cols, LDBLE * matrix) +/* ---------------------------------------------------------------------- */ +{ + int r, i, j; + r = rows; + if (rows == cols) + return (OK); + if (rows > cols) + return (ERROR); + for (i = 0; i < cols; i++) + { + for (j = 0; j < rows; j++) + { + if (matrix[j * (cols + 1) + i] != 0.0) + break; + } + if (j < rows) + continue; + for (j = 0; j < cols + 1; j++) + matrix[r * (cols + 1) + j] = 0.0; + matrix[r * (cols + 1) + i] = 1.0; + r++; + } + if (r == cols) + return (OK); + return (ERROR); +} +#endif +#define ZERO_TOL 1.0e-30 +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_root(LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_kb, LDBLE xcaq, LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE x0, y0, x1, y1, xb, miny; + +/* + * Bracket answer + */ + x0 = 0.0; + x1 = 0.0; + y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + miny = fabs(y0); + for (i = 1; i <= 10; i++) + { + x1 = (LDBLE) i / 10; + y1 = ss_f(x1, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + if (fabs(y1) < miny) + { + miny = fabs(y1); + } + if (y0 * y1 < 0) + { + break; + } + else + { + x0 = x1; + y0 = y1; + } + } +/* + * Interval halve + */ + if (i > 10) + { + xb = 0.0; + } + else + { + xb = ss_halve(l_a0, l_a1, x0, x1, l_kc, l_kb, xcaq, xbaq); + } + return (xb); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_halve(LDBLE l_a0, LDBLE l_a1, LDBLE x0, LDBLE x1, LDBLE l_kc, LDBLE l_kb, + LDBLE xcaq, LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE l_x, y0, dx, y; + + y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + dx = (x1 - x0); +/* + * Loop for interval halving + */ + for (i = 0; i < 100; i++) + { + dx *= 0.5; + l_x = x0 + dx; + y = ss_f(l_x, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + if (dx < 1e-8 || y == 0) + { + break; + } + if (y0 * y >= 0) + { + x0 = l_x; + y0 = y; + } + } + return (x0 + dx); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_f(LDBLE xb, LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_kb, LDBLE xcaq, + LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ +/* + * Need root of this function to determine xb + */ + LDBLE lb, lc, f, xc, r; + xc = 1 - xb; + if (xb == 0) + xb = 1e-20; + if (xc == 0) + xc = 1e-20; + lc = exp((l_a0 - l_a1 * (-4 * xb + 3)) * xb * xb); + lb = exp((l_a0 + l_a1 * (4 * xb - 1)) * xc * xc); + r = lc * l_kc / (lb * l_kb); + f = xcaq * (xb / r + xc) + xbaq * (xb + r * xc) - 1; + return (f); +} +//#define ORIGINAL +#ifdef ORIGINAL +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +numerical_jacobian(void) +/* ---------------------------------------------------------------------- */ +{ + std::vector base; + LDBLE d, d1, d2; + int i, j; + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (! + (numerical_deriv || + (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) || + (gas_phase_ptr != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + )) + return(OK); + + calculating_deriv = TRUE; + gammas(mu_x); + molalities(TRUE); + mb_sums(); + residuals(); +/* + * Clear array, note residuals are in array[i, count_unknowns+1] + */ + + for (i = 0; i < count_unknowns; i++) + { + my_array[i] = 0.0; + } + for (i = 1; i < count_unknowns; i++) + { + memcpy((void *) &(my_array[(size_t)i * (count_unknowns + 1)]), + (void *) &(my_array[0]), count_unknowns * sizeof(LDBLE)); + } + base.resize(count_unknowns); + base = residual; + d = 0.0001; + d1 = d * LOG_10; + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d * LOG_10; + break; + case MH: + s_eminus->la += d; + d2 = d * LOG_10; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d * LOG_10; + break; + case PITZER_GAMMA: + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + //mass_water_aq_x *= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //d2 = log(1.0 + d); + //break; + // DL_pitz + d1 = mass_water_aq_x * d; + mass_water_aq_x += d1; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x += d1; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = d1; + break; + case MU: + d2 = d * mu_x; + mu_x += d2; + gammas(mu_x); + break; + case PP: + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + d2 = -1e-8; + delta[i] = d2; + reset(); + d2 = delta[i]; + break; + case SS_MOLES: + if (x[i]->ss_in == FALSE) + continue; + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + /*d2 = -1e-8; */ + d2 = d * 10 * x[i]->moles; + //d2 = -.1 * x[i]->moles; + /* + if (d2 > -1e-10) d2 = -1e-10; + calculating_deriv = FALSE; + */ + delta[i] = d2; + /*fprintf (stderr, "delta before reset %e\n", delta[i]); */ + reset(); + d2 = delta[i]; + /*fprintf (stderr, "delta after reset %e\n", delta[i]); */ + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = (x[i]->moles > 1 ? 1 : 20); + d2 *= d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + } + molalities(TRUE); + mb_sums(); + /* + mb_ss(); + mb_gases(); + */ + residuals(); + //output_msg(sformatf( "%d\n", i)); + for (j = 0; j < count_unknowns; j++) + { + // avoid overflow + if (residual[j] > 1.0e101) + { + LDBLE t = (LDBLE) pow((LDBLE) 10.0, (LDBLE) (DBL_MAX_10_EXP - 50.0)); + if (residual[j] > t) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -pow(10.0, DBL_MAX_10_EXP - 50.0); + } + else + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2; + if (x[i]->type == MH2O) // DL_pitz + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x; + } + } + else if (residual[j] < -1.0e101) + { + LDBLE t = pow((LDBLE) 10.0, (LDBLE) (DBL_MIN_10_EXP + 50.0)); + if (residual[j] < -t) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = pow(10.0, DBL_MIN_10_EXP + 50.0); + } + else + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2; + if (x[i]->type == MH2O) // DL_pitz + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x; + } + } + else + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2; + if (x[i]->type == MH2O) // DL_pitz + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x; + if (!PHR_ISFINITE(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i])) + { + //fprintf(stderr, "oops, got NaN: %e, %e, %e, %e\n", residual[j], base[j], d2, array[j * (count_unknowns + 1) + i]); + } + } + + //output_msg(sformatf( "\t%d %e %e %e %e\n", j, array[j*(count_unknowns + 1) + i] , residual[j], base[j], d2)); + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0) + { + /*output_msg(sformatf( "Zero diagonal for MH\n")); */ + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = + under(s_h2->lm) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + //mass_water_aq_x /= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //break; + //DL_pitz + mass_water_aq_x -= d1; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x -= d1; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + gammas(mu_x); + break; + case PP: + delta[i] = -d2; + reset(); + break; + case SS_MOLES: + delta[i] = -d2; + reset(); + break; + case GAS_MOLES: + x[i]->moles -= d2; + break; + } + } + molalities(TRUE); + mb_sums(); + mb_gases(); + mb_ss(); + residuals(); + base.clear(); + calculating_deriv = FALSE; + return OK; +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +numerical_jacobian(void) +/* ---------------------------------------------------------------------- */ +{ + std::vector base; + LDBLE d, d1, d2; + int i, j; + cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + std::vector phase_ptrs; + std::vector base_phases; + double base_mass_water_bulk_x = 0, base_moles_h2o = 0; + cxxGasPhase base_gas_phase; + cxxSurface base_surface; + + if (! + (numerical_deriv || + (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) || + (gas_phase_ptr != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + )) + return(OK); + + //jacobian_sums(); + if (use.Get_surface_ptr() != NULL) + { + base_surface = *use.Get_surface_ptr(); + } + if (use.Get_gas_phase_ptr() != NULL) + { + //cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + base_gas_phase = *gas_phase_ptr; + base_phases.resize(gas_phase_ptr->Get_gas_comps().size()); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp* gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + class phase* phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE); + phase_ptrs.push_back(phase_ptr); + base_phases[i] = *phase_ptr; + } + } + calculating_deriv = TRUE; + gammas(mu_x); + molalities(TRUE); + mb_sums(); + //mb_gases(); + //mb_ss(); + residuals(); + /* + * Clear array, note residuals are in array[i, count_unknowns+1] + */ + + //for (i = 0; i < count_unknowns; i++) + //{ + // my_array[i] = 0.0; + //} + //for (i = 1; i < count_unknowns; i++) + //{ + // memcpy((void*)&(my_array[(size_t)i * (count_unknowns + 1)]), + // (void*)&(my_array[0]), count_unknowns * sizeof(LDBLE)); + //} + + base.resize(count_unknowns); + base = residual; + d = 0.0001; + d1 = d * LOG_10; + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d;// *LOG_10; + break; + case MH: + s_eminus->la += d; + d2 = d;// *LOG_10; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d;// *LOG_10; + break; + case PITZER_GAMMA: + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + //mass_water_aq_x *= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //d2 = log(1.0 + d); + //break; + // DL_pitz + d1 = mass_water_aq_x * d; + mass_water_aq_x += d1; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x += d1; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = d1; + break; + case MU: + d2 = d * mu_x; + mu_x += d2; + //gammas(mu_x); + break; + case PP: + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + d2 = -1e-8; + delta[i] = d2; + reset(); + d2 = delta[i]; + break; + case SS_MOLES: + if (x[i]->ss_in == FALSE) + continue; + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + /*d2 = -1e-8; */ + d2 = d * 10 * x[i]->moles; + //d2 = -.1 * x[i]->moles; + /* + if (d2 > -1e-10) d2 = -1e-10; + calculating_deriv = FALSE; + */ + delta[i] = d2; + /*fprintf (stderr, "delta before reset %e\n", delta[i]); */ + reset(); + d2 = delta[i]; + /*fprintf (stderr, "delta after reset %e\n", delta[i]); */ + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = (x[i]->moles > 1 ? 1 : 30); + d2 *= d * x[i]->moles; + d2 = (d2 < ineq_tol ? ineq_tol : d2); + //if (d2 < 1e-14) + // d2 = 1e-14; + x[i]->moles += d2; + break; + } + gammas(mu_x); + molalities(TRUE); + mb_sums(); + //mb_gases(); + //mb_ss(); + residuals(); + + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2; + if (x[i]->type == MH2O) // DL_pitz + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d2; + break; + case MH: + s_eminus->la -= d2; + if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0) + { + /*output_msg(sformatf( "Zero diagonal for MH\n")); */ + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = + under(s_h2->lm) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d2; + break; + case MH2O: + //mass_water_aq_x /= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //break; + //DL_pitz + mass_water_aq_x -= d2; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x -= d2; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //gammas(mu_x); + break; + case PP: + delta[i] = -d2; + reset(); + break; + case SS_MOLES: + delta[i] = -d2; + reset(); + break; + case GAS_MOLES: + x[i]->moles -= d2; + break; + } + if (use.Get_surface_ptr() != NULL) + { + *use.Get_surface_ptr() = base_surface; + } + if (use.Get_gas_phase_ptr() != NULL) + { + *use.Get_gas_phase_ptr() = base_gas_phase; + for (size_t g = 0; g < base_phases.size(); g++) + { + *phase_ptrs[g] = base_phases[g]; + } + } + //gammas(mu_x); + //molalities(TRUE); + //mb_sums(); + ////mb_gases(); + ////mb_ss(); + //residuals(); + } + gammas(mu_x); + molalities(TRUE); + mb_sums(); + //mb_gases(); + //mb_ss(); + residuals(); + //for (i = 0; i < count_unknowns; i++) + //{ + // //Debugging + // if (fabs(2.0 * (residual[i] - base[i]) / (residual[i] + base[i])) > 1e-2 && + // fabs(residual[i]) + fabs(base[i]) > 1e-8) + // { + // std::cerr << iterations << ": " << x[i]->description << " " << residual[i] << " " << base[i] << std::endl; + // } + //} + base.clear(); + calculating_deriv = FALSE; + return OK; +} +#endif +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_inert_moles(void) +/* ---------------------------------------------------------------------- */ +{ + int j; + if (use.Get_pp_assemblage_ptr() == NULL) return; + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_precipitate_only()) + { + x[j]->inert_moles = x[j]->moles; + x[j]->moles = 0; + } + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +unset_inert_moles() +/* ---------------------------------------------------------------------- */ +{ + int j; + if (use.Get_pp_assemblage_ptr() == NULL) return; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_precipitate_only()) + { + x[j]->moles += x[j]->inert_moles; + x[j]->inert_moles = 0; + } + } +} diff --git a/phreeqcpp/nvector.cpp b/phreeqcpp/nvector.cpp new file mode 100644 index 00000000..1da1a863 --- /dev/null +++ b/phreeqcpp/nvector.cpp @@ -0,0 +1,272 @@ +/************************************************************************** + * * + * File : nvector.c * + * Programmers : Radu Serban, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic NVECTOR * + * package. It contains the implementation of the N_Vector * + * kernels listed in nvector.h. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include "nvector.h" /* generic M_Env and N_Vector */ + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +N_Vector +N_VNew(integertype n, M_Env machEnv) +{ + N_Vector v_new; + v_new = machEnv->ops->nvnew(n, machEnv); + return (v_new); +} + +N_Vector_S +N_VNew_S(integertype ns, integertype n, M_Env machEnv) +{ + N_Vector_S vs_new; + vs_new = machEnv->ops->nvnewS(ns, n, machEnv); + return (vs_new); +} + +void +N_VFree(N_Vector v) +{ + v->menv->ops->nvfree(v); +} + +void +N_VFree_S(integertype ns, N_Vector_S vs) +{ + (*vs)->menv->ops->nvfreeS(ns, vs); +} + +N_Vector +N_VMake(integertype n, realtype * v_data, M_Env machEnv) +{ + N_Vector v_new; + v_new = machEnv->ops->nvmake(n, v_data, machEnv); + return (v_new); +} + +void +N_VDispose(N_Vector v) +{ + v->menv->ops->nvdispose(v); +} + +realtype * +N_VGetData(N_Vector v) +{ + realtype *data; + data = v->menv->ops->nvgetdata(v); + return (data); +} + +void +N_VSetData(realtype * v_data, N_Vector v) +{ + v->menv->ops->nvsetdata(v_data, v); +} + +void +N_VLinearSum(realtype a, N_Vector x, realtype b, N_Vector y, N_Vector z) +{ + z->menv->ops->nvlinearsum(a, x, b, y, z); +} + +void +N_VConst(realtype c, N_Vector z) +{ + z->menv->ops->nvconst(c, z); +} + +void +N_VProd(N_Vector x, N_Vector y, N_Vector z) +{ + z->menv->ops->nvprod(x, y, z); +} + +void +N_VDiv(N_Vector x, N_Vector y, N_Vector z) +{ + z->menv->ops->nvdiv(x, y, z); +} + +void +N_VScale(realtype c, N_Vector x, N_Vector z) +{ + z->menv->ops->nvscale(c, x, z); +} + +void +N_VAbs(N_Vector x, N_Vector z) +{ + z->menv->ops->nvabs(x, z); +} + +void +N_VInv(N_Vector x, N_Vector z) +{ + z->menv->ops->nvinv(x, z); +} + +void +N_VAddConst(N_Vector x, realtype b, N_Vector z) +{ + z->menv->ops->nvaddconst(x, b, z); +} + +realtype +N_VDotProd(N_Vector x, N_Vector y) +{ + realtype prod; + prod = y->menv->ops->nvdotprod(x, y); + return (prod); +} + +realtype +N_VMaxNorm(N_Vector x) +{ + realtype norm; + norm = x->menv->ops->nvmaxnorm(x); + return (norm); +} + +realtype +N_VWrmsNorm(N_Vector x, N_Vector w) +{ + realtype norm; + norm = x->menv->ops->nvwrmsnorm(x, w); + return (norm); +} + +realtype +N_VMin(N_Vector x) +{ + realtype minval; + minval = x->menv->ops->nvmin(x); + return (minval); +} + +realtype +N_VWL2Norm(N_Vector x, N_Vector w) +{ + realtype norm; + norm = x->menv->ops->nvwl2norm(x, w); + return (norm); +} + +realtype +N_VL1Norm(N_Vector x) +{ + realtype norm; + norm = x->menv->ops->nvl1norm(x); + return (norm); +} + +void +N_VOneMask(N_Vector x) +{ + x->menv->ops->nvonemask(x); +} + +void +N_VCompare(realtype c, N_Vector x, N_Vector z) +{ + z->menv->ops->nvcompare(c, x, z); +} + +booleantype +N_VInvTest(N_Vector x, N_Vector z) +{ + booleantype flag; + flag = z->menv->ops->nvinvtest(x, z); + return (flag); +} + +booleantype +N_VConstrProdPos(N_Vector c, N_Vector x) +{ + booleantype flag; + flag = x->menv->ops->nvconstrprodpos(c, x); + return (flag); +} + +booleantype +N_VConstrMask(N_Vector c, N_Vector x, N_Vector m) +{ + booleantype flag; + flag = x->menv->ops->nvconstrmask(c, x, m); + return (flag); +} + +realtype +N_VMinQuotient(N_Vector num, N_Vector denom) +{ + realtype quotient; + quotient = num->menv->ops->nvminquotient(num, denom); + return (quotient); +} + +void +N_VPrint(N_Vector x) +{ + x->menv->ops->nvprint(x); +} diff --git a/phreeqcpp/nvector.h b/phreeqcpp/nvector.h new file mode 100644 index 00000000..fa32f832 --- /dev/null +++ b/phreeqcpp/nvector.h @@ -0,0 +1,485 @@ +/************************************************************************** + * * + * File : nvector.h * + * Programmers : Radu Serban, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a generic NVECTOR package. * + * It defines the N_Vector and M_Env structures: * + * M_Env has an implementation-dependent 'content' field * + * which contains the data needed to generate a new * + * nvector in that implementation and an 'ops' filed * + * which is a structure listing operations acting on * + * such nvectors. * + * N_Vector has an implementation-dependent 'content' field * + * which contains the description and actual data of * + * the nvector and a 'menv' field which points to the * + * M_Env structure used in creating the nvector. * + * * + * Part I of this file contains type declarations for the * + * the following structures: _generic_M_Env, _generic_N_Vector, * + * and _generic_N_Vector_Ops, as well as references to pointers * + * to such structures (M_Env and N_Vector). * + * * + * Part II of this file contains the prototypes for the vector * + * kernels which operate on N_Vector. * + * * + * A particular implementation of an NVECTOR package must then * + * specify the 'content' fields of M_Env and N_Vector, define * + * the propotypes for kernel operations on those N_Vectors * + * (NOTE: kernel routine names must be unique to that * + * implementation), and finally provide an initialization * + * routine (which generates an M_Env with that particular * + * 'content' field and links the defined vector kernel routines * + * into the 'ops' field). * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +class Phreeqc; +#ifndef included_nvector_h +#define included_nvector_h + +#include "sundialstypes.h" /* definition of types */ + +/**************************************************************** + * Generic definitions of machine environment and N_Vector * + ****************************************************************/ + +/* Forward reference for pointer to N_Vector_Ops object */ + typedef struct _generic_N_Vector_Ops *N_Vector_Ops; + +/* Forward reference for pointer to M_Env object */ + typedef struct _generic_M_Env *M_Env; + +/* Forward reference for pointer to N_Vector object */ + typedef struct _generic_N_Vector *N_Vector; + +/* Define array of N_Vectors */ + typedef N_Vector *N_Vector_S; + +/* Structure containing function pointers to vector operations */ + struct _generic_N_Vector_Ops + { + N_Vector(*nvnew) (integertype, M_Env); + N_Vector_S(*nvnewS) (integertype, integertype, M_Env); + void (*nvfree) (N_Vector); + void (*nvfreeS) (integertype, N_Vector_S); + N_Vector(*nvmake) (integertype, realtype *, M_Env); + void (*nvdispose) (N_Vector); + realtype *(*nvgetdata) (N_Vector); + void (*nvsetdata) (realtype *, N_Vector); + void (*nvlinearsum) (realtype, N_Vector, realtype, N_Vector, + N_Vector); + void (*nvconst) (realtype, N_Vector); + void (*nvprod) (N_Vector, N_Vector, N_Vector); + void (*nvdiv) (N_Vector, N_Vector, N_Vector); + void (*nvscale) (realtype, N_Vector, N_Vector); + void (*nvabs) (N_Vector, N_Vector); + void (*nvinv) (N_Vector, N_Vector); + void (*nvaddconst) (N_Vector, realtype, N_Vector); + realtype(*nvdotprod) (N_Vector, N_Vector); + realtype(*nvmaxnorm) (N_Vector); + realtype(*nvwrmsnorm) (N_Vector, N_Vector); + realtype(*nvmin) (N_Vector); + realtype(*nvwl2norm) (N_Vector, N_Vector); + realtype(*nvl1norm) (N_Vector); + void (*nvonemask) (N_Vector); + void (*nvcompare) (realtype, N_Vector, N_Vector); + booleantype(*nvinvtest) (N_Vector, N_Vector); + booleantype(*nvconstrprodpos) (N_Vector, N_Vector); + booleantype(*nvconstrmask) (N_Vector, N_Vector, N_Vector); + realtype(*nvminquotient) (N_Vector, N_Vector); + void (*nvprint) (N_Vector); + }; + +/* A machine environment is a structure with an implementation + dependent 'content' representation (used to generate a new vector + in that implementation), a set of operations defined in the above + structure, and an ID tag */ + struct _generic_M_Env + { + void *content; + struct _generic_N_Vector_Ops *ops; + char tag[8]; + Phreeqc * phreeqc_ptr; + }; + +/* A vector is a structure with an implementation dependent content + representation and a pointer to the machine environment + corresponding to that implementation */ + struct _generic_N_Vector + { + void *content; + struct _generic_M_Env *menv; + }; + +/**************************************************************** + * Functions exported by nvector * + ****************************************************************/ + +/*--------------------------------------------------------------* + * Function : N_VNew * + * Usage : v = N_VNew(n, machEnv); * + *--------------------------------------------------------------* + * Returns a new N_Vector of length n. The parameter machEnv * + * is a pointer to machine environment-specific information. * + * If there is not enough memory for a new N_Vector, then * + * N_VNew returns NULL. * + *--------------------------------------------------------------*/ + + N_Vector N_VNew(integertype n, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VNew_S * + * Usage : v = N_VNew_S(ns, n, machEnv); * + *--------------------------------------------------------------* + * Returns an array of ns new N_Vectors of length n. The * + * parameter machEnv is a pointer to machine environment * + * specific information. * + * If there is not enough memory for a new array of N_Vectors * + * or for one of the components, then N_VNew_S returns NULL. * + *--------------------------------------------------------------*/ + + N_Vector_S N_VNew_S(integertype ns, integertype n, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VFree * + * Usage : N_VFree(v); * + *--------------------------------------------------------------* + * Frees the N_Vector v. It is illegal to use v after the call * + * N_VFree(v). * + *--------------------------------------------------------------*/ + + void N_VFree(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VFree_S * + * Usage : N_VFree_S(ns, vs); * + *--------------------------------------------------------------* + * Frees the array of ns N_Vectors vs. * + * It is illegal to use vs after the call N_VFree_S(Ns,vs). * + *--------------------------------------------------------------*/ + + void N_VFree_S(integertype ns, N_Vector_S vs); + +/*--------------------------------------------------------------* + * Function : N_VMake * + * Usage : v = N_VMake(n, v_data, machEnv); * + *--------------------------------------------------------------* + * Creates an N_Vector with component array data allocated by * + * the user. * + *--------------------------------------------------------------*/ + + N_Vector N_VMake(integertype n, realtype * v_data, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VDispose * + * Usage : N_VDispose(v); * + *--------------------------------------------------------------* + * Destroys an N_Vector with component array data allocated by * + * the user. * + *--------------------------------------------------------------*/ + + void N_VDispose(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VGetData * + * Usage : v_data = N_VGetData(v); * + *--------------------------------------------------------------* + * Extracts the data component array from the N_Vector v. * + * Note: this routine is used in the solver-specific interfaces * + * to the dense and banded linear solvers, as well as the * + * interfaces to the banded preconditioners provided with * + * SUNDIALS. It needs not be implemented by a user * + * defined NVECTOR module, if these linear solvers are not* + * used. * + *--------------------------------------------------------------*/ + + realtype *N_VGetData(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VSetData * + * Usage : N_VSetData(v_data, v); * + *--------------------------------------------------------------* + * Attaches the data component array v_data to the N_Vector v. * + * Note: this routine is used in the solver-specific interfaces * + * to the dense and banded linear solvers, as well as the * + * interfaces to the banded preconditioners provided with * + * SUNDIALS. It needs not be implemented by a user * + * defined NVECTOR module, if these linear solvers are not* + * used. * + *--------------------------------------------------------------*/ + + void N_VSetData(realtype * v_data, N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VLinearSum * + * Operation : z = a x + b y * + *--------------------------------------------------------------*/ + + void N_VLinearSum(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VConst * + * Operation : z[i] = c for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VConst(realtype c, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VProd * + * Operation : z[i] = x[i] * y[i] for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VProd(N_Vector x, N_Vector y, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VDiv * + * Operation : z[i] = x[i] / y[i] for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VDiv(N_Vector x, N_Vector y, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VScale * + * Operation : z = c x * + *--------------------------------------------------------------*/ + + void N_VScale(realtype c, N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VAbs * + * Operation : z[i] = |x[i]|, for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VAbs(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VInv * + * Operation : z[i] = 1.0 / x[i] for i = 0, 1, ..., N-1 * + *--------------------------------------------------------------* + * This routine does not check for division by 0. It should be * + * called only with an N_Vector x which is guaranteed to have * + * all non-zero components. * + *--------------------------------------------------------------*/ + + void N_VInv(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VAddConst * + * Operation : z[i] = x[i] + b for i = 0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VAddConst(N_Vector x, realtype b, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VDotProd * + * Usage : dotprod = N_VDotProd(x, y); * + *--------------------------------------------------------------* + * Returns the value of the ordinary dot product of x and y: * + * -> sum (i=0 to N-1) {x[i] * y[i]} * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VDotProd(N_Vector x, N_Vector y); + +/*--------------------------------------------------------------* + * Function : N_VMaxNorm * + * Usage : maxnorm = N_VMaxNorm(x); * + *--------------------------------------------------------------* + * Returns the maximum norm of x: * + * -> max (i=0 to N-1) |x[i]| * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VMaxNorm(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VWrmsNorm * + * Usage : wrmsnorm = N_VWrmsNorm(x, w); * + *--------------------------------------------------------------* + * Returns the weighted root mean square norm of x with * + * weight vector w: * + * -> sqrt [(sum (i=0 to N-1) {(x[i] * w[i])^2}) / N] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VWrmsNorm(N_Vector x, N_Vector w); + +/*--------------------------------------------------------------* + * Function : N_VMin * + * Usage : min = N_VMin(x); * + *--------------------------------------------------------------* + * Returns the smallest element of x: * + * -> min (i=0 to N-1) x[i] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VMin(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VWL2Norm * + * Usage : wl2norm = N_VWL2Norm(x, w); * + *--------------------------------------------------------------* + * Returns the weighted Euclidean L2 norm of x with * + * weight vector w: * + * -> sqrt [(sum (i=0 to N-1) {(x[i] * w[i])^2}) ] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VWL2Norm(N_Vector x, N_Vector w); + +/*--------------------------------------------------------------* + * Function : N_VL1Norm * + * Usage : l1norm = N_VL1Norm(x); * + *--------------------------------------------------------------* + * Returns the L1 norm of x: * + * -> sum (i=0 to N-1) {ABS(x[i])} * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VL1Norm(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VOneMask * + * Operation : x[i] = 1.0 if |x[i]| != 0. i = 0, 1, ..., N-1 * + * 0.0 otherwise * + *--------------------------------------------------------------*/ + + void N_VOneMask(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VCompare * + * Operation : z[i] = 1.0 if |x[i]| >= c i = 0, 1, ..., N-1 * + * 0.0 otherwise * + *--------------------------------------------------------------*/ + + void N_VCompare(realtype c, N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VInvTest * + * Operation : z[i] = 1.0 / x[i] with a test for x[i]==0.0 * + * before inverting x[i]. * + *--------------------------------------------------------------* + * This routine returns TRUE if all components of x are * + * non-zero (successful inversion) and returns FALSE * + * otherwise. * + *--------------------------------------------------------------*/ + + booleantype N_VInvTest(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VConstrProdPos * + * Usage : booltest = N_VConstrProdPos(c,x); * + *--------------------------------------------------------------* + * Returns a boolean equal to * + * FALSE if some c[i] != 0.0 and x[i]*c[i] <= 0.0, or * + * TRUE otherwise. * + * * + * This routine is used for constraint checking. * + *--------------------------------------------------------------*/ + + booleantype N_VConstrProdPos(N_Vector c, N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VConstrMask * + * Operation : m[i] = 1.0 if constraint test fails for x[i] * + * m[i] = 0.0 if constraint test passes for x[i] * + * where the constraint tests are as follows: * + * If c[i] = 2.0, then x[i] must be > 0.0. * + * If c[i] = 1.0, then x[i] must be >= 0.0. * + * If c[i] = -1.0, then x[i] must be <= 0.0. * + * If c[i] = -2.0, then x[i] must be < 0.0. * + *--------------------------------------------------------------* + * This routine returns a boolean FALSE if any element failed * + * the constraint test, TRUE if all passed. It also sets a * + * mask vector m, with elements equal to 1.0 where the * + * corresponding constraint test failed, and equal to 0.0 * + * where the constraint test passed. * + * This routine is specialized in that it is used only for * + * constraint checking. * + *--------------------------------------------------------------*/ + + booleantype N_VConstrMask(N_Vector c, N_Vector x, N_Vector m); + +/*--------------------------------------------------------------* + * Function : N_VMinQuotient * + * Operation : minq = min ( num[i]/denom[i]) over all i such * + * that denom[i] != 0. * + *--------------------------------------------------------------* + * This routine returns the minimum of the quotients obtained * + * by term-wise dividing num[i] by denom[i]. A zero element * + * in denom will be skipped. If no such quotients are found, * + * then the large value 1.e99 is returned. * + *--------------------------------------------------------------*/ + + realtype N_VMinQuotient(N_Vector num, N_Vector denom); + +/*--------------------------------------------------------------* + * Function : N_VPrint * + * Usage : N_VPrint(x); * + *--------------------------------------------------------------* + * Prints the N_Vector x to stdout. * + * This routine is provided as an aid in debugging code which * + * uses this vector package. * + *--------------------------------------------------------------*/ + + void N_VPrint(N_Vector x); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ diff --git a/phreeqcpp/nvector_serial.cpp b/phreeqcpp/nvector_serial.cpp new file mode 100644 index 00000000..761739f9 --- /dev/null +++ b/phreeqcpp/nvector_serial.cpp @@ -0,0 +1,1031 @@ +/************************************************************************** + * * + * File : nvector_serial.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, * + * Radu Serban, and Allan G. Taylor, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a serial implementation * + * of the NVECTOR package. It contains the implementation of * + * the serial machine environment intialization and free * + * routines (and of the Fortran callable interfaces to them) * + * and of the N_Vector kernels listed in nvector_serial.h. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#include +#include +#include +#include "nvector_serial.h" +#include "sundialstypes.h" +#include "sundialsmath.h" + +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define HALF RCONST(0.5) +#define ONE RCONST(1.0) +#define ONEPT5 RCONST(1.5) + + +/* Private Helper Prototypes */ +/* z=x */ +static void VCopy_Serial(N_Vector x, N_Vector z); +/* z=x+y */ +static void VSum_Serial(N_Vector x, N_Vector y, N_Vector z); +/* z=x-y */ +static void VDiff_Serial(N_Vector x, N_Vector y, N_Vector z); +/* z=-x */ +static void VNeg_Serial(N_Vector x, N_Vector z); +/* z=c(x+y) */ +static void VScaleSum_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z); +/* z=c(x-y) */ +static void VScaleDiff_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z); +/* z=ax+y */ +static void VLin1_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z); +/* z=ax-y */ +static void VLin2_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z); +/* y <- ax+y */ +static void Vaxpy_Serial(realtype a, N_Vector x, N_Vector y); +/* x <- ax */ +static void VScaleBy_Serial(realtype a, N_Vector x); + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/********************* Exported Functions ************************/ + +/* Serial implementation of the machine environment + initialization routine */ + +M_Env +M_EnvInit_Serial(integertype vec_length) +{ + M_Env me; + + /* Create machine environment structure */ + me = (M_Env) malloc(sizeof *me); + if (me == NULL) + return (NULL); + + /* Create serial content of machine environment structure */ + me->content = + (M_EnvSerialContent) malloc(sizeof(struct _M_EnvSerialContent)); + if (me->content == NULL) + { + free(me); + return (NULL); + } + + /* Load serial content of machine environment structure */ + ME_CONTENT_S(me)->length = vec_length; + + /* Attach vector operations */ + me->ops = (N_Vector_Ops) malloc(sizeof(struct _generic_N_Vector_Ops)); + if (me->ops == NULL) + { + free(me->content); + free(me); + return (NULL); + } + + me->ops->nvnew = N_VNew_Serial; + me->ops->nvnewS = N_VNew_S_Serial; + me->ops->nvfree = N_VFree_Serial; + me->ops->nvfreeS = N_VFree_S_Serial; + me->ops->nvmake = N_VMake_Serial; + me->ops->nvdispose = N_VDispose_Serial; + me->ops->nvgetdata = N_VGetData_Serial; + me->ops->nvsetdata = N_VSetData_Serial; + me->ops->nvlinearsum = N_VLinearSum_Serial; + me->ops->nvconst = N_VConst_Serial; + me->ops->nvprod = N_VProd_Serial; + me->ops->nvdiv = N_VDiv_Serial; + me->ops->nvscale = N_VScale_Serial; + me->ops->nvabs = N_VAbs_Serial; + me->ops->nvinv = N_VInv_Serial; + me->ops->nvaddconst = N_VAddConst_Serial; + me->ops->nvdotprod = N_VDotProd_Serial; + me->ops->nvmaxnorm = N_VMaxNorm_Serial; + me->ops->nvwrmsnorm = N_VWrmsNorm_Serial; + me->ops->nvmin = N_VMin_Serial; + me->ops->nvwl2norm = N_VWL2Norm_Serial; + me->ops->nvl1norm = N_VL1Norm_Serial; + me->ops->nvonemask = N_VOneMask_Serial; + me->ops->nvcompare = N_VCompare_Serial; + me->ops->nvinvtest = N_VInvTest_Serial; + me->ops->nvconstrprodpos = N_VConstrProdPos_Serial; + me->ops->nvconstrmask = N_VConstrMask_Serial; + me->ops->nvminquotient = N_VMinQuotient_Serial; + me->ops->nvprint = N_VPrint_Serial; + + /* Attach ID tag */ + strcpy(me->tag, ID_TAG_S); + + return (me); + +} + +/* Serial implementation of the machine environment + free routine */ + +void +M_EnvFree_Serial(M_Env machEnv) +{ + if (machEnv == NULL) + return; + + free(machEnv->content); + free(machEnv->ops); + free(machEnv); +} + +/***************************************************************************/ + +/* BEGIN implementation of vector operations */ + +N_Vector +N_VNew_Serial(integertype n, M_Env machEnv) +{ + N_Vector v; + integertype length; + + if (n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + v = (N_Vector) malloc(sizeof *v); + if (v == NULL) + return (NULL); + + v->content = + (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); + if (v->content == NULL) + { + free(v); + return (NULL); + } + + length = ME_CONTENT_S(machEnv)->length; + + NV_CONTENT_S(v)->data = (realtype *) malloc(length * sizeof(realtype)); + if (NV_CONTENT_S(v)->data == NULL) + { + free(v->content); + free(v); + return (NULL); + } + + NV_CONTENT_S(v)->length = length; + + v->menv = machEnv; + + return (v); +} + + +N_Vector_S +N_VNew_S_Serial(integertype ns, integertype n, M_Env machEnv) +{ + N_Vector_S vs; + integertype is, j; + + + if (ns <= 0 || n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + vs = (N_Vector_S) malloc(ns * sizeof(N_Vector *)); + if (vs == NULL) + return (NULL); + + for (is = 0; is < ns; is++) + { + vs[is] = N_VNew_Serial(n, machEnv); + if (vs[is] == NULL) + { + for (j = 0; j < is; j++) + N_VFree_Serial(vs[j]); + free(vs); + return (NULL); + } + } + + return (vs); +} + + +void +N_VFree_Serial(N_Vector v) +{ + free(NV_DATA_S(v)); + free(NV_CONTENT_S(v)); + free(v); +} + + +void +N_VFree_S_Serial(integertype ns, N_Vector_S vs) +{ + integertype is; + + for (is = 0; is < ns; is++) + N_VFree_Serial(vs[is]); + free(vs); +} + +N_Vector +N_VMake_Serial(integertype n, realtype * v_data, M_Env machEnv) +{ + N_Vector v; + integertype length; + + if (n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + v = (N_Vector) malloc(sizeof *v); + if (v == NULL) + return (NULL); + + v->content = + (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); + if (v->content == NULL) + { + free(v); + return (NULL); + } + + length = ME_CONTENT_S(machEnv)->length; + + NV_CONTENT_S(v)->data = v_data; + + NV_CONTENT_S(v)->length = length; + + v->menv = machEnv; + + return (v); +} + +void +N_VDispose_Serial(N_Vector v) +{ + free(NV_CONTENT_S(v)); + free(v); +} + +realtype * +N_VGetData_Serial(N_Vector v) +{ + realtype *v_data; + v_data = NV_CONTENT_S(v)->data; + return (v_data); +} + +void +N_VSetData_Serial(realtype * v_data, N_Vector v) +{ + NV_CONTENT_S(v)->data = v_data; +} + +void +N_VLinearSum_Serial(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z) +{ + integertype i, N; + realtype c, *xd, *yd, *zd; + N_Vector v1, v2; + booleantype test; + + if ((b == ONE) && (z == y)) + { /* BLAS usage: axpy y <- ax+y */ + Vaxpy_Serial(a, x, y); + return; + } + + if ((a == ONE) && (z == x)) + { /* BLAS usage: axpy x <- by+x */ + Vaxpy_Serial(b, y, x); + return; + } + + /* Case: a == b == 1.0 */ + + if ((a == ONE) && (b == ONE)) + { + VSum_Serial(x, y, z); + return; + } + + /* Cases: (1) a == 1.0, b = -1.0, (2) a == -1.0, b == 1.0 */ + + /*if ((test = ((a == ONE) && (b == -ONE))) || ((a == -ONE) && (b == ONE))) { */ + test = ((a == ONE) && (b == -ONE)); + if (test || ((a == -ONE) && (b == ONE))) + { + v1 = test ? y : x; + v2 = test ? x : y; + VDiff_Serial(v2, v1, z); + return; + } + + /* Cases: (1) a == 1.0, b == other or 0.0, (2) a == other or 0.0, b == 1.0 */ + /* if a or b is 0.0, then user should have called N_VScale */ + + /*if ((test = (a == ONE)) || (b == ONE)) { */ + test = (a == ONE); + if (test || (b == ONE)) + { + c = test ? b : a; + v1 = test ? y : x; + v2 = test ? x : y; + VLin1_Serial(c, v1, v2, z); + return; + } + + /* Cases: (1) a == -1.0, b != 1.0, (2) a != 1.0, b == -1.0 */ + + /*if ((test = (a == -ONE)) || (b == -ONE)) { */ + test = (a == -ONE); + if (test || (b == -ONE)) + { + c = test ? b : a; + v1 = test ? y : x; + v2 = test ? x : y; + VLin2_Serial(c, v1, v2, z); + return; + } + + /* Case: a == b */ + /* catches case both a and b are 0.0 - user should have called N_VConst */ + + if (a == b) + { + VScaleSum_Serial(a, x, y, z); + return; + } + + /* Case: a == -b */ + + if (a == -b) + { + VScaleDiff_Serial(a, x, y, z); + return; + } + + /* Do all cases not handled above: + (1) a == other, b == 0.0 - user should have called N_VScale + (2) a == 0.0, b == other - user should have called N_VScale + (3) a,b == other, a !=b, a != -b */ + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) + b * (*yd++); +} + + +void +N_VConst_Serial(realtype c, N_Vector z) +{ + integertype i, N; + realtype *zd; + + N = NV_LENGTH_S(z); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c; +} + + +void +N_VProd_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) * (*yd++); +} + + +void +N_VDiv_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) / (*yd++); +} + + +void +N_VScale_Serial(realtype c, N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + if (z == x) + { /* BLAS usage: scale x <- cx */ + VScaleBy_Serial(c, x); + return; + } + + if (c == ONE) + { + VCopy_Serial(x, z); + } + else if (c == -ONE) + { + VNeg_Serial(x, z); + } + else + { + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + for (i = 0; i < N; i++) + *zd++ = c * (*xd++); + } +} + + +void +N_VAbs_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++, xd++, zd++) + *zd = ABS(*xd); +} + + +void +N_VInv_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = ONE / (*xd++); +} + + +void +N_VAddConst_Serial(N_Vector x, realtype b, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) + b; +} + + +realtype +N_VDotProd_Serial(N_Vector x, N_Vector y) +{ + integertype i, N; + realtype sum = ZERO, *xd, *yd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + + for (i = 0; i < N; i++) + sum += (*xd++) * (*yd++); + + return (sum); +} + + +realtype +N_VMaxNorm_Serial(N_Vector x) +{ + integertype i, N; + realtype max = ZERO, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++, xd++) + { + if (ABS(*xd) > max) + max = ABS(*xd); + } + + return (max); +} + + +realtype +N_VWrmsNorm_Serial(N_Vector x, N_Vector w) +{ + integertype i, N; + realtype sum = ZERO, prodi, *xd, *wd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + wd = NV_DATA_S(w); + + for (i = 0; i < N; i++) + { + prodi = (*xd++) * (*wd++); + sum += prodi * prodi; + } + + return (RSqrt(sum / N)); +} + + +realtype +N_VMin_Serial(N_Vector x) +{ + integertype i, N; + realtype min, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + min = xd[0]; + + xd++; + for (i = 1; i < N; i++, xd++) + { + if ((*xd) < min) + min = *xd; + } + + return (min); +} + + +realtype +N_VWL2Norm_Serial(N_Vector x, N_Vector w) +{ + integertype i, N; + realtype sum = ZERO, prodi, *xd, *wd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + wd = NV_DATA_S(w); + + for (i = 0; i < N; i++) + { + prodi = (*xd++) * (*wd++); + sum += prodi * prodi; + } + + return (RSqrt(sum)); +} + + +realtype +N_VL1Norm_Serial(N_Vector x) +{ + integertype i, N; + realtype sum = ZERO, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++) + sum += ABS(xd[i]); + + return (sum); +} + + +void +N_VOneMask_Serial(N_Vector x) +{ + integertype i, N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++, xd++) + { + if (*xd != ZERO) + *xd = ONE; + } +} + + +void +N_VCompare_Serial(realtype c, N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++, xd++, zd++) + { + *zd = (ABS(*xd) >= c) ? ONE : ZERO; + } +} + + +booleantype +N_VInvTest_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + { + if (*xd == ZERO) + return (FALSE); + *zd++ = ONE / (*xd++); + } + + return (TRUE); +} + + +booleantype +N_VConstrProdPos_Serial(N_Vector c, N_Vector x) +{ + integertype i, N; + realtype *xd, *cd; + booleantype test; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + cd = NV_DATA_S(c); + + test = TRUE; + + for (i = 0; i < N; i++, xd++, cd++) + { + if (*cd != ZERO) + { + if ((*xd) * (*cd) <= ZERO) + { + test = FALSE; + break; + } + } + } + return (test); +} + + +booleantype +N_VConstrMask_Serial(N_Vector c, N_Vector x, N_Vector m) +{ + integertype i, N; + booleantype test; + realtype *cd, *xd, *md; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + cd = NV_DATA_S(c); + md = NV_DATA_S(m); + + test = TRUE; + + for (i = 0; i < N; i++, cd++, xd++, md++) + { + *md = ZERO; + if (*cd == ZERO) + continue; + if (*cd > ONEPT5 || (*cd) < -ONEPT5) + { + if ((*xd) * (*cd) <= ZERO) + { + test = FALSE; + *md = ONE; + } + continue; + } + if ((*cd) > HALF || (*cd) < -HALF) + { + if ((*xd) * (*cd) < ZERO) + { + test = FALSE; + *md = ONE; + } + } + } + return (test); +} + + +realtype +N_VMinQuotient_Serial(N_Vector num, N_Vector denom) +{ + booleantype notEvenOnce; + integertype i, N; + realtype *nd, *dd, min; + + N = NV_LENGTH_S(num); + nd = NV_DATA_S(num); + dd = NV_DATA_S(denom); + min = 0; + + notEvenOnce = TRUE; + + for (i = 0; i < N; i++, nd++, dd++) + { + if (*dd == ZERO) + continue; + else + { + if (notEvenOnce) + { + min = *nd / *dd; + notEvenOnce = FALSE; + } + else + min = MIN(min, (*nd) / (*dd)); + } + } + if (notEvenOnce) + min = 1.e99; + + return (min); +} + + +void +N_VPrint_Serial(N_Vector x) +{ + integertype N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + +#if !defined(R_SO) + for (integertype i = 0; i < N; i++) + printf("%11.8g\n", (double) (*xd++)); + + printf("\n"); +#endif +} + + +/***************** Private Helper Functions **********************/ + + +static void +VCopy_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = *xd++; +} + + +static void +VSum_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) + (*yd++); +} + + +static void +VDiff_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) - (*yd++); +} + + +static void +VNeg_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = -(*xd++); +} + + +static void +VScaleSum_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c * ((*xd++) + (*yd++)); +} + + +static void +VScaleDiff_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c * ((*xd++) - (*yd++)); +} + + +static void +VLin1_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) + (*yd++); +} + + +static void +VLin2_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) - (*yd++); +} + +static void +Vaxpy_Serial(realtype a, N_Vector x, N_Vector y) +{ + integertype i, N; + realtype *xd, *yd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + + if (a == ONE) + { + for (i = 0; i < N; i++) + *yd++ += (*xd++); + return; + } + + if (a == -ONE) + { + for (i = 0; i < N; i++) + *yd++ -= (*xd++); + return; + } + + for (i = 0; i < N; i++) + *yd++ += a * (*xd++); +} + +static void +VScaleBy_Serial(realtype a, N_Vector x) +{ + integertype i, N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++) + *xd++ *= a; +} diff --git a/phreeqcpp/nvector_serial.h b/phreeqcpp/nvector_serial.h new file mode 100644 index 00000000..8a5ecb90 --- /dev/null +++ b/phreeqcpp/nvector_serial.h @@ -0,0 +1,369 @@ +#ifndef _INC_NVECTOR_SERIAL_H +#define _INC_NVECTOR_SERIAL_H +/************************************************************************** + * * + * File : nvector_serial.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, * + * : Radu Serban, and Allan G. Taylor, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a serial implementation of the * + * NVECTOR package. * + * * + * Part I of this file contains declarations which are specific * + * to the particular machine environment in which this version * + * of the vector package is to be used. This includes the * + * typedef for the 'content' fields of the structures M_Env and * + * N_Vector (M_EnvSerialContent and N_VectorSerialContent, * + * respectively). * + * * + * Part II of this file defines accessor macros that allow the * + * user to use efficiently the type N_Vector without making * + * explicit references to its underlying representation. * + * * + * Part III of this file contains the prototype for the * + * initialization routine specific to this implementation * + * (M_EnvInit_Serial) as well as prototypes for the vector * + * kernels which operate on the serial N_Vector. These * + * prototypes are unique to this particular implementation of * + * the vector package. * + * * + * NOTES: * + * * + * The definitions of the generic M_Env and N_Vector structures * + * are in the header file nvector.h. * + * * + * The definitions of the types realtype and integertype are in * + * the header file sundialstypes.h and these may be changed * + * according to the user's needs. The sundialstypes.h file also * + * contains the definition for the type booleantype. * + * * + * N_Vector arguments to arithmetic kernels need not be * + * distinct. Thus, for example, the call * + * N_VLinearSum_Serial(a,x,b,y,y); y <- ax+by * + * is legal. * + * * + * This version of nvector is for the ordinary sequential * + * machine environment. In the documentation given below, N is * + * the length of all N_Vector parameters and x[i] denotes the * + * ith component of the N_Vector x, where 0 <= i <= N-1. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef included_nvector_serial_h +#define included_nvector_serial_h + +#include "nvector.h" /* Generic M_Env and N_Vector type definitions */ +#include "sundialstypes.h" + + +/**************************************************************** + * PART I: * + * Serial implementaion of M_Env and N_Vector * + ****************************************************************/ + +/* The serial implementation of the machine environment has + ID tag 'serial' */ +#define ID_TAG_S "serial" + +/* The serial implementation of the machine environment 'content' + structure contains the length of vectors */ + + struct _M_EnvSerialContent + { + integertype length; + }; + + typedef struct _M_EnvSerialContent *M_EnvSerialContent; + +/* The serial implementation of the N_Vector 'content' + structure contains the length of the vector and a pointer + to an array of realtype components */ + + struct _N_VectorSerialContent + { + integertype length; + realtype *data; + }; + + typedef struct _N_VectorSerialContent *N_VectorSerialContent; + +/**************************************************************** + * * + * PART II: Macros * + * NV_MAKE_S, NV_DISPOSE_S, NVS_MAKE_S, NVS_DISPOSE_S * + * ME_CONTENT_S, NV_CONTENT_S * + * NV_DATA_S, NV_LENGTH_S, NV_Ith_S * + *--------------------------------------------------------------* + * In the descriptions below, the following user declarations * + * are assumed: * + * * + * M_Env machenv; * + * N_Vector v, *vs; * + * realtype *v_data, **vs_data, r; * + * integertype v_len, s_len, i; * + * * + * (1) NV_MAKE_S, NV_DISPOSE_S * + * * + * These companion routines are used to create and * + * destroy an N_Vector with a component array v_data * + * allocated by the user. * + * * + * The call NV_MAKE_S(v, v_data, machenv) makes v an * + * N_Vector with component array v_data. The length of the * + * array is taken from machenv. * + * NV_MAKE_S stores the pointer v_data so that changes * + * made by the user to the elements of v_data are * + * simultaneously reflected in v. There is no copying of * + * elements. * + * * + * The call NV_DISPOSE_S(v) frees all memory associated * + * with v except for its component array. This memory was * + * allocated by the user and, therefore, should be * + * deallocated by the user. * + * * + * (2) NVS_MAKE_S, NVS_DISPOSE_S * + * * + * These companion routines are used to create and destroy * + * an array of N_Vectors with component vs_data allocated * + * by the user. * + * * + * The call NVS_MAKE_S(vs, vs_data, s_len, machenv) makes * + * vs an array of s_len N_Vectors, each with component * + * array vs_data[i] and array length taken from machenv. * + * NVS_MAKE_S stores the pointers vs_data[i] so that * + * changes made by the user to the elements of vs_data are * + * simultaneously reflected in vs. There is no copying of * + * elements. * + * * + * The call NVS_DISPOSE_S(vs) frees all memory associated * + * with vs except for its components' component array. * + * This memory was allocated by the user and, therefore, * + * should be deallocated by the user. * + * * + * (3) ME_CONTENT_S, NV_CONTENT_S * + * * + * These routines give access to the contents of the serial * + * machine environment and N_Vector, respectively. * + * * + * The assignment m_cont = ME_CONTENT_S(machenv) sets * + * m_cont to be a pointer to the serial machine * + * environment content structure. * + * * + * The assignment v_cont = NV_CONTENT_S(v) sets * + * v_cont to be a pointer to the serial N_Vector content * + * structure. * + * * + * (4) NV_DATA_S, NV_LENGTH_S * + * * + * These routines give individual access to the parts of * + * the content of a serial N_Vector. * + * * + * The assignment v_data=NV_DATA_S(v) sets v_data to be * + * a pointer to the first component of v. The assignment * + * NV_DATA_S(v)=v_data sets the component array of v to * + * be v_data by storing the pointer v_data. * + * * + * The assignment v_len=NV_LENGTH_S(v) sets v_len to be * + * the length of v. The call NV_LENGTH_S(v)=len_v sets * + * the length of v to be len_v. * + * * + * (5) NV_Ith_S * + * * + * In the following description, the components of an * + * N_Vector are numbered 0..N-1, where N is the length of * + * v. * + * * + * The assignment r=NV_Ith_S(v,i) sets r to be the value of * + * the ith component of v. The assignment NV_Ith_S(v,i)=r * + * sets the value of the ith component of v to be r. * + * * + * Notes.. * + * * + * Users who use the macros (1) and/or (2) must * + * #include since these macros expand to calls to * + * malloc and free. * + * * + * When looping over the components of an N_Vector v, it is * + * more efficient to first obtain the component array via * + * v_data=NV_DATA_S(v) and then access v_data[i] within the * + * loop than it is to use NV_Ith_S(v,i) within the loop. * + * * + * NV_MAKE_S and NV_DISPOSE_S are similar to N_VNew_Serial and * + * N_VFree_Serial, while NVS_MAKE_S and NVS_DISPOSE_S are * + * similar to N_VNew_S_Serial and N_VFree_S_Serial. The * + * difference is one of responsibility for component memory * + * allocation and deallocation. N_VNew_Serial allocates memory * + * for the N_Vector components and N_VFree_Serial frees the * + * component memory allocated by N_VNew_Serial. For NV_MAKE_S * + * and NV_DISPOSE_S, the component memory is allocated and * + * freed by the user of this package. Similar remarks hold for * + * NVS_MAKE_S, NVS_DISPOSE_S and N_VNew_S_Serial, * + * N_VFree_S_Serial. * + * * + ****************************************************************/ + +#define NV_MAKE_S(v, v_data, machenv) \ + v = (N_Vector) malloc(sizeof(*v)); \ + v->content = (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); \ + v->content->data = v_data; \ + v->content->length = machenv->content->v_len; \ + v->menv = machenv + +#define NV_DISPOSE_S(v) \ + PHRQ_free((N_VectorSerialContent)(v->content)); \ + PHRQ_free(v) + +#define NVS_MAKE_S(vs, vs_data, s_len, machenv) \ + vs = (N_Vector_S) malloc(s_len*sizeof(N_Vector *)); \ + for ((int)is=0; iscontent) ) + +#define NV_CONTENT_S(v) ( (N_VectorSerialContent)(v->content) ) + +#define NV_LENGTH_S(v) ( NV_CONTENT_S(v)->length ) + +#define NV_DATA_S(v) ( NV_CONTENT_S(v)->data ) + +#define NV_Ith_S(v,i) ( NV_DATA_S(v)[i] ) + + +/**************************************************************** + * PART III: * + * Functions exported by nvector_serial * + ****************************************************************/ + +/*--------------------------------------------------------------* + * Routine : M_EnvInit_Serial * + *--------------------------------------------------------------* + * This function sets the content field of the machine * + * environment for the serial implementation to a structure of * + * type _MEnvSerialContent and attaches the vector operations * + * defined for this implementation. * + * * + * If successful, M_EnvInit_Serial returns a pointer of type * + * M_Env. This pointer should in turn be passed in any user * + * calls to N_VNew, or uses of the macros NV_MAKE_S and * + * NVS_MAKE_S. * + * * + *--------------------------------------------------------------* + * * + * vec_length is the length of the vector. * + * * + *--------------------------------------------------------------*/ + + M_Env M_EnvInit_Serial(integertype vec_length); + +/*--------------------------------------------------------------* + * Function M_EnvFree_Serial * + *--------------------------------------------------------------* + * Function to free the block of machine-dependent environment * + * information created by M_EnvInit_Serial. * + * Its only argument is the pointer machenv returned by * + * M_EnvInit_Serial. * + * * + *--------------------------------------------------------------*/ + + void M_EnvFree_Serial(M_Env machenv); + +/*--------------------------------------------------------------* + * Serial implementations of the vector operations * + * * + * For a complete description of each of the following routines * + * see the header file nvector.h * + *--------------------------------------------------------------*/ + + N_Vector N_VNew_Serial(integertype n, M_Env machEnv); + N_Vector_S N_VNew_S_Serial(integertype ns, integertype n, M_Env machEnv); + void N_VFree_Serial(N_Vector v); + void N_VFree_S_Serial(integertype ns, N_Vector_S vs); + N_Vector N_VMake_Serial(integertype n, realtype * v_data, M_Env machEnv); + void N_VDispose_Serial(N_Vector v); + realtype *N_VGetData_Serial(N_Vector v); + void N_VSetData_Serial(realtype * v_data, N_Vector v); + void N_VLinearSum_Serial(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z); + void N_VConst_Serial(realtype c, N_Vector z); + void N_VProd_Serial(N_Vector x, N_Vector y, N_Vector z); + void N_VDiv_Serial(N_Vector x, N_Vector y, N_Vector z); + void N_VScale_Serial(realtype c, N_Vector x, N_Vector z); + void N_VAbs_Serial(N_Vector x, N_Vector z); + void N_VInv_Serial(N_Vector x, N_Vector z); + void N_VAddConst_Serial(N_Vector x, realtype b, N_Vector z); + realtype N_VDotProd_Serial(N_Vector x, N_Vector y); + realtype N_VMaxNorm_Serial(N_Vector x); + realtype N_VWrmsNorm_Serial(N_Vector x, N_Vector w); + realtype N_VMin_Serial(N_Vector x); + realtype N_VWL2Norm_Serial(N_Vector x, N_Vector w); + realtype N_VL1Norm_Serial(N_Vector x); + void N_VOneMask_Serial(N_Vector x); + void N_VCompare_Serial(realtype c, N_Vector x, N_Vector z); + booleantype N_VInvTest_Serial(N_Vector x, N_Vector z); + booleantype N_VConstrProdPos_Serial(N_Vector c, N_Vector x); + booleantype N_VConstrMask_Serial(N_Vector c, N_Vector x, N_Vector m); + realtype N_VMinQuotient_Serial(N_Vector num, N_Vector denom); + void N_VPrint_Serial(N_Vector x); + + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_NVECTOR_SERIAL_H */ diff --git a/phreeqcpp/parse.cpp b/phreeqcpp/parse.cpp new file mode 100644 index 00000000..82eb5400 --- /dev/null +++ b/phreeqcpp/parse.cpp @@ -0,0 +1,1043 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +parse_eq(char* eqn, std::vector& new_elt_list, int association) +/* ---------------------------------------------------------------------- */ +/* + * function to break equation up into component species + * returns species name, coefficient, and charge in global variable "rxn_list". + * Also returns pointer to elt_list structure array with list of elements + * and coefficients in species. + * rxn_list global variable, output with contiguous rxn_list structures, + * which is the reaction. Target species is first in + * list, coefficient is -1.0. + * + * Argurments: + * *eqn input, pointer to equation to be parsed. + * **elt_ptr output, pointer to contiguous elt_list structures, + * which is list of elements and coefficients in the + * target species. + * association input, TRUE or FALSE if reaction is an association reaction + */ +{ + int i; + LDBLE coef, l_z; + char c; + const char* cptr; + char token[MAX_LENGTH]; + + paren_count = 0; + /* + * Remove white space + */ + squeeze_white(eqn); + /* + * Check for illegal characters + */ + for (i = 0; (c = eqn[i]) != '\0'; i++) + { + if (islegit(c) == FALSE) + { + error_string = sformatf("Character is not allowed,\ + %c (octal: %o).", c, c); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + + /* + * Find coefficients, name, and charge for each species for lhs + */ + count_trxn = 0; + trxn.dz[0] = trxn.dz[1] = trxn.dz[2] = 0.0; + cptr = eqn; + c = cptr[0]; + for (;;) + { + if (c == '=') + break; + if (c == '\0') + { + error_string = sformatf("Equation has no equal sign.\n\t%s", eqn); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (get_species(&cptr) == ERROR) + { + return (ERROR); + } + c = cptr[0]; + if (association == FALSE) + { + trxn.token[count_trxn].coef *= -1.0; + } + count_trxn++; + } + /* + * Get coefficient, name, and charge of species for dissociation reaction + */ + cptr++; + if (association == TRUE) + { + if (get_species(&cptr) == ERROR) + { + return (ERROR); + } + trxn.token[count_trxn].coef *= -1.0; + /* Swap species into first structure position */ + const char* char_ptr = trxn.token[0].name; + coef = trxn.token[0].coef; + l_z = trxn.token[0].z; + trxn.token[0].name = trxn.token[count_trxn].name; + trxn.token[0].coef = trxn.token[count_trxn].coef; + trxn.token[0].z = trxn.token[count_trxn].z; + trxn.token[count_trxn].name = char_ptr; + trxn.token[count_trxn].coef = coef; + trxn.token[count_trxn].z = l_z; + count_trxn++; + } + /* + * Get reaction species from rhs of equation + */ + c = cptr[0]; + for (;;) + { + if (c == '\0') + break; + if (get_species(&cptr) == ERROR) + { + return (ERROR); + } + c = cptr[0]; + if (association == TRUE) + { + trxn.token[count_trxn].coef *= -1.0; + } + count_trxn++; + } + /* + * Sort list of reaction species + */ + trxn_sort(); + /* + * Get elements in species or mineral formula + */ + count_elts = 0; + strcpy(token, trxn.token[0].name); + replace("(s)", "", token); + replace("(S)", "", token); + replace("(g)", "", token); + replace("(G)", "", token); + const char* char_ptr = token; + + if (get_elts_in_species(&char_ptr, trxn.token[0].coef) == ERROR) + { + return (ERROR); + } + /* + * Sort elements in reaction and combine + */ + if (elt_list_combine() == ERROR) + return (ERROR); + /* + * Malloc space and store element data for return + */ + new_elt_list.resize(count_elts + 1); + for (i = 0; i < count_elts; i++) + { + new_elt_list[i].elt = elt_list[i].elt; + new_elt_list[i].coef = -elt_list[i].coef; + } + new_elt_list[count_elts].elt = NULL; + + /* + * Debugging print of parsed equation + trxn_print(); + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_eqn(int association) +/* ---------------------------------------------------------------------- */ +/* + * Check that equation is balanced reaction. Uses array "trxn.token" and + * assumes "count_trxn" is set to number of species in reaction. + * Charge and elements are checked. + * + * Arguments: + * + * association input, TRUE or FALSE if reaction is an association reaction. + */ +{ + int i; + int oops = 0; + LDBLE sumcharge; + + paren_count = 0; + count_elts = 0; +/* + * Check that coefficient of first species is -1.0 + */ + if (equal(trxn.token[0].coef, -1.0, TOL) == FALSE) + { + if (association == TRUE) + { + error_string = sformatf( + "Coefficient of first species on rhs is not equal to 1.0."); + error_msg(error_string, CONTINUE); + } + else + { + error_string = sformatf( + "Coefficient of mineral (first on lhs) is not equal to 1.0."); + error_msg(error_string, CONTINUE); + } + return (ERROR); + } +/* + * Go through all species in the reaction; sum the charge and store elements + */ + sumcharge = 0.0; + for (i = 0; i < count_trxn; i++) + { + sumcharge += (trxn.token[i].coef) * (trxn.token[i].z); + const char* t_ptr = trxn.token[i].name; + if (get_elts_in_species(&t_ptr, trxn.token[i].coef) == ERROR) + { + return (ERROR); + } + } +/* + * Sort elements in reaction and combine + */ + if (elt_list_combine() == ERROR) + return (ERROR); +/* + * Check charge + */ + if (equal(sumcharge, 0.0, TOL) == FALSE) + { + error_string = sformatf( "Equation is not charge balanced, right - left = %7.4f moles charge", sumcharge); + error_msg(error_string, CONTINUE); + oops++; + } +/* + * Check mass balance + */ + for (i = 0; i < count_elts; i++) + { + if ((equal(elt_list[i].coef, 0.0, TOL) == FALSE) && + strncmp((elt_list[i].elt)->name, "e", MAX_LENGTH) != 0) + { + error_string = sformatf( + "Equation does not balance for element, %s: right - left = %7.4f moles", + (elt_list[i].elt)->name, elt_list[i].coef); + error_msg(error_string, CONTINUE); + oops++; + } + } + if (oops == 0) + { + return (OK); + } + else + { + return (ERROR); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_charge(char *charge, LDBLE * l_z) +/* ---------------------------------------------------------------------- */ +/* + * Function takes character string and calculates the charge on + * the species. Charge can be in two forms: (1) string of "+" or "-" + * or (2) + or - followed by an integer. Charge is reduced to form (2) + * and stored in the pointer location *charge. + * + * Arguments: + * *charge input, string containing charge + * output, string containing charge of the form + or - + * followed by an integer, if integer greater than 1. + * *z output, value of charge. + * + * Returns: + * ERROR, + * OK. + */ +{ + int i; + char* ptr; + char c, c1; +/* + * Charge is zero + */ + if ((c = charge[0]) == '\0') + { + *l_z = 0.0; + return (OK); + } +/* + * Error check for + or - at start of string + */ + if (c != '+' && c != '-') + { + error_string = sformatf( + "Character string for charge does not start with + or -,\t%s.", + charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } +/* + * Count string of +'s or -'s + */ + i = 0; + while (c == (c1 = charge[i++])); + i--; + if (c1 == '\0') + { + if (c == '-') + i = -i; + } + else + { +/* + * + or - followed by a number + */ + errno = 0; + i = strtol(charge, &ptr, 0); +/* + * Truncate fractional part of charge if all zeros + */ + if (*ptr != '\0') + { + if (*ptr == '.') + { + while (*(++ptr) != '\0') + { + if (*ptr != '0') + { + char* ptr; + *l_z = strtod(charge, &ptr); + return (OK); + } + } +/* + * Non-numeric characters + */ + } + else + { + error_string = sformatf( + "Error in character string for charge, %s.", charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + } +/* + * Charge is zero, must have had +0 or -0 in eqn + */ + if (i == 0) + { + charge[0] = '\0'; + } +/* + * Charge is +1 or -1, single + or - + */ + if (abs(i) == 1) + { + charge[0] = c; + charge[1] = '\0'; + } +/* + * Abs(z)>1, set charge to + or - plus integer + */ + if (abs(i) > 1) + { + if (sprintf(charge, "%-+d", i) == EOF) + { + error_string = sformatf( + "Error converting charge to character string, %s.", + charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + *l_z = i; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_coef(LDBLE * coef, const char **eqnaddr) +/* ---------------------------------------------------------------------- */ +/* + * Function reads through eqn and determines the coefficient of the next + * species. + * + * Arguments: + * *coef output, coefficient of next species. + * + * **eqnaddr input, pointer to a position in eqn to start parsing + * output, pointer to next position after coefficient + * + * Returns: + * ERROR, + * OK. + */ +{ + int i; + char c, c1; + const char* cptr; + const char* rest; + char* ptr1; + char token[MAX_LENGTH];; + + rest = *eqnaddr; + cptr = *eqnaddr; /* address of a position in eqn */ + c = *cptr; /* character in eqn */ + *coef = 0.0; +/* + * No leading sign or number + */ + if (isalpha((int) c) || + (c == '(') || (c == ')') || (c == '[') || (c == ']')) + { + *coef = 1.0; + return (OK); + } +/* + * Leading +, no digits + */ + c1 = *(cptr + 1); + if (c == '+' && + (isalpha((int) c1) || + (c1 == '(') || (c1 == ')') || (c1 == '[') || (c1 == ']'))) + { + *eqnaddr = ++cptr; + *coef = 1.0; + return (OK); + } +/* + * Leading -, no digits + */ + if (c == '-' && + (isalpha((int) c1) || + (c1 == '(') || (c1 == ')') || (c1 == '[') || (c1 == ']'))) + { + *eqnaddr = ++cptr; + *coef = -1.0; + return (OK); + } + i = 0; +/* + * Has number coefficient + */ + if (isdigit((int) c) || c == '+' || c == '-' || c == '.') + { + while (isdigit((int) c) || c == '+' || c == '-' || c == '.') + { + token[i++] = c; + if (i >= MAX_LENGTH) + { + error_string = sformatf( + "Coefficient has more than MAX_LENGTH characters."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + c = *(++cptr); + } + token[i] = '\0'; + *eqnaddr = cptr; + errno = 0; + *coef = strtod(token, &ptr1); + if ((errno == ERANGE) || (*ptr1 != '\0')) + { + error_string = sformatf( + "Error converting coefficient in get_coef, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); + } +/* + * None of the above, unknown construct + */ + error_string = sformatf( + "Illegal equation construct detected in get_coef.\n\t%s.", rest); + error_msg(error_string, CONTINUE); + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_elt(const char** t_ptr, std::string& element, int* i) +/* ---------------------------------------------------------------------- */ +/* + * Function reads an element name out of the equation string. + * An element name is composed of a capital letter followed by any number + * of lower case characters. + * + * Arguments: + * **t_ptr input, points to position in the equation to begin + * output, points to next character of equation after + * element name. + * *element input pointer to place to return element character string + */ +{ + char c; + + element.clear(); + c = *(*t_ptr)++; + if (c == '\0') + { + error_string = sformatf( + "Empty string in get_elt. Expected an element name."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + /* + * Load name into char array element + */ + element.push_back(c); + *i = 1; + if (c == '[') + { + while ((c = (**t_ptr)) != ']') + { + element.push_back(c); + (*i)++; + (*t_ptr)++; + if ((c = (**t_ptr)) == ']') + { + element.push_back(c); + (*i)++; + (*t_ptr)++; + break; + } + else if (**t_ptr == '\0') + { + error_msg("No ending bracket (]) for element name", CONTINUE); + input_error++; + break; + } + } + while (islower((int)(c = (**t_ptr))) || c == '_') + { + element.push_back(c); + (*i)++; + (*t_ptr)++; + } + } + else + { + while (islower((int)(c = (**t_ptr))) || c == '_') + { + element.push_back(c); + (*i)++; + (*t_ptr)++; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_elts_in_species(const char **t_ptr, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int l; + size_t count; + char c, c1; + LDBLE d; + std::string element; + const char** t_ptr_save = t_ptr; + while (((c = **t_ptr) != '+') && (c != '-') && (c != '\0')) + { + /* close parenthesis */ + if (c == ')') + { + paren_count--; + if (paren_count < 0) + { + error_string = sformatf( "Too many right parentheses."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + (*t_ptr)++; + return (OK); + } + c1 = *((*t_ptr) + 1); + /* beginning of element name */ + if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '[')) + { +/* + * Get new element and subscript + */ + if (get_elt(t_ptr, element, &l) == ERROR) + { + return (ERROR); + } + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + elt_list[count_elts].elt = element_store(element.c_str()); + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].coef = d * coef; + count_elts++; +/* + * Expand working space for elements if necessary + */ + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + continue; + } +/* + * Open parentheses + */ + if (c == '(') + { + count = count_elts; + if (c1 == ')') + { + error_string = sformatf( "Empty parentheses."); + warning_msg(error_string); + } + paren_count++; + (*t_ptr)++; + if (get_elts_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + for (size_t i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Colon + */ + if (c == ':') + { + count = count_elts; + (*t_ptr)++; + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + if (get_elts_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + for (size_t i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Not beginning of element and not opening paren + */ + error_string = sformatf( + "Parsing error in get_elts_in_species, unexpected character, %c.", + c); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + if (paren_count != 0) + { + error_string = sformatf( "Unbalanced parentheses: %s", *t_ptr_save); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +get_secondary(const char **t_ptr, char *element, int *i) +/* ---------------------------------------------------------------------- */ +/* + * Function reads an element name out of the equation string. + * An element name is composed of a capital letter followed by any number + * of lower case characters. + * + * Arguments: + * **t_ptr input, points to position in the equation to begin + * output, points to next character of equation after + * element name. + * *element input pointer to place to return element character string + */ +{ + int j; + char c; + const char* cptr; + + c = *(*t_ptr)++; + if (c == '\0') + { + error_string = sformatf( + "Empty string in get_elt. Expected an element name."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Load name into char array element + */ + element[0] = c; + *i = 1; + if (c == '[') + { + while ((c = (**t_ptr)) != ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + if ((c = (**t_ptr)) == ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + c = (**t_ptr); + break; + } + else if ((c = (**t_ptr)) == '\0') + { + error_msg("Did not find ending bracket (])", CONTINUE); + input_error++; + return (ERROR); + } + } + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + else + { + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } +/* + * Check if secondary master species element + */ + j = *i; + cptr = *t_ptr; + if (c == '(') + { + /* copy parenthesis */ + element[*i] = c; + (*i)++; + (*t_ptr)++; + /* copy number */ + for (;;) + { + c = **t_ptr; + if (isdigit((int) c) || c == '-' || c == '.') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + else if (c == '+') + { + (*t_ptr)++; + } + else + { + break; + } + } + /* go back to before parenthesis */ + if (c != ')') + { + *i = j; + *t_ptr = cptr; + /* put in closing parenthesis */ + } + else + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + element[*i] = '\0'; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_secondary_in_species(const char **t_ptr, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int l; + size_t count; + char c, c1; + LDBLE d; + char element[MAX_LENGTH]; + const char** t_ptr_save = t_ptr; + while (((c = **t_ptr) != '+') && (c != '-') && (c != '\0')) + { + /* close parenthesis */ + if (c == ')') + { + paren_count--; + if (paren_count < 0) + { + error_string = sformatf( "Too many right parentheses."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + (*t_ptr)++; + return (OK); + } + c1 = *((*t_ptr) + 1); + /* beginning of element name */ + if (isupper((int) c) || c == '[' || (c == 'e' && c1 == '-')) + { +/* + * Get new element and subscript + */ + if (get_secondary(t_ptr, element, &l) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].elt = element_store(element); + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].coef = d * coef; + count_elts++; +/* + * Expand working space for elements if necessary + */ + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + continue; + } +/* + * Open parentheses + */ + if (c == '(') + { + count = count_elts; + if (c1 == ')') + { + error_string = sformatf( "Empty parentheses."); + warning_msg(error_string); + } + paren_count++; + (*t_ptr)++; + if (get_secondary_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + for (size_t i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Colon + */ + if (c == ':') + { + count = count_elts; + (*t_ptr)++; + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + if (get_secondary_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + for (size_t i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Not beginning of element and not opening paren + */ + error_string = sformatf( + "Parsing error in get_secondary_in_species, unexpected character, %c.", + c); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (paren_count != 0) + { + error_string = sformatf("Unbalanced parentheses: %s", *t_ptr_save); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_num(const char **t_ptr, LDBLE * num) +/* ---------------------------------------------------------------------- */ +/* + * Function reads through a string looking for leading numeric field + * if no numeric field is found, the number is set to 1.0. + * + * Arguments: + * + * **t_ptr input, points to a position in a character string from which + * a number is to be extracted. + * output, points to next position to be parsed. + * *num address where the number is to be stored. + * + * Returns: + * ERROR, + * OK. + */ +{ + int i, decimal; + char c; + char* ptr1; + char token[MAX_LENGTH]; + + *num = 1.0; + i = 0; + c = **t_ptr; + decimal = 0; + if (isdigit((int) c) || (c == '.')) + { + while (isdigit((int) c) || (c == '.')) + { + if (c == '.') + decimal++; + if (decimal > 1) + break; + token[i++] = c; + /* check number length */ + if (i >= MAX_LENGTH) + { + error_string = sformatf( + "Number was greater than MAX_LENGTH characters."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + c = *(++(*t_ptr)); + } + token[i] = '\0'; + errno = 0; + *num = strtod(token, &ptr1); + if (errno == ERANGE) + { + error_string = sformatf( "Converting number in get_num, %s.", token); + input_error++; + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_species(const char **cptr) +/* ---------------------------------------------------------------------- */ +{ +/* Function reads next species out of the equation, including optional + * preceding coefficient and optional trailing charge. Data are + * store in trxn.token[count]. + * + * Arguments: + * **cptr input, points to the position in the equation to pick up the species. + * output, points to the next character after the species charge. + * + */ + std::string string; + int l; + + if ((size_t) count_trxn + 1 > trxn.token.size()) + trxn.token.resize(count_trxn + 1); + /* coefficient */ + if (get_coef(&(trxn.token[count_trxn].coef), cptr) == ERROR) + { + return (ERROR); + } + /* name and charge */ + if (get_token(cptr, string, &trxn.token[count_trxn].z, &l) == ERROR) + { + return (ERROR); + } + trxn.token[count_trxn].name = string_hsave(string.c_str()); + /* + trxn.token[count_trxn].z = 0; + trxn.token[count_trxn].s = NULL; + trxn.token[count_trxn].unknown = NULL; + */ + return (OK); +} diff --git a/phreeqcpp/phast.xsd b/phreeqcpp/phast.xsd new file mode 100644 index 00000000..1e6c660e --- /dev/null +++ b/phreeqcpp/phast.xsd @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phreeqcpp/phqalloc.cpp b/phreeqcpp/phqalloc.cpp new file mode 100644 index 00000000..456e84dc --- /dev/null +++ b/phreeqcpp/phqalloc.cpp @@ -0,0 +1,316 @@ +#define INCLUDE_PHRQALLOC_H +#include "Phreeqc.h" +#include +#include +#include + +#if defined(PHREEQCI_GUI) +#define _CRTDBG_MAP_ALLOC +#include +#endif + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +#if defined(USE_PHRQ_ALLOC) +/* ---------------------------------------------------------------------- */ +#if !defined(NDEBUG) +void * Phreeqc:: +PHRQ_malloc(size_t size, const char *szFileName, int nLine) +#else +void *Phreeqc:: +PHRQ_malloc(size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + p = (PHRQMemHeader *) malloc(sizeof(PHRQMemHeader) + size); + + if (p == NULL) + return NULL; +#if !defined(NDEBUG) + memset(p, 0, sizeof(PHRQMemHeader) + size); +#endif + p->pNext = NULL; + + if ((p->pPrev = s_pTail) != NULL) + { + s_pTail->pNext = p; + } + + p->size = sizeof(PHRQMemHeader) + size; +#if !defined(NDEBUG) + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + s_pTail = p; + p++; + return ((void *) (p)); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free(void *ptr) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + if (ptr == NULL) + return; + + p = (PHRQMemHeader *) ptr - 1; + + if (p->pNext != NULL) + { + p->pNext->pPrev = p->pPrev; + } + else + { + /* Handle special case when (p == s_pTail) */ + assert(s_pTail != NULL); + assert(p == s_pTail); + s_pTail = p->pPrev; + } + + if (p->pPrev) + { + p->pPrev->pNext = p->pNext; + } + +#if !defined(NDEBUG) + free(p->szFileName); +#endif + + free(p); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free_all(void) +/* ---------------------------------------------------------------------- */ +{ + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + std::ostringstream ostrm; + + if (s_pTail == NULL) + { +#if !defined(NDEBUG) + output_msg("No memory leaks\n"); +#endif + return; + } + while (s_pTail->pPrev != NULL) + { + s_pTail = s_pTail->pPrev; +#if !defined(NDEBUG) + ostrm.clear(); + ostrm << s_pTail->pNext->szFileName << "(" << s_pTail->pNext->nLine; + ostrm << ") " << (void *) (s_pTail->pNext + 1) << ": freed in PHRQ_free_all\n"; + output_msg(ostrm.str().c_str()); + free(s_pTail->pNext->szFileName); +#endif + free(s_pTail->pNext); + } + +#if !defined(NDEBUG) + ostrm.clear(); + ostrm << s_pTail->szFileName << "(" << s_pTail->nLine; + ostrm << ") " << (void *) (s_pTail + 1) << ": freed in PHRQ_free_all\n"; + output_msg(ostrm.str().c_str()); + free(s_pTail->szFileName); +#endif + free(s_pTail); + s_pTail = NULL; +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +PHRQ_calloc(size_t num, size_t size +#if !defined(NDEBUG) + , const char *szFileName, int nLine +#endif + ) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + //p = (PHRQMemHeader *) malloc(sizeof(PHRQMemHeader) + size * num); + p = (PHRQMemHeader *) calloc(1, sizeof(PHRQMemHeader) + size * num); // appt + + if (p == NULL) + return NULL; + + p->pNext = NULL; + + if ((p->pPrev = s_pTail) != NULL) + { + s_pTail->pNext = p; + } + + p->size = sizeof(PHRQMemHeader) + size * num; + +#if !defined(NDEBUG) + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + s_pTail = p; + p++; + return memset(p, 0, size * num); +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +PHRQ_realloc(void *ptr, size_t size +#if !defined(NDEBUG) + , const char *szFileName, int nLine +#endif + ) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + size_t new_size; + size_t old_size; + + if (ptr == NULL) + { + return PHRQ_malloc(size +#if !defined(NDEBUG) + , szFileName, nLine +#endif + ); + } + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + p = (PHRQMemHeader *) ptr - 1; + + new_size = sizeof(PHRQMemHeader) + size; + + old_size = p->size; + p = (PHRQMemHeader *) realloc(p, new_size); + if (p != NULL) + { + p->size = new_size; +#if !defined(NDEBUG) + if (new_size > old_size) + { + memset((char *) p + old_size, 0, new_size - old_size); + } +#endif + } + + if (p == NULL) + return NULL; + + if (p->pPrev != NULL) + { + p->pPrev->pNext = p; + } + + if (p->pNext != NULL) + { + p->pNext->pPrev = p; + } + else + { + s_pTail = p; + } + +#if !defined(NDEBUG) + free(p->szFileName); + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + p++; + return ((void *) (p)); +} +#else /* USE_PHRQ_ALLOC */ +/* ---------------------------------------------------------------------- */ +void *Phreeqc:: +#if !defined(NDEBUG) +PHRQ_malloc(size_t size, const char *szFileName, int nLine) +#else +PHRQ_malloc(size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _malloc_dbg(size, _NORMAL_BLOCK, szFileName, nLine); +#else + return malloc(size); +#endif +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free(void *ptr) +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + _free_dbg(ptr, _NORMAL_BLOCK); +#else + free(ptr); +#endif +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free_all(void) +/* ---------------------------------------------------------------------- */ +{ +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +#if !defined(NDEBUG) +PHRQ_calloc(size_t num, size_t size, const char *szFileName, int nLine) +#else +PHRQ_calloc(size_t num, size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _calloc_dbg(num, size, _NORMAL_BLOCK, szFileName, nLine); +#else + return calloc(num, size); +#endif +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +#if !defined(NDEBUG) +PHRQ_realloc(void *ptr, size_t size, const char *szFileName, int nLine) +#else +PHRQ_realloc(void *ptr, size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _realloc_dbg(ptr, size, _NORMAL_BLOCK, szFileName, nLine); +#else + return realloc(ptr, size); +#endif +} +#endif /* USE_PHRQ_ALLOC */ diff --git a/phreeqcpp/phqalloc.h b/phreeqcpp/phqalloc.h new file mode 100644 index 00000000..12e0454d --- /dev/null +++ b/phreeqcpp/phqalloc.h @@ -0,0 +1,47 @@ +#if !defined (INCLUDE_PHRQALLOC_H) +#define INCLUDE_PHRQALLOC_H + +#if defined (USE_PHRQ_ALLOC) + +#if !defined(NDEBUG) +void *PHRQ_malloc(size_t, const char *, int); +void *PHRQ_calloc(size_t, size_t, const char *, int); +void *PHRQ_realloc(void *, size_t, const char *, int); +#else +extern void *PHRQ_malloc(size_t); +extern void *PHRQ_calloc(size_t, size_t); +extern void *PHRQ_realloc(void *, size_t); +#endif + +extern void PHRQ_free(void *); +extern void PHRQ_free_all(void); + +#if !defined(NDEBUG) +#define PHRQ_malloc(s) PHRQ_malloc(s, __FILE__, __LINE__) +#define PHRQ_calloc(c, s) PHRQ_calloc(c, s, __FILE__, __LINE__) +#define PHRQ_realloc(p, s) PHRQ_realloc(p, s, __FILE__, __LINE__) +#endif + +#else /* defined (USE_PHRQ_ALLOC) */ + +#if !defined(NDEBUG) +void *PHRQ_malloc(size_t, const char *, int); +void *PHRQ_calloc(size_t, size_t, const char *, int); +void *PHRQ_realloc(void *, size_t, const char *, int); +#else +extern void *PHRQ_malloc(size_t); +extern void *PHRQ_calloc(size_t, size_t); +extern void *PHRQ_realloc(void *, size_t); +#endif +void PHRQ_free(void *); +void PHRQ_free_all(void); + +#if !defined(NDEBUG) +#define PHRQ_malloc(s) PHRQ_malloc(s, __FILE__, __LINE__) +#define PHRQ_calloc(c, s) PHRQ_calloc(c, s, __FILE__, __LINE__) +#define PHRQ_realloc(p, s) PHRQ_realloc(p, s, __FILE__, __LINE__) +#endif + +#endif /* defined (USE_PHRQ_ALLOC) */ + +#endif /* !defined (INCLUDE_PHRQALLOC_H) */ diff --git a/phreeqcpp/phreeqc.Makefile b/phreeqcpp/phreeqc.Makefile new file mode 100644 index 00000000..ad676f3a --- /dev/null +++ b/phreeqcpp/phreeqc.Makefile @@ -0,0 +1,237 @@ +# +# Make file for PHREEQC +# +# $(CURDIR) is current directory +TOPDIR:=$(CURDIR)/.. +PROGRAM=phreeqc +EXE=$(TOPDIR)/bin/$(PROGRAM) +SRC=. + +# Do not print commands before executing +#.SILENT: + +# Provides compatibility with GNU make +#.SUFFIXES: + +# Change to pawd if using automounter +PWD=pwd + +# Change to C compiler on your system +CC=g++ + +# Change to C compiler options on your system +#CCFLAGS=-O3 -fno-signed-zeros -Wall -ansi -pedantic -std=c99 -DHAVE_ISFINITE -DNDEBUG # -frounding-math # -pg +CCFLAGS=-O3 -Wall -ansi -pedantic -DHAVE_ISFINITE -DNDEBUG # -frounding-math # -pg -std=c99 + +# Remove the following definition if you do not have +# gmp (Gnu Multiple Precision) package on your system +INVERSE_CL1MP=TRUE + +LOADFLAGS= -lm # -pg +LOADFLAGS+=$(call ld-option, -Wl$(comma)--hash-style=sysv) + +#.c.o : +# ${CC} ${CCFLAGS} -c -o $@ $< +%.o : $(SRC)/%.c + ${CC} ${CCFLAGS} -c -o $@ $< +%.o : $(SRC)/%.cpp + ${CC} ${CCFLAGS} -c -o $@ $< + +# Location to copy scripts on installation +BINDIR=$(HOME)/bin + +OBJECTS= \ + advection.o \ + basic.o \ + basicsubs.o \ + cl1.o \ + cvdense.o \ + cvode.o \ + dense.o \ + dw.o \ + input.o \ + integrate.o \ + inverse.o \ + isotopes.o \ + kinetics.o \ + main.o \ + mainsubs.o \ + model.o \ + nvector.o \ + nvector_serial.o \ + p2clib.o \ + parse.o \ + phqalloc.o \ + phreeqc_files.o \ + pitzer.o \ + pitzer_structures.o \ + prep.o \ + print.o \ + read.o \ + readtr.o \ + sit.o \ + smalldense.o \ + spread.o \ + step.o \ + structures.o \ + sundialsmath.o \ + tally.o \ + tidy.o \ + transport.o \ + utilities.o + +ifdef INVERSE_CL1MP + LOADFLAGS += /z/parkplace/usr/lib/libgmp.a + CCFLAGS += -DINVERSE_CL1MP + OBJECTS += cl1mp.o +endif + +all: $(EXE) + +install: +# +# Create directory for binary and scripts if necessary +# + if [ ! -d $(BINDIR) ]; \ + then \ + mkdir $(BINDIR); \ + echo Created directory $(BINDIR); \ + fi +# +# Put path name of current directory into script for +# locating data files, put script in top directory, +# put symbolic link in BINDIR +# + cd $(TOPDIR); dir1=`$(PWD)`/bin; cd $(BINDIR); if [ `$(PWD)` = $$dir1 ]; \ + then \ + echo "Can not install to $(BINDIR). Choose another directory."; \ + exit 4 ; \ + fi + cd $(TOPDIR); \ + rm -f $(BINDIR)/$(PROGRAM); \ + rm -f $(PROGRAM); \ + sed "s?TOPDIR=.\{0,80\}?TOPDIR=`$(PWD)`?" bin/$(PROGRAM).orig > $(PROGRAM); \ + chmod 755 $(PROGRAM) + cd $(TOPDIR); dir1=`$(PWD)`; cd $(BINDIR); if [ `$(PWD)` != $$dir1 ]; then \ + ln -s $$dir1/$(PROGRAM) $(BINDIR); \ + echo Symbolic link for $(PROGRAM) has been placed in $(BINDIR). ; \ + fi +# +# Check that all necessary files are in place. +# + if [ -f $(BINDIR)/$(PROGRAM) -a \ + -f $(TOPDIR)/bin/$(PROGRAM) -a \ + -f $(TOPDIR)/$(PROGRAM) ]; \ + then echo "Installation complete."; \ + else echo "Installation incomplete."; \ + for FILE in $(BINDIR)/$(PROGRAM) \ + $(TOPDIR)/bin/$(PROGRAM) $(TOPDIR)/$(PROGRAM) ; \ + do \ + if [ ! -f $$FILE ]; then echo $$FILE is missing.; fi; \ + done; \ + fi + echo "Add directory $(BINDIR) to PATH." + +clean: + rm -f $(BINDIR)/$(PROGRAM) + rm -f $(TOPDIR)/bin/$(PROGRAM) + rm -f $(TOPDIR)/src/*.o + rm -f $(SUN_DIR)/bin/$(PROGRAM) + rm -f $(SUN_DIR)/src/*.o + rm -f $(TOPDIR)/src/$(PROGRAM) + rm -f $(DEBUG_DIR)/*.o + echo Removed object and executable files generated by make. + +$(EXE): $(OBJECTS) + echo $(TOPDIR) + $(CC) -o $(EXE) $(OBJECTS) $(LOADFLAGS) # -L/z/parkplace/home/dlpark/packages/efence -lefence + echo Compilation complete, $(EXE). + +advection.o: $(SRC)/advection.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +basic.o: $(SRC)/basic.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/p2c.h $(SRC)/basic.h + +basicsubs.o: $(SRC)/basicsubs.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +cl1.o: $(SRC)/cl1.c $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqtype.h + +cl1mp.o: $(SRC)/cl1mp.c $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqtype.h + +cvdense.o: $(SRC)/cvdense.c $(SRC)/cvdense.h $(SRC)/cvode.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/nvector.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +cvode.o: $(SRC)/cvode.c $(SRC)/cvode.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/nvector.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/kinetics.h $(SRC)/phqalloc.h + +dense.o: $(SRC)/dense.c $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/output.h $(SRC)/phqalloc.h + +dw.o: $(SRC)/dw.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phrqproto.h $(SRC)/output.h $(SRC)/pitzer.h + +input.o: $(SRC)/input.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/input.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/phqalloc.h + +integrate.o: $(SRC)/integrate.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +inverse.o: $(SRC)/inverse.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +isotopes.o: $(SRC)/isotopes.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +kinetics.o: $(SRC)/kinetics.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/sundialstypes.h $(SRC)/cvode.h $(SRC)/nvector.h $(SRC)/cvdense.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/nvector_serial.h $(SRC)/kinetics.h + +main.o: $(SRC)/main.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +mainsubs.o: $(SRC)/mainsubs.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +model.o: $(SRC)/model.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +nvector.o: $(SRC)/nvector.c $(SRC)/nvector.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/output.h + +nvector_serial.o: $(SRC)/nvector_serial.c $(SRC)/nvector_serial.h $(SRC)/nvector.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +output.o: $(SRC)/output.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/phqalloc.h + +p2clib.o: $(SRC)/p2clib.c $(SRC)/p2c.h $(SRC)/output.h + +parse.o: $(SRC)/parse.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +phqalloc.o: $(SRC)/phqalloc.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h + +phreeqc_files.o: $(SRC)/phreeqc_files.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +pitzer.o: $(SRC)/pitzer.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +pitzer_structures.o: $(SRC)/pitzer_structures.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +prep.o: $(SRC)/prep.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +print.o: $(SRC)/print.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer.h + +read.o: $(SRC)/read.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +readtr.o: $(SRC)/readtr.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +sit.o: $(SRC)/sit.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +smalldense.o: $(SRC)/smalldense.c $(SRC)/smalldense.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +spread.o: $(SRC)/spread.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +step.o: $(SRC)/step.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +structures.o: $(SRC)/structures.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +sundialsmath.o: $(SRC)/sundialsmath.c $(SRC)/sundialsmath.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/output.h + +tally.o: $(SRC)/tally.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +tidy.o: $(SRC)/tidy.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +transport.o: $(SRC)/transport.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +utilities.o: $(SRC)/utilities.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +-include $(CURDIR)/distribution.mk + +#ld-option +# Usage: ldflags += $(call ld-option, -Wl$(comma)--hash-style=sysv) +comma=, +ld-option = $(shell if $(CC) $(1) \ + -nostdlib -o /dev/null -xc /dev/null \ + > /dev/null 2>&1 ; then echo "$(1)" ; else echo "$(2)"; fi) diff --git a/phreeqcpp/phreex.ico b/phreeqcpp/phreex.ico new file mode 100644 index 0000000000000000000000000000000000000000..8044dda4bdebd3d953d6d54ea920797863f12a78 GIT binary patch literal 318 zcma)1y9&ZU5S)PEBN$R?YZ(g>U!P+YqJmAp!qy^1N+F0Qeu*A}{awU5N5Bt|`vW;Q zK1#t^W@mR61{N@=Y}*DM9BRDv)Jd~%oVWz#ROQI4YRwVtD%)$$W#FppJ6fVqgI qWB{A9aj|An9)vhU5R_7|)Lq=HLY2DVt%`DKEez|WpZ5E&|1M7lE?-Un literal 0 HcmV?d00001 diff --git a/phreeqcpp/pitzer.cpp b/phreeqcpp/pitzer.cpp new file mode 100644 index 00000000..0b5db899 --- /dev/null +++ b/phreeqcpp/pitzer.cpp @@ -0,0 +1,2707 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "Solution.h" +#define PITZER_LISTS +#define PITZER + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_init(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Initialization for pitzer + */ + pitzer_model = FALSE; + use_etheta = TRUE; + pitz_params.clear(); + theta_params.clear(); + + ICON = TRUE; + OTEMP = -100.; + OPRESS = -100.; + for (i = 0; i < 23; i++) + { + BK[i] = 0.0; + DK[i] = 0.0; + } + pitzer_pe = FALSE; + VP = 0; + DW0 = 0; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_tidy(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Make lists of species for cations, anions, neutral + */ + const char *string1, *string2; + int i, j, order; + int i0, i1, i2; + int count_pos, count_neg, count_neut, count[3], jj; + LDBLE z0, z1; + class pitz_param *pzp_ptr; + class theta_param *theta_param_ptr; + /* + * Ensure new parameters are calculated + */ + OTEMP = -100.; + OPRESS = -100.; + /* + * allocate pointers to species structures + */ + spec.clear(); + spec.resize(3 * s.size(), NULL); + cations = &spec[0]; + neutrals = &(spec[s.size()]); + anions = &(spec[2 * s.size()]); + MAXCATIONS = (int)s.size(); + FIRSTANION = 2 * (int)s.size(); + MAXNEUTRAL = (int)s.size(); + count_cations = 0; + count_anions = 0; + count_neutrals = 0; + if (itmax < 200) + itmax = 200; + /* + * allocate other arrays for Pitzer + */ + IPRSNT.resize(3 * s.size()); + M.resize(3 * s.size()); + LGAMMA.resize(3 * s.size()); + + for (i = 0; i < (int)s.size(); i++) + { + if (s[i] == s_eminus) + continue; + if (s[i] == s_h2o) + continue; + if (s[i]->type == EX || s[i]->type == SURF) + continue; + if (s[i]->z < -.001) + { + anions[count_anions++] = s[i]; + } + else if (s[i]->z > .001) + { + cations[count_cations++] = s[i]; + } + else + { + neutrals[count_neutrals++] = s[i]; + } + } + /* + * Add etheta to parameter list in case theta not defined for + * cation-cation or anion-anion pair + * Remove old TYPE_ETHETA definitions + */ + + std::vector pitz_params_temp = pitz_params; + pitz_params.clear(); + + for (i = 0; i < (int)pitz_params_temp.size(); i++) + { + if (pitz_params_temp[i]->type == TYPE_ETHETA) + { + delete pitz_params_temp[i]; + } + else + { + pitz_params.push_back(pitz_params_temp[i]); + } + } + for (i = 0; i < count_cations - 1; i++) + { + for (j = i + 1; j < count_cations; j++) + { + sprintf(line, "%s %s 1", spec[i]->name, spec[j]->name); + pzp_ptr = pitz_param_read(line, 2); + pzp_ptr->type = TYPE_ETHETA; + size_t count_pitz_param = pitz_params.size(); + pitz_params.resize(count_pitz_param + 1); + pitz_params[count_pitz_param] = pzp_ptr; + } + } + for (i = 2 * (int)s.size(); i < 2 * (int)s.size() + count_anions - 1; i++) + { + for (j = i + 1; j < 2 * (int)s.size() + count_anions; j++) + { + sprintf(line, "%s %s 1", spec[i]->name, spec[j]->name); + pzp_ptr = pitz_param_read(line, 2); + pzp_ptr->type = TYPE_ETHETA; + size_t count_pitz_param = pitz_params.size(); + pitz_params.resize(count_pitz_param + 1); + pitz_params[count_pitz_param] = pzp_ptr; + } + } + /* + * put species numbers in pitz_params + */ + for (i = 0; i < (int)pitz_params.size(); i++) + { + for (j = 0; j < 3; j++) + { + if (pitz_params[i]->species[j] == NULL) + continue; + pitz_params[i]->ispec[j] = ISPEC(pitz_params[i]->species[j]); + if ((j < 2 && pitz_params[i]->ispec[j] == -1) || + (j == 2 && (pitz_params[i]->type == TYPE_PSI || pitz_params[i]->type == TYPE_ZETA) + && pitz_params[i]->ispec[j] == -1)) + { + input_error++; + error_string = sformatf( + "Species for Pitzer parameter not defined in SOLUTION_SPECIES, %s", + pitz_params[i]->species[j]); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + } + /* + * MacInnes data + */ + string1 = string_hsave("K+"); + string2 = string_hsave("Cl-"); + IC = ISPEC(string2); + for (i = 0; i < (int)pitz_params.size(); i++) + { + if ((pitz_params[i]->species[0] == string1 && + pitz_params[i]->species[1] == string2) || + (pitz_params[i]->species[0] == string2 && + pitz_params[i]->species[1] == string1) ) + { + switch (pitz_params[i]->type) + { + case TYPE_B0: + mcb0 = pitz_params[i]; + break; + case TYPE_B1: + mcb1 = pitz_params[i]; + break; + case TYPE_C0: + mcc0 = pitz_params[i]; + break; + case TYPE_B2: + case TYPE_THETA: + case TYPE_LAMDA: + case TYPE_ZETA: + case TYPE_PSI: + case TYPE_ETHETA: + case TYPE_ALPHAS: + case TYPE_MU: + case TYPE_ETA: + case TYPE_Other: + default: + break; + } + } + } + if (mcb0 == NULL && mcb1 == NULL && mcc0 == NULL && ICON == TRUE) + { + error_string = sformatf( + "No KCl interaction parameters, turning off MacInnis scaling."); + warning_msg(error_string); + ICON = FALSE; + } + /* + * Set alpha values + */ + for (i = 0; i < (int)pitz_params.size(); i++) + { + z0 = fabs(spec[pitz_params[i]->ispec[0]]->z); + z1 = fabs(spec[pitz_params[i]->ispec[1]]->z); + if (equal(z0, 1.0, 1e-8) || equal(z1, 1.0, 1e-8)) + { + order = 1; + } + else if (equal(z0, 2.0, 1e-8) && equal(z1, 2.0, 1e-8)) + { + order = 2; + } + else + { + order = 3; + } + if (pitz_params[i]->type == TYPE_B1) + { + switch (order) + { + case 1: + case 3: + pitz_params[i]->alpha = 2.0; + break; + case 2: + pitz_params[i]->alpha = 1.4; + break; + } + } + else if (pitz_params[i]->type == TYPE_B2) + { + switch (order) + { + case 1: + pitz_params[i]->alpha = 12.0; + break; + case 2: + pitz_params[i]->alpha = 12.0; + break; + case 3: + pitz_params[i]->alpha = 50.0; + break; + } + } + } + /* + * Add specific alphas + */ + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_ALPHAS) + { + for (j = 0; j < (int)pitz_params.size(); j++) + { + if (pitz_params[j]->type != TYPE_B1) + continue; + if (pitz_params[i]->ispec[0] != pitz_params[j]->ispec[0]) + continue; + if (pitz_params[i]->ispec[1] != pitz_params[j]->ispec[1]) + continue; + pitz_params[j]->alpha = pitz_params[i]->a[0]; + break; + } + for (j = 0; j < (int)pitz_params.size(); j++) + { + if (pitz_params[j]->type != TYPE_B2) + continue; + if (pitz_params[i]->ispec[0] != pitz_params[j]->ispec[0]) + continue; + if (pitz_params[i]->ispec[1] != pitz_params[j]->ispec[1]) + continue; + pitz_params[j]->alpha = pitz_params[i]->a[1]; + break; + } + } + } + + /* + * Add thetas pointer to etheta pitzer parameters + */ + + for (i = 0; i < (int)theta_params.size(); i++) + { + delete theta_params[i]; + } + theta_params.clear(); + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_ETHETA) + { + z0 = spec[pitz_params[i]->ispec[0]]->z; + z1 = spec[pitz_params[i]->ispec[1]]->z; + theta_param_ptr = theta_param_search(z0, z1); + if (theta_param_ptr == NULL) + { + size_t count_theta_param = theta_params.size(); + theta_params.resize(count_theta_param + 1); + theta_params[count_theta_param] = new class theta_param; + theta_params[count_theta_param]->zj = z0; + theta_params[count_theta_param]->zk = z1; + theta_param_ptr = theta_params[count_theta_param]; + } + pitz_params[i]->thetas = theta_param_ptr; + } + } + /* + * Tidy TYPE_MU + */ + + /* Coef for Osmotic coefficient for TYPE_MU */ + + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + i2 = pitz_params[i]->ispec[2]; + count_pos = count_neg = count_neut = 0; + for (j = 0; j <= 2; j++) + { + if (spec[pitz_params[i]->ispec[j]]->z > 0) + { + count_pos++; + } + if (spec[pitz_params[i]->ispec[j]]->z == 0) + { + count_neut++; + } + if (spec[pitz_params[i]->ispec[j]]->z < 0) + { + count_neg++; + } + } + /* All neutral */ + if (count_neut == 3) + { + if (i0 == i1 && i1 == i2) + { + /* type n, n, n */ + pitz_params[i]->os_coef = 1; + continue; + } + else if (i0 == i1 || i1 == i2 || i0 == i2) + { + /* type n, n, n' */ + pitz_params[i]->os_coef = 3; + continue; + } + else + { + /* type n, n', n'' */ + pitz_params[i]->os_coef = 6; + continue; + } + } + /* Two neutral, one anion or cation */ + if (i0 == i1 || i1 == i2 || i0 == i2) + { + /* type n, n, a|c */ + pitz_params[i]->os_coef = 3; + continue; + } + else + { + /* type n, n', a|c */ + pitz_params[i]->os_coef = 6; + continue; + } + } + } + + /* Coef for gammas for TYPE_MU */ + + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + for (j = 0; j <= 2; j++) + { + count[j] = 0; + for (jj = 0; jj <= 2; jj++) + { + if (pitz_params[i]->ispec[j] == pitz_params[i]->ispec[jj]) + { + count[j]++; + } + } + } + for (j = 0; j <= 2; j++) + { + /* cation or anion */ + if (spec[pitz_params[i]->ispec[j]]->z < 0 + || spec[pitz_params[i]->ispec[j]]->z > 0) + { + if (count[0] > 1 || count[1] > 1) + { + pitz_params[i]->ln_coef[j] = 3; + } + else + { + pitz_params[i]->ln_coef[j] = 6; + } + continue; + } + /* Neutral */ + if (count[j] == 3) + { + pitz_params[i]->ln_coef[j] = 1; + } + else if (count[j] == 2) + { + pitz_params[i]->ln_coef[j] = 3; + } + else if (count[j] == 1) + { + if (count[0] > 1 || count[1] > 1) + { + pitz_params[i]->ln_coef[j] = 3; + } + else + { + pitz_params[i]->ln_coef[j] = 6; + } + } + } + } + } + /* Debug TYPE_MU coefficients */ + /* + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + fprintf(stderr, "%s\t%s\t%s\n", pitz_params[i]->species[0], pitz_params[i]->species[1], pitz_params[i]->species[2]); + fprintf(stderr, "%f\t%f\t%f\n", pitz_params[i]->ln_coef[0], pitz_params[i]->ln_coef[1], pitz_params[i]->ln_coef[2]); + fprintf(stderr, "%f\n\n", pitz_params[i]->os_coef); + } + } + */ + /* + * Tidy TYPE_LAMDA + */ + + /* Coef for Osmotic coefficient for TYPE_LAMDA */ + + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_LAMDA) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + /* All neutral */ + if (i0 == i1) + { + /* type n, n */ + pitz_params[i]->os_coef = 0.5; + pitz_params[i]->ln_coef[0] = 1; + pitz_params[i]->ln_coef[1] = 1; + } + else + { + /* type nn', na, nc */ + pitz_params[i]->os_coef = 1; + pitz_params[i]->ln_coef[0] = 2; + pitz_params[i]->ln_coef[1] = 2; + } + } + } + /* Debug TYPE_LAMDA coefficients */ + /* + for (i = 0; i < (int)pitz_params.size(); i++) + { + if (pitz_params[i]->type == TYPE_LAMDA) + { + fprintf(stderr, "%s\t%s\n", pitz_params[i]->species[0], pitz_params[i]->species[1]); + fprintf(stderr, "%f\t%f\n", pitz_params[i]->ln_coef[0], pitz_params[i]->ln_coef[1]); + fprintf(stderr, "%f\n\n", pitz_params[i]->os_coef); + } + } + */ + /* remake map */ + { + pitz_param_map.clear(); + for (int j = 0; j < (int)pitz_params.size(); j++) + { + std::set< std::string > header; + for (int i = 0; i < 3; i++) + { + if (pitz_params[j]->species[i] != NULL) header.insert(pitz_params[j]->species[i]); + } + std::ostringstream key_str; + key_str << pitz_params[j]->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + pitz_param_map[key] = j; + } + assert ((int) pitz_param_map.size() == (int)pitz_params.size()); + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ISPEC(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Find species number in spec for character string species name + */ +{ + int i; + for (i = 0; i < 3 * (int)s.size(); i++) + { + if (spec[i] == NULL) + continue; + if (name == spec[i]->name) + { + return (i); + } + } + return (-1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + /* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + int n; + class pitz_param *pzp_ptr; + pitz_param_type pzp_type; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "b0", /* 0 */ + "b1", /* 1 */ + "b2", /* 2 */ + "c0", /* 3 */ + "theta", /* 4 */ + "lamda", /* 5 */ + "zeta", /* 6 */ + "psi", /* 7 */ + "macinnes", /* 8 */ + "macinnis", /* 9 */ + "mac", /* 10 */ + "redox", /* 11 */ + "pe", /* 12 */ + "alphas", /* 13 */ + "mu", /* 14 */ + "eta", /* 15 */ + "etheta", /* 16 */ + "use_etheta", /* 17 */ + "lambda", /* 18 */ + "aphi" /* 19 */ + }; + int count_opt_list = 20; + /* + * Read lines + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + n = -1; + pzp_type = TYPE_Other; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + pzp_ptr = pitz_param_read(line, n); + if (pzp_ptr != NULL) + { + pzp_ptr->type = pzp_type; + if (pzp_type == TYPE_APHI) + { + delete aphi; + aphi = pzp_ptr; + } + else + { + pitz_param_store(pzp_ptr); + } + } + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PITZER keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* b0 */ + pzp_type = TYPE_B0; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 1: /* b1 */ + pzp_type = TYPE_B1; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 2: /* b2 */ + pzp_type = TYPE_B2; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 3: /* c0 */ + pzp_type = TYPE_C0; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 4: /* theta */ + pzp_type = TYPE_THETA; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 5: /* lamda */ + case 18: /* lambda */ + pzp_type = TYPE_LAMDA; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 6: /* zeta */ + pzp_type = TYPE_ZETA; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 7: /* psi */ + pzp_type = TYPE_PSI; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 13: /* alphas */ + pzp_type = TYPE_ALPHAS; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 8: /* macinnes */ + case 9: /* macinnis */ + case 10: /* mac */ + opt_save = OPTION_ERROR; + ICON = get_true_false(next_char, TRUE); + break; + case 11: /* redox */ + case 12: /* pe */ + opt_save = OPTION_ERROR; + pitzer_pe = get_true_false(next_char, TRUE); + break; + case 14: /* mu */ + pzp_type = TYPE_MU; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 15: /* eta */ + pzp_type = TYPE_ETA; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 16: /* etheta */ + case 17: /* use_etheta */ + opt_save = OPTION_ERROR; + use_etheta = get_true_false(next_char, TRUE); + break; + case 19: /* aphi */ + pzp_type = TYPE_APHI; + n = 0; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + pitzer_model = TRUE; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +PTEMP(LDBLE TK) +/* ---------------------------------------------------------------------- */ +{ +/* +C +C SUBROUTINE TO CALUCLATE TEMPERATURE DEPENDENCE OF PITZER PARAMETER +C +*/ + LDBLE TR = 298.15; + + if (fabs(TK - OTEMP) < 0.001 && fabs(patm_x - OPRESS) < 0.1) + return OK; + DW0 = rho_0 = calc_rho_0(TK - 273.15, patm_x); + VP = patm_x; +#if !defined(PITZER_LISTS) + int i; + for (i = 0; i < (int)pitz_params.size(); i++) + { + calc_pitz_param(pitz_params[i], TK, TR); + } +#else + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + calc_pitz_param(pitz_params[i], TK, TR); + } + if (aphi) + { + calc_pitz_param(aphi, TK, TR); + } + if (mcb0) + { + calc_pitz_param(mcb0, TK, TR); + } + if (mcb1) + { + calc_pitz_param(mcb1, TK, TR); + } + if (mcc0) + { + calc_pitz_param(mcc0, TK, TR); + } +#endif + calc_dielectrics(TK - 273.15, patm_x); + OTEMP = TK; + OPRESS = patm_x; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_pitz_param(class pitz_param *pz_ptr, LDBLE TK, LDBLE TR) +/* ---------------------------------------------------------------------- */ +{ + LDBLE param; + /* + */ + if (fabs(TK - TR) < 0.001) + { + param = pz_ptr->a[0]; + } + else + { + param = (pz_ptr->a[0] + + pz_ptr->a[1] * (1.e0 / TK - 1.e0 / TR) + + pz_ptr->a[2] * log(TK / TR) + + pz_ptr->a[3] * (TK - TR) + + pz_ptr->a[4] * (TK * TK - TR * TR)) + + pz_ptr->a[5] * (1.e0 / (TK * TK) - 1.e0 / (TR * TR)); + } + pz_ptr->p = param; + switch (pz_ptr->type) + { + case TYPE_B0: + pz_ptr->U.b0 = param; + break; + case TYPE_B1: + pz_ptr->U.b1 = param; + break; + case TYPE_B2: + pz_ptr->U.b2 = param; + break; + case TYPE_C0: + pz_ptr->U.c0 = param; + break; + case TYPE_THETA: + pz_ptr->U.theta = param; + break; + case TYPE_LAMDA: + pz_ptr->U.lamda = param; + break; + case TYPE_ZETA: + pz_ptr->U.zeta = param; + break; + case TYPE_ETHETA: + break; + case TYPE_PSI: + pz_ptr->U.psi = param; + break; + case TYPE_ALPHAS: + break; + case TYPE_MU: + pz_ptr->U.mu = param; + break; + case TYPE_ETA: + pz_ptr->U.eta = param; + break; + case TYPE_APHI: + pz_ptr->U.aphi = param; + break; + case TYPE_Other: + default: + error_msg("Should not be TYPE_Other in function calc_pitz_param", + STOP); + break; + } + return OK; +} +#if !defined(PITZER_LISTS) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1, i2; + LDBLE param, l_alpha, z0, z1; + LDBLE etheta, ethetap; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ + LDBLE CONV, XX, OSUM, BIGZ, DI, F, F1, F2, F_var, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B, B1, B2; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + CONV = 1.0 / LOG_10; + XX = 0.0; + OSUM = 0.0; + /*n + I = *I_X; + TK = *TK_X; + */ + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO M + C + */ + for (i = 0; i < 3 * (int)s.size(); i++) + { + IPRSNT[i] = FALSE; + M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + M[i] = under(spec[i]->lm); + if (M[i] > MIN_TOTAL) + IPRSNT[i] = TRUE; + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + /* + ICON = 0; + M[1] = 1.40070736; + M[4] = 2.52131086E-05; + M[140] = 4.59985435E-09; + */ + + /* + C + C COMPUTE PITZER COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP(TK); + for (i = 0; i < 2 * (int)s.size() + count_anions; i++) + { + LGAMMA[i] = 0.0; + if (IPRSNT[i] == TRUE) + { + XX = XX + M[i] * fabs(spec[i]->z); + OSUM = OSUM + M[i]; + } + } + /* + C + C EQUATION (8) + C + */ + BIGZ = XX; + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + B = 1.2; + F = F1 = F2 = -A0 * (DI / (1.0 + B * DI) + 2.0 * log(1.0 + B * DI) / B); + if (patm_x > 1.0) + { + LDBLE pap; + pap = (7e-5 + 1.93e-9 * pow(TK - 250.0, 2.0)) * patm_x; + B1 = B - (pap > 0.2 ? 0.2 : pap); + pap = (9.65e-10 * pow(TK - 263.0, 2.773)) * pow(patm_x, 0.623); + //pap = (-5.22e-4 + 7.19e-8 * pow(TK - 263.0, 2.0)) * pow(patm_x, 0.623); + B2 = B - (pap > 0.2 ? 0.2 : pap); + if (B1 != 0) + F1 = -A0 * (DI / (1.0 + B1 * DI) + 2.0 * log(1.0 + B1 * DI) / B1); + if (B2 != 0) + F2 = -A0 * (DI / (1.0 + B2 * DI) + 2.0 * log(1.0 + B2 * DI) / B2); + } + XXX = 2.0 * DI; + XXX = + (1.0 - (1.0 + XXX - XXX * XXX * 0.5) * exp(-XXX)) / (XXX * XXX); + /*GAMCLM=F+I*2.0e0*(BCX(1,IK,IC)+BCX(2,IK,IC)*XXX)+1.5e0*BCX(4,IK,IC)*I*I; */ + /*GAMCLM=F+I*2.0e0*(mcb0->U.b0 + mcb1->U.b1*XXX) + 1.5e0*mcc0->U.c0*I*I; */ + /*GAMCLM = F + I * 2.0e0 * (mcb0->p + mcb1->p * XXX) + 1.5e0 * mcc0->p * I * I; */ + GAMCLM = F1; + if (mcb0 != NULL) + GAMCLM += I * 2.0 * mcb0->p; + if (mcb1 != NULL) + GAMCLM += I * 2.0 * mcb1->p * XXX; + if (mcc0 != NULL) + GAMCLM += 1.5 * mcc0->p * I * I; + CSUM = 0.0; + OSMOT = -(A0) * pow(I, (LDBLE) 1.5) / (1.0 + B * DI); + /* + * Calculate ethetas + */ + for (i = 0; i < (int)theta_params.size(); i++) + { + z0 = theta_params[i]->zj; + z1 = theta_params[i]->zk; + ETHETAS(z0, z1, I, ðeta, ðetap); + theta_params[i]->etheta = etheta; + theta_params[i]->ethetap = ethetap; + } + /* + * Sums for F, LGAMMA, and OSMOT + */ + for (i = 0; i < (int)pitz_params.size(); i++) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + if (IPRSNT[i0] == FALSE || IPRSNT[i1] == FALSE) + continue; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = pitz_params[i]->p; + l_alpha = pitz_params[i]->alpha; + F_var = 0; + switch (pitz_params[i]->type) + { + case TYPE_B0: + LGAMMA[i0] += M[i1] * 2.0 * param; + LGAMMA[i1] += M[i0] * 2.0 * param; + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_B1: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_B2: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_C0: + CSUM += + M[i0] * M[i1] * pitz_params[i]->p / (2.0 * + sqrt(fabs(z0 * z1))); + LGAMMA[i0] += M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + LGAMMA[i1] += M[i0] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + OSMOT += + M[i0] * M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + break; + case TYPE_THETA: + LGAMMA[i0] += 2.0 * M[i1] * (param /*+ ETHETA(z0, z1, I) */ ); + LGAMMA[i1] += 2.0 * M[i0] * (param /*+ ETHETA(z0, z1, I) */ ); + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_ETHETA: + /* + ETHETAS(z0, z1, I, ðeta, ðetap); + */ + if (use_etheta == TRUE) + { + etheta = pitz_params[i]->thetas->etheta; + ethetap = pitz_params[i]->thetas->ethetap; + F_var = M[i0] * M[i1] * ethetap; + LGAMMA[i0] += 2.0 * M[i1] * etheta; + LGAMMA[i1] += 2.0 * M[i0] * etheta; + OSMOT += M[i0] * M[i1] * (etheta + I * ethetap); + /* + F += M[i0]*M[i1]*ETHETAP(z0, z1, I); + LGAMMA[i0] += 2.0*M[i1]*(ETHETA(z0, z1, I) ); + LGAMMA[i1] += 2.0*M[i0]*(ETHETA(z0, z1, I) ); + OSMOT += M[i0]*M[i1]*(ETHETA(z0, z1, I) + I*ETHETAP(z0, z1, I) ); + */ + } + break; + case TYPE_PSI: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_LAMDA: + LGAMMA[i0] += M[i1] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * param * pitz_params[i]->ln_coef[1]; + OSMOT += M[i0] * M[i1] * param * pitz_params[i]->os_coef; + break; + case TYPE_ZETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_MU: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + + LGAMMA[i0] += M[i1] * M[i2] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * M[i2] * param * pitz_params[i]->ln_coef[1]; + LGAMMA[i2] += M[i0] * M[i1] * param * pitz_params[i]->ln_coef[2]; + OSMOT += M[i0] * M[i1] * M[i2] * param * pitz_params[i]->os_coef; + break; + case TYPE_ETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_ALPHAS: + break; + case TYPE_Other: + default: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + F += F_var; + F1 += F_var; + F2 += F_var; + } + + /* + * Add F and CSUM terms to LGAMMA + */ + + for (i = 0; i < count_cations; i++) + { + if (!IPRSNT[i]) + continue; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + for (i = 2 * (int)s.size(); i < 2 * (int)s.size() + count_anions; i++) + { + if (!IPRSNT[i]) + continue; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + if (ICON == TRUE) + { + PHIMAC = LGAMMA[IC] - GAMCLM; + /* + C + C CORRECTED ERROR IN PHIMAC, NOVEMBER, 1989 + C + */ + for (i = 0; i < 2 * (int)s.size() + count_anions; i++) + { + if (IPRSNT[i] == TRUE) + { + LGAMMA[i] = LGAMMA[i] + spec[i]->z * PHIMAC; + } + } + } + + COSMOT = 1.0 + 2.0 * OSMOT / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837); + /* + if (AW > 1.0) + AW = 1.0; + */ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (i = 0; i < 2 * (int)s.size() + count_anions; i++) + { + if (IPRSNT[i] == FALSE) + continue; + /*spec[i]->lg=LGAMMA[i]*CONV; */ + spec[i]->lg_pitzer = LGAMMA[i] * CONV; + /* + output_msg(sformatf( "%d %s:\t%e\t%e\t%e\t%e \n", i, spec[i]->name, M[i], spec[i]->la, spec[i]->lg_pitzer, spec[i]->lg)); + */ + } + /* + output_msg(sformatf( "OSUM: %e\n", OSUM)); + output_msg(sformatf( "OSMOT: %e\n", OSMOT)); + output_msg(sformatf( "COSMOT: %e\n", COSMOT)); + output_msg(sformatf( "F: %e\n", F)); + output_msg(sformatf( "AW: %e\n", AW)); + */ + /* + *I_X = I; + *COSMOT_X = COSMOT; + */ + return (OK); +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1, i2; + LDBLE param, l_alpha, z0, z1; + LDBLE etheta, ethetap; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ + LDBLE CONV, XX, OSUM, BIGZ, DI, F, F1, F2, F_var, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B, B1, B2; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + CONV = 1.0 / LOG_10; + XX = 0.0; + OSUM = 0.0; + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO M + C + */ + for (size_t j = 0; j < s_list.size(); j++) + { + i = s_list[j]; + IPRSNT[i] = FALSE; + M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + M[i] = under(spec[i]->lm); + if (M[i] > MIN_TOTAL) + IPRSNT[i] = TRUE; + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + /* + C + C COMPUTE PITZER COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP(TK); + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + LGAMMA[i] = 0.0; + XX = XX + M[i] * fabs(spec[i]->z); + OSUM = OSUM + M[i]; + } + /* + C + C EQUATION (8) + C + */ + BIGZ = XX; + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + B = 1.2; + F = F1 = F2 = -A0 * (DI / (1.0 + B * DI) + 2.0 * log(1.0 + B * DI) / B); + if (patm_x > 1.0) + { + LDBLE pap = 0.0; + pap = (7e-5 + 1.93e-9 * pow(TK - 250.0, 2.0)) * patm_x; + B1 = B - (pap > 0.2 ? 0.2 : pap); + if (TK > 263.0) + { + pap = (9.65e-10 * pow(TK - 263.0, 2.773)) * pow(patm_x, 0.623); + //pap = (-5.22e-4 + 7.19e-8 * pow(TK - 263.0, 2.0)) * pow(patm_x, 0.623); + } + B2 = B - (pap > 0.2 ? 0.2 : pap); + if (B1 != 0) + F1 = -A0 * (DI / (1.0 + B1 * DI) + 2.0 * log(1.0 + B1 * DI) / B1); + if (B2 != 0) + F2 = -A0 * (DI / (1.0 + B2 * DI) + 2.0 * log(1.0 + B2 * DI) / B2); + } + XXX = 2.0 * DI; + XXX = (1.0 - (1.0 + XXX - XXX * XXX * 0.5) * exp(-XXX)) / (XXX * XXX); + GAMCLM = F1; + if (mcb0 != NULL) + GAMCLM += I * 2.0 * mcb0->p; + if (mcb1 != NULL) + GAMCLM += I * 2.0 * mcb1->p * XXX; + if (mcc0 != NULL) + GAMCLM += 1.5 * mcc0->p * I * I; + CSUM = 0.0; + OSMOT = -(A0) * pow(I, (LDBLE) 1.5) / (1.0 + B * DI); + /* + * Calculate ethetas + */ + if (use_etheta == TRUE) + { + for (i = 0; i < (int)theta_params.size(); i++) + { + z0 = theta_params[i]->zj; + z1 = theta_params[i]->zk; + ETHETAS(z0, z1, I, ðeta, ðetap); + theta_params[i]->etheta = etheta; + theta_params[i]->ethetap = ethetap; + } + } + /* + * Sums for F, LGAMMA, and OSMOT + */ + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = pitz_params[i]->p; + l_alpha = pitz_params[i]->alpha; + F_var = 0; + switch (pitz_params[i]->type) + { + case TYPE_B0: + LGAMMA[i0] += M[i1] * 2.0 * param; + LGAMMA[i1] += M[i0] * 2.0 * param; + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_B1: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_B2: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_C0: + CSUM += + M[i0] * M[i1] * pitz_params[i]->p / (2.0 * + sqrt(fabs(z0 * z1))); + LGAMMA[i0] += M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + LGAMMA[i1] += M[i0] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + OSMOT += + M[i0] * M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + break; + case TYPE_THETA: + LGAMMA[i0] += 2.0 * M[i1] * (param /*+ ETHETA(z0, z1, I) */ ); + LGAMMA[i1] += 2.0 * M[i0] * (param /*+ ETHETA(z0, z1, I) */ ); + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_ETHETA: + /* + ETHETAS(z0, z1, I, ðeta, ðetap); + */ + if (use_etheta == TRUE) + { + etheta = pitz_params[i]->thetas->etheta; + ethetap = pitz_params[i]->thetas->ethetap; + F_var = M[i0] * M[i1] * ethetap; + LGAMMA[i0] += 2.0 * M[i1] * etheta; + LGAMMA[i1] += 2.0 * M[i0] * etheta; + OSMOT += M[i0] * M[i1] * (etheta + I * ethetap); + } + break; + case TYPE_PSI: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_LAMDA: + LGAMMA[i0] += M[i1] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * param * pitz_params[i]->ln_coef[1]; + OSMOT += M[i0] * M[i1] * param * pitz_params[i]->os_coef; + break; + case TYPE_ZETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_MU: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + + LGAMMA[i0] += M[i1] * M[i2] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * M[i2] * param * pitz_params[i]->ln_coef[1]; + LGAMMA[i2] += M[i0] * M[i1] * param * pitz_params[i]->ln_coef[2]; + OSMOT += M[i0] * M[i1] * M[i2] * param * pitz_params[i]->os_coef; + break; + case TYPE_ETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_ALPHAS: + break; + case TYPE_Other: + default: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + F += F_var; + F1 += F_var; + F2 += F_var; + } + + /* + * Add F and CSUM terms to LGAMMA + */ + for (size_t j = 0; j < ion_list.size(); j++) + { + int i = ion_list[j]; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + if (ICON == TRUE) + { + PHIMAC = LGAMMA[IC] - GAMCLM; + /* + C + C CORRECTED ERROR IN PHIMAC, NOVEMBER, 1989 + C + */ + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + LGAMMA[i] = LGAMMA[i] + spec[i]->z * PHIMAC; + } + } + + COSMOT = 1.0 + 2.0 * OSMOT / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837); + /* + if (AW > 1.0) + AW = 1.0; + */ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + spec[i]->lg_pitzer = LGAMMA[i] * CONV; + } + /* + *I_X = I; + *COSMOT_X = COSMOT; + */ + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +G(LDBLE L_Y) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d=0.0; + if (L_Y != 0.0) + { + d = 2.0e0 * (1.0e0 - (1.0e0 + L_Y) * exp(-L_Y)) / (L_Y * L_Y); + } + + return (d); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +GP(LDBLE L_Y) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d=0.0; + if (L_Y != 0.0) + { + d = -2.0e0 * (1.0e0 - (1.0e0 + L_Y + L_Y * L_Y / 2.0e0) * exp(-L_Y)) / + (L_Y * L_Y); + } + return d; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ETHETAS(LDBLE ZJ, LDBLE ZK, LDBLE I, LDBLE * etheta, LDBLE * ethetap) +/* ---------------------------------------------------------------------- */ +{ + /* Revised ETHETAS code thanks to Wouter Falkena and the MoReS team, June, 2015 */ + *etheta = 0.0; + *ethetap = 0.0; + + if (ZJ == ZK) + return (OK); + + const LDBLE XCON = 6.0e0 * A0 * sqrt(I); + const LDBLE ZZ = ZJ * ZK; +/* +C +C NEXT 3 ARE EQUATION (A1) +C +*/ + const LDBLE XJK = XCON * ZZ; + const LDBLE XJJ = XCON * ZJ * ZJ; + const LDBLE XKK = XCON * ZK * ZK; + +/* +C +C EQUATION (A3) +C +*/ + LDBLE JAY_XJK; + LDBLE JPRIME_XJK; + ETHETA_PARAMS( XJK, JAY_XJK, JPRIME_XJK ); + + LDBLE JAY_XJJ; + LDBLE JPRIME_XJJ; + ETHETA_PARAMS( XJJ, JAY_XJJ, JPRIME_XJJ ); + + LDBLE JAY_XKK; + LDBLE JPRIME_XKK; + ETHETA_PARAMS( XKK, JAY_XKK, JPRIME_XKK ); + + *etheta = + ZZ * (JAY_XJK - JAY_XJJ / 2.0e0 - JAY_XKK / 2.0e0) / (4.0e0 * I); + *ethetap = + ZZ * (JPRIME_XJK - JPRIME_XJJ / 2.0e0 - + JPRIME_XKK / 2.0e0) / (8.0e0 * I * I) - *etheta / I; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +ETHETA_PARAMS(LDBLE X, LDBLE& JAY, LDBLE& JPRIME ) +/* ---------------------------------------------------------------------- */ +/* +C +C NUMERICAL APPROXIMATION TO THE INTEGRALS IN THE EXPRESSIONS FOR J0 +C AND J1. CHEBYSHEV APPROXIMATION IS USED. THE CONSTANTS 'AK' ARE +C DEFINED IN BLOCK COMMON. +C +*/ +/* +C +C AK IS USED TO CALCULATE HIGHER ORDER ELECTROSTATIC TERMS IN +C SUBROUTINE PITZER +C +*/ +{ + static const LDBLE AKX[42] = { + 1.925154014814667e0, -.060076477753119e0, -.029779077456514e0, + -.007299499690937e0, 0.000388260636404e0, 0.000636874599598e0, + 0.000036583601823e0, -.000045036975204e0, -.000004537895710e0, + 0.000002937706971e0, 0.000000396566462e0, -.000000202099617e0, + -.000000025267769e0, 0.000000013522610e0, 0.000000001229405e0, + -.000000000821969e0, -.000000000050847e0, 0.000000000046333e0, + 0.000000000001943e0, -.000000000002563e0, -.000000000010991e0, + 0.628023320520852e0, 0.462762985338493e0, 0.150044637187895e0, + -.028796057604906e0, -.036552745910311e0, -.001668087945272e0, + 0.006519840398744e0, 0.001130378079086e0, -.000887171310131e0, + -.000242107641309e0, 0.000087294451594e0, 0.000034682122751e0, + -.000004583768938e0, -.000003548684306e0, -.000000250453880e0, + 0.000000216991779e0, 0.000000080779570e0, 0.000000004558555e0, + -.000000006944757e0, -.000000002849257e0, 0.000000000237816e0 + }; +/* + LDBLE PRECISION AK, BK, DK + COMMON / MX8 / AK(0:20,2),BK(0:22),DK(0:22) +*/ + const LDBLE *AK; + LDBLE L_Z = 0.0; + LDBLE L_DZ = 0.0; + + if ( X <= 1.0e0 ) + { + const LDBLE powX0_2 = pow( X, 0.2 ); + L_Z = 4.0e0 * powX0_2 - 2.0e0; + L_DZ = 0.8e0 * powX0_2 / 2.0e0; + AK = &AKX[0]; + } + else + { + const LDBLE powXmin0_1 = pow( X, -0.1 ); + L_Z = ( 40.0e0 * powXmin0_1 - 22.0e0 ) / 9.0e0; + L_DZ = -4.0e0 * powXmin0_1 / 18.0e0; + AK = &AKX[21]; + } + + BK[20] = AK[20]; + BK[19] = L_Z * AK[20] + AK[19]; + DK[19] = AK[20]; + for ( int i = 18; i >= 0; i-- ) + { + BK[i] = L_Z * BK[i + 1] - BK[i + 2] + AK[i]; + DK[i] = BK[i + 1] + L_Z * DK[i + 1] - DK[i + 2]; + } + + JAY = X / 4.0e0 - 1.0e0 + 0.5e0 * (BK[0] - BK[2]); + JPRIME = X * .25e0 + L_DZ * (DK[0] - DK[2]); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_clean_up(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all allocated memory, except strings + */ + int i; + for (i = 0; i < (int)pitz_params.size(); i++) + { + delete pitz_params[i]; + } + pitz_param_map.clear(); + pitz_params.clear(); + for (i = 0; i < (int)theta_params.size(); i++) + { + delete theta_params[i]; + } + theta_params.clear(); + LGAMMA.clear(); + IPRSNT.clear(); + spec.clear(); + if (aphi != NULL) + { + delete aphi; + aphi = NULL; + } + M.clear(); + + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_pz(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg_pitzer = 0.0; + } + if (initial == TRUE || set_and_run_attempt > 0) + { + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_x[i]->lg = 0.0; + } + } +/* + * Set master species activities + */ + + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + potV_x = solution_ptr->Get_potV(); // added in DL_pitz + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + AW = pow((LDBLE) 10.0, s_h2o->la); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) + pitzer_initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) + initial_surface_water(); + pitzer_revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise molalities species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + + max_iter = 100; + if (iterations < 0 && (use.Get_surface_in() || use.Get_exchange_in())) + gammas_pz(true); // DL_pitz : for SURF estimates + l_iter = 0; + repeat = TRUE; + fail = FALSE; + double d = 2; + double logd = log10(d); + while (repeat == TRUE && fail == FALSE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + /*pitzer(); */ + /*s_h2o->la = 0.0; */ + /*molalities(TRUE); */ + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } + /*n + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || +/* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + f = fabs(x[i]->sum); + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += logd; + if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (f > d * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + x[i]->master[0]->s->la += + weight * log10(fabs(x[i]->moles / x[i]->sum)); + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in pitzer_revise_guesses: %d\n", l_iter)); + /*mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; */ + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_pz(void) +/* ---------------------------------------------------------------------- */ +{ // calculate the derivatives numerically + std::vector base; + std::vector phase_ptrs; + std::vector base_phases; + cxxGasPhase base_gas_phase; + cxxSurface base_surface; + LDBLE d, d1, d2; + int i, j; +Restart: + if (use.Get_surface_ptr() != NULL) + { + base_surface = *use.Get_surface_ptr(); + } + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + base_gas_phase = *gas_phase_ptr; + base_phases.resize(gas_phase_ptr->Get_gas_comps().size()); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp* gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + class phase* phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE); + phase_ptrs.push_back(phase_ptr); + base_phases[i] = *phase_ptr; + } + } + calculating_deriv = 1; + molalities(TRUE); + if (full_pitzer == TRUE) + { + pitzer(); + } + mb_sums(); + residuals(); + + size_t pz_max_unknowns = max_unknowns; + base.resize(count_unknowns); + for (i = 0; i < count_unknowns; i++) + { + base[i] = residual[i]; + } + d = 0.0001; + d1 = d * LOG_10; + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + //d2 = d1; + d2 = d * LOG_10; + break; + case AH2O: + x[i]->master[0]->s->la += d; + //d2 = d1; + d2 = d * LOG_10; + break; + case PITZER_GAMMA: + if (!full_pitzer) + continue; + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + //mass_water_aq_x *= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //d2 = log(1.0 + d); + //break; + // DL_pitz + d1 = mass_water_aq_x * d; + mass_water_aq_x += d1; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x += d1; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //d2 = log(1.0 + d); + d2 = d1; + break; + case MH: + if (pitzer_pe == TRUE) + { + s_eminus->la += d; + //d2 = d1; + d2 = d * LOG_10; + break; + } + else + { + continue; + } + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = (x[i]->moles > 1 ? 1 : 30); + d2 *= d * x[i]->moles; + d2 = (d2 < ineq_tol ? ineq_tol : d2); + //if (d2 < 1e-14) + // d2 = 1e-14; + x[i]->moles += d2; + break; + case MU: + //continue; + d2 = d * mu_x; + mu_x += d2; + //k_temp(tc_x, patm_x); + gammas_pz(false); + break; + case PP: + continue; + break; + case SS_MOLES: + //continue; + //break; + if (x[i]->ss_in == FALSE) + continue; + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + d2 = d * 10 * x[i]->moles; + delta[i] = d2; + reset(); + d2 = delta[i]; + break; + } + molalities(TRUE); + if (max_unknowns > pz_max_unknowns) + { + base.clear(); + gammas_pz(false); + jacobian_sums(); + goto Restart; + } + if (full_pitzer == TRUE) + pitzer(); + mb_sums(); + residuals(); + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2; + if (x[i]->type == MH2O) // DL_pitz + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0) + { + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = + exp(s_h2->lm * LOG_10) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + //mass_water_aq_x /= (1 + d); + //x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + //break; + //DL_pitz + mass_water_aq_x -= d1; + if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL) + mass_water_bulk_x -= d1; + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //k_temp(tc_x, patm_x); + gammas_pz(false); + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + x[i]->moles -= d2; + break; + case SS_MOLES: + delta[i] = -d2; + reset(); + break; + } + if (use.Get_surface_ptr() != NULL) + { + *use.Get_surface_ptr() = base_surface; + } + if (use.Get_gas_phase_ptr() != NULL) + { + *use.Get_gas_phase_ptr() = base_gas_phase; + for (size_t g = 0; g < base_phases.size(); g++) + { + *phase_ptrs[g] = base_phases[g]; + } + } + //molalities(TRUE); + //if (full_pitzer == TRUE) + // pitzer(); + //mb_sums(); + //residuals(); + } + molalities(TRUE); + if (full_pitzer == TRUE) + pitzer(); + mb_sums(); + residuals(); + //for (i = 0; i < count_unknowns; i++) + //{ + // //Debugging + // if (fabs(2.0 * (residual[i] - base[i]) / (residual[i] + base[i])) > 1e-2 && + // fabs(residual[i]) + fabs(base[i]) > 1e-6) + // { + // std::cerr << iterations << ": " << x[i]->description << " " << residual[i] << " " << base[i] << std::endl; + // } + //} + base.clear(); + calculating_deriv = 0; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model_pz(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * pitzer_revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + gamma_iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } +#if defined(PITZER_LISTS) + //pitzer_make_lists(); +#endif + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + overall_iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } + /* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + /* + * Calculate jacobian + */ + gammas_pz(false); // appt: no gammas_a_f here + jacobian_sums(); + jacobian_pz(); + /* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf( "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + // appt calculate gammas_a_f here + gammas_pz(true); + if (full_pitzer == TRUE) + pitzer(); + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + if (molalities(FALSE) == ERROR) + { + pitzer_revise_guesses(); + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + if (switch_bases() == TRUE) + { + + count_basis_change++; + count_unknowns -= (int)this->s_x.size(); + reprep(); + full_pitzer = false; + } + //debug + //species_list_sort(); + //sum_species(); + //print_species(); + //print_exchange(); + //print_surface(); + + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + /* remove_unstable_phases is set in check_residuals */ + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + gamma_iterations++; + if (gamma_iterations > itmax) + { + error_string = sformatf( "Maximum gamma iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + if (check_gammas_pz() != TRUE) + { + full_pitzer = TRUE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n", iterations)); + log_msg(sformatf( "Number of gamma iterations: %d\n\n", gamma_iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_gammas_pz(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE /*old_aw,*/ old_mu, tol; + int converge, i; + + old_mu = mu_x; + pitzer(); + molalities(TRUE); + mb_sums(); + converge = TRUE; + tol = convergence_tolerance * 10.; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PITZER_GAMMA) + continue; + if (fabs(x[i]->s->lg - x[i]->s->lg_pitzer) > tol) + { + converge = FALSE; + } + } + if (fabs(old_mu - mu_x) > tol) + converge = FALSE; + + if ((pow((LDBLE) 10.0, s_h2o->la) - AW) > tol) + converge = FALSE; + return converge; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas_pz(bool exch_a_f) +/* ---------------------------------------------------------------------- */ +{ +/* + * Need exchange gammas for pitzer + */ + int i, j; + LDBLE coef, equiv; + /* Initialize */ + k_temp(tc_x, patm_x); +/* + * Calculate activity coefficients + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + break; + case 4: /* Exchange */ + /* Now calculated in next loop */ + break; + case 5: /* Always 1.0 */ + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x.token[j].s->primary->unknown->moles; + break; + } + } + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) // DL_pitz + { + /* mole fraction */ + equiv = 1.0; + } + else + { + equiv = s_x[i]->equiv; + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + break; + case 8: /* LLNL CO2 */ + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + /* + * calculate exchange gammas + */ + + if (use.Get_exchange_ptr() != NULL && exch_a_f) // appt for gammas_a_f + { + for (i = 0; i < (int)this->s_x.size(); i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + case 5: /* Always 1.0 */ + case 6: /* Surface */ + case 7: /* LLNL */ + case 8: /* LLNL CO2 */ + case 9: /* activity water */ + break; + case 4: /* Exchange */ + /* + * Find CEC + * z contains valence of cation for exchange species, alk contains cec + */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + { + s_x[i]->alk = s_x[i]->rxn_x.token[j].s->primary->unknown->moles; + break; + } + } + /* + * Master species is a dummy variable with meaningless activity and mass + */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + if (s_x[i]->primary != NULL) + { + break; + } + /* + * All other species + */ + /* modific 29 july 2005... */ + if (s_x[i]->equiv != 0 && s_x[i]->alk > 0) + { + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + } + if (use.Get_exchange_ptr()->Get_pitzer_exchange_gammas()) + { + /* Assume equal gamma's of solute and exchangeable species... */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + continue; + coef = s_x[i]->rxn_x.token[j].coef; + s_x[i]->lg += coef * s_x[i]->rxn_x.token[j].s->lg; + } + } + if (s_x[i]->a_f && s_x[i]->primary == NULL && s_x[i]->moles) + gammas_a_f(i); // appt + } + } + } +/* ...end modific 29 july 2005 */ + + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +pitzer_make_lists(void) +/* ---------------------------------------------------------------------- */ +{ + double log_min = log10(MIN_TOTAL); + s_list.clear(); + cation_list.clear(); + neutral_list.clear(); + anion_list.clear(); + ion_list.clear(); + param_list.clear(); + OTEMP = -100.0; + for (int j = 0; j < 3; j++) + { + int min, max; + switch (j) + { + case 0: + min = 0; + max = count_cations; + break; + case 1: + min = (int)s.size(); + max = (int)s.size() + count_neutrals; + break; + case 2: + min = 2* (int)s.size(); + max = 2* (int)s.size() + count_anions; + break; + } + for (int i = min; i < max; i++) + { + IPRSNT[i] = FALSE; + M[i] = 0.0; + if ((spec[i] != NULL && spec[i]->in == TRUE) || + (ICON == TRUE && i == IC)) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + IPRSNT[i] = TRUE; + s_list.push_back(i); + if (i < (int)s.size()) + { + cation_list.push_back(i); + } + if (i >= (int)s.size() && i < 2* (int)s.size()) + { + neutral_list.push_back(i); + } + if (i >= 2* (int)s.size()) + { + anion_list.push_back(i); + } + if (i < (int)s.size() || i >= 2* (int)s.size()) + { + ion_list.push_back(i); + } + if (spec[i]->lm > log_min) + { + M[i] = under(spec[i]->lm); + } + } + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + for (int i = 0; i < (int)pitz_params.size(); i++) + { + /* + TYPE_B0, TYPE_B1, TYPE_B2, TYPE_C0, TYPE_THETA, TYPE_LAMDA, TYPE_ZETA, + TYPE_PSI, TYPE_ETHETA, TYPE_ALPHAS, TYPE_MU, TYPE_ETA, TYPE_Other, + TYPE_SIT_EPSILON, TYPE_SIT_EPSILON_MU + */ + int i0 = pitz_params[i]->ispec[0]; + int i1 = pitz_params[i]->ispec[1]; + if (IPRSNT[i0] == FALSE || IPRSNT[i1] == FALSE) continue; + int i2 = pitz_params[i]->ispec[2]; + if (pitz_params[i]->type == TYPE_PSI || + pitz_params[i]->type == TYPE_ZETA || + pitz_params[i]->type == TYPE_MU || + pitz_params[i]->type == TYPE_ETA) + { + if (IPRSNT[i2] == FALSE) + continue; + } + param_list.push_back(i); + } +} diff --git a/phreeqcpp/pitzer_structures.cpp b/phreeqcpp/pitzer_structures.cpp new file mode 100644 index 00000000..380d7711 --- /dev/null +++ b/phreeqcpp/pitzer_structures.cpp @@ -0,0 +1,225 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include +#include + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ********************************************************************** + * + * Routines related to structure "pitz_param" + * + * ********************************************************************** */ + +/* ---------------------------------------------------------------------- */ +class pitz_param * Phreeqc:: +pitz_param_read(char *string, int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read pitzer parameter info from string + * n is number of species (character values) + * + */ + int l, i, j, k; + const char* cptr; + char token[2 * MAX_LENGTH]; + class pitz_param pzp, *pzp_ptr; + + if (n != 2 && n != 3 && n != 0) + return (NULL); + if (string == NULL) + return (NULL); + + cptr = string; + if (copy_token(token, &cptr, &l) == EMPTY) + return (NULL); + cptr = string; + for (i = 0; i < n; i++) + { + int j = copy_token(token, &cptr, &l); + if (j == EMPTY) + return (NULL); + if (j != UPPER && token[0] != '(') + { + input_error++; + std::ostringstream err; + err << "Wrong number of species for a Pitzer parameter.\n" << line; + error_msg(err.str().c_str(), CONTINUE); + } + pzp.species[i] = string_hsave(token); + } + k = 0; + for (i = 0; i < 6; i++) + { + if (copy_token(token, &cptr, &l) == EMPTY) + break; + j = sscanf(token, SCANFORMAT, &pzp.a[i]); + if (j <= 0) + break; + k++; + } + if (k <= 0) + return (NULL); + pzp_ptr = new class pitz_param; + *pzp_ptr = pzp; + return (pzp_ptr); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +pitz_param_store(class pitz_param *pzp_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of pitz_params for same type and species + * Returns -1 if not found, index number in pitz_params if found + */ + int i; + if (pzp_ptr == NULL) + return; + if (pzp_ptr->type == TYPE_Other) + return; + std::set< std::string > header; + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) header.insert(pzp_ptr->species[i]); + } + + std::ostringstream key_str; + key_str << pzp_ptr->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + std::map< std::string, size_t>::iterator jit = pitz_param_map.find(key); + if (jit != pitz_param_map.end()) + { + if (pzp_ptr->species[2] != NULL) + { + error_string = sformatf( "Redefinition of parameter, %s %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1], pzp_ptr->species[2]); + } + else + { + error_string = sformatf( "Redefinition of parameter, %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1]); + } + warning_msg(error_string); + delete pitz_params[(*jit).second]; + pitz_params[(*jit).second] = pzp_ptr; + } + else + { + size_t count_pitz_param = pitz_params.size(); + pitz_params.resize(count_pitz_param + 1); + pitz_params[count_pitz_param] = pzp_ptr; + pitz_param_map[key] = count_pitz_param; + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +sit_param_store(class pitz_param *pzp_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of pitz_params for same type and species + * Returns -1 if not found, index number in pitz_params if found + */ + int i; + if (pzp_ptr == NULL) + return; + if (pzp_ptr->type == TYPE_Other) + return; + + std::set< std::string > header; + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) header.insert(pzp_ptr->species[i]); + } + + std::ostringstream key_str; + key_str << pzp_ptr->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + + std::map< std::string, size_t>::iterator jit = sit_param_map.find(key); + if (jit != sit_param_map.end()) + { + if (pzp_ptr->species[2] != NULL) + { + error_string = sformatf( "Redefinition of parameter, %s %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1], pzp_ptr->species[2]); + } + else + { + error_string = sformatf( "Redefinition of parameter, %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1]); + } + warning_msg(error_string); + delete sit_params[(*jit).second]; + sit_params[(*jit).second] = pzp_ptr; + } + else + { + size_t count_sit_param = sit_params.size(); + sit_params.resize(count_sit_param + 1); + sit_params[count_sit_param] = pzp_ptr; + sit_param_map[key] = count_sit_param; + } +} +class pitz_param* Phreeqc:: +pitz_param_copy(const class pitz_param* src) +{ + if (src == NULL) return NULL; + class pitz_param* dest = new class pitz_param; + *dest = *src; + for (size_t i = 0; i < 3; i++) + { + if (src->species[i] != NULL) + { + dest->species[i] = string_hsave(src->species[i]); + } + } + dest->thetas = NULL; + return dest; +} + +/* ********************************************************************** + * + * Routines related to structure "theta_parm" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class theta_param * Phreeqc:: +theta_param_search(LDBLE zj, LDBLE zk) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of theta_params for same charge + * Returns NULL if not found, index number in theta_params if found + */ + int i; + for (i = 0; i < (int)theta_params.size(); i++) + { + if ((theta_params[i]->zj == zj && theta_params[i]->zk == zk) || + (theta_params[i]->zj == zk && theta_params[i]->zk == zj)) + { + return theta_params[i]; + } + } + return NULL; +} diff --git a/phreeqcpp/prep.cpp b/phreeqcpp/prep.cpp new file mode 100644 index 00000000..82b3125c --- /dev/null +++ b/phreeqcpp/prep.cpp @@ -0,0 +1,6205 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include +#include +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "SS.h" +#include "Solution.h" +#include "cxxKinetics.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +prep(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input is model defined by the structure use. + * Most of routine is skipped if model, as defined by master.total + * plus use.pure_phases, is same as previous calculation. + * Routine sets up class unknown for each unknown. + * Determines elements, species, and phases that are in the model. + * Calculates mass-action equations for each species and phase. + * Routine builds a set of lists for calculating mass balance and + * for building jacobian. + */ + cxxSolution *solution_ptr; + + if (state >= REACTION) + { + same_model = check_same_model(); + } + else + { + same_model = FALSE; + last_model.force_prep = true; + } + /*same_model = FALSE; */ +/* + * Initialize s, master, and unknown pointers + */ + solution_ptr = use.Get_solution_ptr(); + if (solution_ptr == NULL) + { + error_msg("Solution needed for calculation not found, stopping.", + STOP); + return ERROR; + } + description_x = solution_ptr->Get_description(); +/* + * Allocate space for unknowns + * Must allocate all necessary space before pointers to + * X are set. + */ + if (same_model == FALSE || my_array.size() == 0) + { + clear(); + setup_unknowns(); +/* + * Set unknown pointers, unknown types, validity checks + */ + if (state == INITIAL_SOLUTION) + convert_units(solution_ptr); + setup_solution(); + setup_exchange(); + setup_surface(); + setup_pure_phases(); + setup_gas_phase(); + setup_ss_assemblage(); + setup_related_surface(); + tidy_redox(); + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Allocate space for array + */ + my_array.resize((max_unknowns + 1) * max_unknowns); + delta.resize(max_unknowns); + residual.resize(max_unknowns); + for (int j = 0; j < max_unknowns; j++) + { + residual[j] = 0; + } + +/* + * Build lists to fill Jacobian array and species list + */ + build_model(); + adjust_setup_pure_phases(); + adjust_setup_solution(); + } + else + { +/* + * If model is same, just update masses, don`t rebuild unknowns and lists + */ + quick_setup(); + } + if (debug_mass_balance) + { + output_msg(sformatf("\nTotals for the equation solver.\n")); + output_msg(sformatf("\n\tRow\tName Type Total moles\n")); + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PITZER_GAMMA) + continue; + output_msg(sformatf("\t%3d\t%-17s%2d %15.6e\n", + x[i]->number, x[i]->description, (int)x[i]->type, (double)x[i]->moles)); + } + output_msg(sformatf("\n\n")); + } + if (get_input_errors() > 0) + { + error_msg("Program stopping due to input errors.", STOP); + } + if (sit_model) sit_make_lists(); + if (pitzer_model) + pitzer_make_lists(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +quick_setup(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine is used if model is the same as previous model + * Assumes moles of elements, exchangers, surfaces, gases, and solid solutions have + * been accumulated in array master, usually by subroutine step. + * Updates essential information for the model. + */ + int i; + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->s->type == SURF_PSI) + continue; + if (master[i]->s == s_eminus || + master[i]->s == s_hplus || + master[i]->s == s_h2o || master[i]->s == s_h2 + || master[i]->s == s_o2) + continue; + if (master[i]->total > 0) + { + if (master[i]->s->secondary != NULL) + { + master[i]->s->secondary->unknown->moles = master[i]->total; + } + else + { + master[i]->unknown->moles = master[i]->total; + } + } + } +/* + * Reaction: pH for charge balance + */ + ph_unknown->moles = use.Get_solution_ptr()->Get_cb(); +/* + * Reaction: pe for total hydrogen + */ + if (mass_hydrogen_unknown != NULL) + { +/* Use H - 2O linear combination in place of H */ +#define COMBINE + /*#define COMBINE_CHARGE */ +#ifdef COMBINE + mass_hydrogen_unknown->moles = + use.Get_solution_ptr()->Get_total_h() - 2 * use.Get_solution_ptr()->Get_total_o(); +#else + mass_hydrogen_unknown->moles = use.Get_solution_ptr()->total_h; +#endif + } +/* + * Reaction H2O for total oxygen + */ + if (mass_oxygen_unknown != NULL) + { + mass_oxygen_unknown->moles = use.Get_solution_ptr()->Get_total_o(); + } + +/* + * pp_assemblage + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + assert(comp_ptr != NULL); + //assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end()); + //cxxPPassemblageComp * comp_ptr = &(it->second); + x[i]->pp_assemblage_comp_ptr = comp_ptr; + x[i]->moles = comp_ptr->Get_moles(); + /* A. Crapsi */ + x[i]->si = comp_ptr->Get_si(); + x[i]->delta = comp_ptr->Get_delta(); + /* End A. Crapsi */ + x[i]->dissolve_only = comp_ptr->Get_dissolve_only() ? TRUE : FALSE; + comp_ptr->Set_delta(0.0); + } + } + // Need to update SIs for gases + adjust_setup_pure_phases(); + +/* + * gas phase + */ + if (gas_unknown != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if ((gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) && + numerical_fixed_volume && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume)) + { + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + gas_unknowns[i]->moles = gc_ptr->Get_moles(); + if (gas_unknowns[i]->moles <= 0) + gas_unknowns[i]->moles = MIN_TOTAL; + gas_unknowns[i]->phase->pr_in = false; + gas_unknowns[i]->phase->pr_phi = 1.0; + gas_unknowns[i]->phase->pr_p = 0; + } + } + else + { + gas_unknown->moles = 0.0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + gas_unknown->moles += gc_ptr->Get_moles(); + } + if (gas_unknown->moles <= 0) + gas_unknown->moles = MIN_TOTAL; + gas_unknown->ln_moles = log(gas_unknown->moles); + } + } + +/* + * ss_assemblage + */ + if (ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SS_MOLES) + break; + } + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + for (size_t k = 0; k < ss_ptrs[j]->Get_ss_comps().size(); k++) + { + x[i]->ss_ptr = ss_ptrs[j]; + cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[k]); + x[i]->ss_comp_ptr = comp_ptr; + x[i]->moles = comp_ptr->Get_moles(); + if (x[i]->moles <= 0) + { + x[i]->moles = MIN_TOTAL_SS; + comp_ptr->Set_moles(MIN_TOTAL_SS); + } + comp_ptr->Set_initial_moles(x[i]->moles); + x[i]->ln_moles = log(x[i]->moles); + + x[i]->phase->dn = comp_ptr->Get_dn(); + x[i]->phase->dnb = comp_ptr->Get_dnb(); + x[i]->phase->dnc = comp_ptr->Get_dnc(); + x[i]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + x[i]->phase->log10_lambda = comp_ptr->Get_log10_lambda(); + i++; + } + } + } +/* + * exchange + */ + // number of moles is set from master->moles above +/* + * surface + */ + if (use.Get_surface_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + break; + } + } + for (; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + x[i]->related_moles = charge_ptr->Get_grams(); + x[i]->mass_water = charge_ptr->Get_mass_water(); + /* moles picked up from master->total */ + } + else if (x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + x[i]->related_moles = charge_ptr->Get_grams(); + x[i]->mass_water = charge_ptr->Get_mass_water(); + } + else if (x[i]->type == SURFACE) + { + /* moles picked up from master->total + except for surfaces related to kinetic minerals ... */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + if (comp_ptr->Get_rate_name().size() > 0) + { + cxxNameDouble::iterator lit; + for (lit = comp_ptr->Get_totals().begin(); lit != comp_ptr->Get_totals().end(); lit++) + { + class element *elt_ptr = element_store(lit->first.c_str()); + class master *master_ptr = elt_ptr->master; + if (master_ptr->type != SURF) + continue; + if (strcmp_nocase(x[i]->description, lit->first.c_str()) == 0) + { + x[i]->moles = lit->second; + } + } + } + } + else + { + break; + } + + } + } + save_model(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * sum of partial pressures equation and + * mass balance equations for elements contained in gases + */ + size_t row, col; + class master *master_ptr; + class rxn_token *rxn_ptr; + class unknown *unknown_ptr; + LDBLE coef, coef_elt; + + if (gas_unknown == NULL) + return (OK); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && + numerical_fixed_volume) + { + return build_fixed_volume_gas(); + } + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); +/* + * Determine elements in gas component + */ + count_elts = 0; + paren_count = 0; + if (phase_ptr->rxn_x.token.size() == 0) + continue; + add_elt_list(phase_ptr->next_elt, 1.0); +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif +/* + * Build mass balance sums for each element in gas + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations. %s.\n", + phase_ptr->name)); + } + + /* All elements in gas */ + for (int j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr != NULL) + { + coef = elt_list[j].coef; + store_mb(&(phase_ptr->moles_x), &(unknown_ptr->f), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + unknown_ptr->description, (double) coef)); + } + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* Total pressure of gases */ + store_mb(&(phase_ptr->p_soln_x), &(gas_unknown->f), 1.0); + } +/* + * Build jacobian sums for mass balance equations + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tJacobian summations %s.\n\n", + phase_ptr->name)); + } + for (int j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr == NULL) + { + continue; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\t%s.\n", + unknown_ptr->description)); + } + row = unknown_ptr->number * (count_unknowns + 1); + coef_elt = elt_list[j].coef; + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + master_ptr->s->la = -999.0; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", + master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + col = master_ptr->unknown->number; + coef = coef_elt * rxn_ptr->coef; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + store_jacob(&(phase_ptr->moles_x), + &(my_array[(size_t)row + (size_t)col]), coef); + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* derivative wrt total moles of gas */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + "gas moles", (double) elt_list[j].coef, + row / (count_unknowns + 1), + gas_unknown->number)); + } + store_jacob(&(phase_ptr->fraction_x), + &(my_array[(size_t)row + (size_t)gas_unknown->number]), coef_elt); + } + } +/* + * Build jacobian sums for sum of partial pressures equation + */ + if (gas_phase_ptr->Get_type() != cxxGasPhase::GP_PRESSURE) + continue; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tPartial pressure eqn %s.\n\n", + phase_ptr->name)); + } + unknown_ptr = gas_unknown; + row = unknown_ptr->number * (count_unknowns + 1); + for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s != s_eminus && rxn_ptr->s->in == FALSE) + { + error_string = sformatf( + "Element in species, %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + warning_msg(error_string); + } + else + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + if (master_ptr && master_ptr->s) + { + master_ptr->s->la = -999.0; + } + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species for %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + assert(false); + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + warning_msg(error_string); + } + col = master_ptr->unknown->number; + coef = rxn_ptr->coef; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + store_jacob(&(phase_ptr->p_soln_x), &(my_array[(size_t)row + (size_t)col]), coef); + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * mass action equation for component + * mass balance equations for elements contained in solid solutions + */ + bool stop; + size_t row, col; + class master *master_ptr; + class rxn_token *rxn_ptr; + const char* cptr; + + if (ss_unknown == NULL) + return (OK); + cxxSS * ss_ptr_old = NULL; + col = 0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + continue; + //cxxSS *ss_ptr = use.Get_ss_assemblage_ptr()->Find(x[i]->ss_name); + cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr; + assert(ss_ptr); + if (ss_ptr != ss_ptr_old) + { + col = x[i]->number; + ss_ptr_old = ss_ptr; + } +/* + * Calculate function value (inverse saturation index) + */ + if (x[i]->phase->rxn_x.token.size() == 0) + continue; + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + /* include mole fraction */ + store_mb(&(x[i]->phase->log10_fraction_x), &(x[i]->f), 1.0); + + /* include activity coeficient */ + store_mb(&(x[i]->phase->log10_lambda), &(x[i]->f), 1.0); +/* + * Put coefficients into mass action equations + */ + /* first IAP terms */ + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr == NULL || master_ptr->unknown == NULL) + continue; + store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number, + rxn_ptr->coef); + } + + if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0) + { +/* + * For binary solid solution + */ + /* next dnc terms */ + row = x[i]->number * (count_unknowns + 1); + if (x[i]->ss_comp_number == 0) + { + col = x[i]->number; + } + else + { + col = x[i]->number - 1; + } + store_jacob(&(x[i]->phase->dnc), &(my_array[(size_t)row + (size_t)col]), -1); + + /* next dnb terms */ + col++; + store_jacob(&(x[i]->phase->dnb), &(my_array[(size_t)row + (size_t)col]), -1); + } + else + { +/* + * For ideal solid solution + */ + row = x[i]->number * (count_unknowns + 1); + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + if ((int) j != x[i]->ss_comp_number) + { +/* store_jacob (&(s_s_ptr->dn), &(array[row + col + j]), -1.0); */ + store_jacob(&(x[i]->phase->dn), &(my_array[(size_t)row + (size_t)col + (size_t)j]), + -1.0); + } + else + { + store_jacob(&(x[i]->phase->dnb), &(my_array[(size_t)row + (size_t)col + (size_t)j]), + -1.0); + } + } + } +/* + * Put coefficients into mass balance equations + */ + count_elts = 0; + paren_count = 0; + cptr = x[i]->phase->formula; + get_elts_in_species(&cptr, 1.0); +/* + * Go through elements in phase + */ +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int j = 0; j < count_elts; j++) + { + + if (strcmp(elt_list[j].elt->name, "H") == 0 + && mass_hydrogen_unknown != NULL) + { + store_jacob0((int)mass_hydrogen_unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, + elt_list[j].coef); + + } + else if (strcmp(elt_list[j].elt->name, "O") == 0 + && mass_oxygen_unknown != NULL) + { + store_jacob0((int)mass_oxygen_unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, + elt_list[j].coef); + + } + else + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL || master_ptr->in == FALSE) + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element in phase, %s, is not in model.", + x[i]->phase->name); + warning_msg(error_string); + } + if (master_ptr != NULL) + { + master_ptr->s->la = -999.9; + } +/* + * Master species is in model + */ + } + else if (master_ptr->in == TRUE) + { + store_jacob0((int)master_ptr->unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], &master_ptr->unknown->delta, + elt_list[j].coef); +/* + * Master species in equation needs to be rewritten + */ + } + else if (master_ptr->in == REWRITE) + { + stop = FALSE; + for (int k = 0; k < count_unknowns; k++) + { + if (x[k]->type != MB) + continue; + for (size_t l = 0; l < x[k]->master.size(); l++) + { + if (x[k]->master[l] == master_ptr) + { + store_jacob0((int)x[k]->master[0]->unknown->number, + (int)x[i]->number, -elt_list[j].coef); + store_sum_deltas(&delta[i], + &x[k]->master[0]->unknown-> + delta, elt_list[j].coef); + stop = TRUE; + break; + } + } + if (stop == TRUE) + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_jacobian_sums(int k) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function builds lists sum_jacob1 and sum_jacob2 that describe how to sum molalities + * to form jacobian. + */ + int i, j, kk; + int count_g; + LDBLE coef; + LDBLE *source, *target; + + if (debug_prep == TRUE) + output_msg(sformatf( "\n\tJacobian summations.\n")); +/* + * Calculate jacobian coefficients for each mass balance equation + */ + for (i = 0; i < (int)mb_unknowns.size(); i++) + { +/* + * Store d(moles) for a mass balance equation + */ + /* initial solution only */ + if (mb_unknowns[i].unknown->type == SOLUTION_PHASE_BOUNDARY) + { + continue; + } + coef = mb_unknowns[i].coef; + if (debug_prep == TRUE) + output_msg(sformatf("\n\tMass balance eq: %-13s\t%f\trow\tcol\n", + mb_unknowns[i].unknown->description, (double)coef)); + store_dn(k, mb_unknowns[i].source, (int)mb_unknowns[i].unknown->number, + coef, mb_unknowns[i].gamma_source); +/* + * Add extra terms for change in dg/dx in diffuse layer model + */ + if (s[k]->type >= H2O || dl_type_x == cxxSurface::NO_DL) + { + continue; + } + else if ((mb_unknowns[i].unknown->type == MB || + mb_unknowns[i].unknown->type == MH || + mb_unknowns[i].unknown->type == MH2O) && state >= REACTION) + { + if (mass_oxygen_unknown != NULL) + { + /* term for water, sum of all surfaces */ + source = &s[k]->tot_dh2o_moles; + target = &(my_array[(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + (size_t)mass_oxygen_unknown->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + "sum[dn(i,s)/dlnwater]", (double) coef, + mb_unknowns[i].unknown->number, + mass_oxygen_unknown->number)); + } + store_jacob(source, target, coef); + } + + /* terms for psi, one for each surface */ + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address(); + target = &(my_array[(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + (size_t)x[j]->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + "dg/dlny", (double) coef, + mb_unknowns[i].unknown->number, x[j]->number)); + } + store_jacob(source, target, coef); + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + + /* terms for related phases */ + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + /* has related phase */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)j - 1]->surface_comp); + if (comp_ptr->Get_phase_name().size() == 0) + continue; + + /* now find the related phase */ + for (kk = (int)count_unknowns - 1; kk >= 0; kk--) + { + if (x[kk]->type != PP) + continue; + //if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + + if (kk >= 0) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address(); + target = &(my_array[(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + (size_t)x[kk]->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d", "dphase", + (double) coef, + mb_unknowns[i].unknown->number, + x[kk]->number)); + } + store_jacob(source, target, coef); + } + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + + } + else if (mb_unknowns[i].unknown->type == SURFACE_CB) + { + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (mb_unknowns[i].unknown->number == x[j]->number) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address(); + target = &(my_array[(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + (size_t)x[j]->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf("\t\t%-24s%10.3f\t%d\t%d", "dg/dlny", + (double)coef, + mb_unknowns[i].unknown->number, + x[j]->number)); + } + store_jacob(source, target, coef); + + /* term for related phase */ + /* has related phase */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)j - 1]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + /* now find the related phase */ + for (kk = (int)count_unknowns - 1; kk >= 0; kk--) + { + if (x[kk]->type != PP) + continue; + //if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (kk >= 0) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address(); + target = &(my_array[(size_t)(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + (size_t)x[kk]->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d", + "dphase", (double) coef, + mb_unknowns[i].unknown->number, + x[kk]->number)); + } + store_jacob(source, target, coef); + } + } + + if (mass_oxygen_unknown != NULL) + { + /* term for water, for same surfaces */ + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dh2o_moles_address(); + target = &(my_array[(size_t)mb_unknowns[i].unknown->number * + (count_unknowns + 1) + + (size_t)mass_oxygen_unknown->number]); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d", + "dn(i,s)/dlnwater", (double) coef, + mb_unknowns[i].unknown->number, + mass_oxygen_unknown->number)); + } + store_jacob(source, target, coef); + } + break; + } + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_mb_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function builds lists sum_mb1 and sum_mb2 that describe how to sum molalities + * to calculate mass balance sums, including activity of water, ionic strength, + * charge balance, and alkalinity. + */ + int i; + LDBLE *target; +/* + * Make space for lists + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations.\n")); + } + for (i = 0; i < (int)mb_unknowns.size(); i++) + { + target = &(mb_unknowns[i].unknown->f); + store_mb(mb_unknowns[i].source, target, mb_unknowns[i].coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + mb_unknowns[i].unknown->description, + (double) mb_unknowns[i].coef)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Guts of prep. Determines species in model, rewrites equations, + * builds lists for mass balance and jacobian sums. + */ + int i, j; + LDBLE coef_e; + + if (s_hplus == NULL || s_eminus == NULL || s_h2o == NULL) + { + error_msg("Data base is missing H+, H2O, or e- species.", CONTINUE); + input_error++; + } +/* + * Make space for lists of pointers to species in the model + */ + // clear sum_species_map, which is built from s_x + sum_species_map_db.clear(); + sum_species_map.clear(); + s_x.clear(); + sum_mb1.clear(); + sum_mb2.clear(); + sum_jacob0.clear(); + sum_jacob1.clear(); + sum_jacob2.clear(); + sum_delta.clear(); + species_list.clear(); +/* + * Pick species in the model, determine reaction for model, build jacobian + */ + s_x.clear(); + compute_gfw("H2O", &gfw_water); + gfw_water *= 0.001; + for (i = 0; i < (int)s.size(); i++) + { + if (s[i]->type > H2O && s[i]->type != EX && s[i]->type != SURF) + continue; + s[i]->in = FALSE; + count_trxn = 0; + trxn_add(s[i]->rxn_s, 1.0, false); /* rxn_s is set in tidy_model */ +/* + * Check if species is in model + */ + s[i]->in = inout(); + if (s[i]->in == TRUE) + { + /* for isotopes, activity of water is for 1H and 16O */ + if (s[i]->gflag == 9) + { + gfw_water = 18.0 / 1000.0; + } + if (pitzer_model == FALSE && sit_model == FALSE) + s[i]->lg = 0.0; + compute_gfw(s[i]->name, &s[i]->gfw); + size_t count_s_x = s_x.size(); + s_x.resize(count_s_x + 1); + s_x[count_s_x] = s[i]; + +/* + * Write mass action equation for current model + */ + //if (write_mass_action_eqn_x(STOP) == ERROR) continue; + write_mass_action_eqn_x(STOP); + if (s[i]->type == SURF) + { + add_potential_factor(); + add_cd_music_factors(i); + } + trxn_copy(s[i]->rxn_x); + for (j = 0; j < 3; j++) + { + s[i]->dz[j] = s[i]->rxn_x.dz[j]; + } + if (debug_mass_action == TRUE) + { + output_msg(sformatf( "\n%s\n\tMass-action equation\n", + s[i]->name)); + trxn_print(); + } +/* + * Determine mass balance equations, build sums for mass balance, build sums for jacobian + */ + count_trxn = 0; + trxn_add(s[i]->rxn_s, 1.0, false); + if (s[i]->next_secondary.size() == 0) + { + write_mb_eqn_x(); + } + else + { + count_elts = 0; + add_elt_list(s[i]->next_secondary, 1.0); + } + if (s[i]->type == SURF) + { + add_potential_factor(); + add_cd_music_factors(i); + add_surface_charge_balance(); + add_cd_music_charge_balances(i); + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n%s, Element composition:\n", + trxn.token[0].s->name)); + for (j = 0; j < count_elts; j++) + { + output_msg(sformatf( "\t\t%-20s\t%10.2f\n", + elt_list[j].elt->name, + (double) elt_list[j].coef)); + } + } + //if (debug_prep == TRUE) + //{ + // output_msg(sformatf( "\n\tMass balance equation\n", + // s[i]->name)); + // trxn_print(); + //} + if (s[i]->type < EMINUS) + { + mb_for_species_aq(i); + } + else if (s[i]->type == EX) + { + mb_for_species_ex(i); + } + else if (s[i]->type == SURF) + { + mb_for_species_surf(i); + } +#ifdef COMBINE + build_mb_sums(); +#else + if (s[i] != s_h2o) + { + build_mb_sums(); + } +#endif + + if (!pitzer_model && !sit_model) + build_jacobian_sums(i); +/* + * Build list of species for summing and printing + */ + if (s[i]->next_secondary.size() == 0) + { + write_mb_for_species_list(i); + } + else + { + count_elts = 0; + add_elt_list(s[i]->next_secondary, 1.0); + } + build_species_list(i); + } + } + if (dl_type_x != cxxSurface::NO_DL && (/*pitzer_model == TRUE || */sit_model == TRUE)) //DL_pitz + { + warning_msg("-diffuse_layer option not tested for SIT model"); + } +/* + * Sum diffuse layer water into hydrogen and oxygen mass balances + */ + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { +#ifndef COMBINE + store_mb(&(x[i]->mass_water), + &(mass_hydrogen_unknwon->f), 2 / gfw_water); +#endif + if (mass_oxygen_unknown != NULL) + { + store_mb(&(x[i]->mass_water), + &(mass_oxygen_unknown->f), 1 / gfw_water); + } + } + } + } +/* + * For Pitzer model add lg unknown for each aqueous species + */ + if (pitzer_model == TRUE || sit_model == TRUE) + { + size_t j0 = count_unknowns; + size_t j = count_unknowns + this->s_x.size(); + size_t k = j0; + for (size_t i = j0; i < j; i++) + { + if (s_x[i - j0]->type == EX) + continue; + if (s_x[i - j0]->type == SURF) + continue; + x[k]->number = k; + x[k]->type = PITZER_GAMMA; + x[k]->s = s_x[i - j0]; + x[k]->description = s_x[i - j0]->name; + k++; + count_unknowns++; + } + sit_aqueous_unknowns = count_unknowns - j0; + } + /* + * Rewrite phases to current master species + */ + for (i = 0; i < (int)phases.size(); i++) + { + count_trxn = 0; + trxn_add_phase(phases[i]->rxn_s, 1.0, false); + trxn_reverse_k(); + phases[i]->in = inout(); + if (phases[i]->in == TRUE) + { +/* + * Replace e- in original equation with default redox reaction + */ + coef_e = trxn_find_coef("e-", 1); + if (equal(coef_e, 0.0, TOL) == FALSE) + { + trxn_add(pe_x[default_pe_x.c_str()], coef_e, TRUE); + } +/* + * Rewrite reaction to current master species + */ + write_mass_action_eqn_x(STOP); + trxn_reverse_k(); + trxn_copy(phases[i]->rxn_x); + write_phase_sys_total(i); + } + } + build_solution_phase_boundaries(); + build_pure_phases(); + build_min_exch(); + build_min_surface(); + build_gas_phase(); + build_ss_assemblage(); +/* + * Sort species list, by master only + */ + if (species_list.size() > 1) qsort(&species_list[0], species_list.size(), + sizeof(class species_list), species_list_compare_master); +/* + * Save model description + */ + save_model(); + + if (input_error > 0) + { + error_msg("Stopping due to input errors.", STOP); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Includes calculation of inverse saturation index in sum_mb. + * Puts coefficients in iap and mass balance equations for each phase. + */ + bool stop; + std::string token; + const char* cptr; + class master *master_ptr; + class rxn_token *rxn_ptr; +/* + * Build into sums the logic to calculate inverse saturation indices for + * pure phases + */ + if (pure_phase_unknown == NULL) + return (OK); + +/* + * Calculate inverse saturation index + */ + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP || x[i]->phase->rxn_x.token.size() == 0) + continue; + if (pure_phase_unknown == NULL) + pure_phase_unknown = x[i]; + + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + store_mb(&(x[i]->si), &(x[i]->f), 1.0); + + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + } + for (int i = 0; i < count_unknowns; i++) + { +/* + * rxn_x is null if an element in phase is not in solution + */ + if (x[i]->type != PP || x[i]->phase->rxn_x.token.size() == 0) + continue; +/* + * Put coefficients into IAP equations + */ + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr == NULL || master_ptr->unknown == NULL) + continue; + store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number, + rxn_ptr->coef); + } +/* + * Put coefficients into mass balance equations + */ + count_elts = 0; + paren_count = 0; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_add_formula().size() > 0) + { + cptr = comp_ptr->Get_add_formula().c_str(); + get_elts_in_species(&cptr, 1.0); + } + else + { + cptr = x[i]->phase->formula; + get_elts_in_species(&cptr, 1.0); + } +/* + * Go through elements in phase + */ + +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int j = 0; j < count_elts; j++) + { + + if (strcmp(elt_list[j].elt->name, "H") == 0 + && mass_hydrogen_unknown != NULL) + { + store_jacob0((int)mass_hydrogen_unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, + elt_list[j].coef); + + } + else if (strcmp(elt_list[j].elt->name, "O") == 0 + && mass_oxygen_unknown != NULL) + { + store_jacob0((int)mass_oxygen_unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, + elt_list[j].coef); + + } + else + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr == NULL) + { + error_string = sformatf( + "Element undefined, %s.", + elt_list[j].elt->name); + error_msg(error_string, STOP); + } + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL || master_ptr->in == FALSE) + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element in phase, %s, is not in model.", + x[i]->phase->name); + warning_msg(error_string); + } + if (master_ptr != NULL) + { + master_ptr->s->la = -999.9; + } +/* + * Master species is in model + */ + } + else if (master_ptr->in == TRUE) + { + store_jacob0((int)master_ptr->unknown->number, (int)x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], &master_ptr->unknown->delta, + elt_list[j].coef); +/* + * Master species in equation needs to be rewritten + */ + } + else if (master_ptr->in == REWRITE) + { + stop = false; + for (int k = 0; k < count_unknowns; k++) + { + if (x[k]->type != MB) + continue; + for (size_t l = 0; l < x[k]->master.size(); l++) + { + if (x[k]->master[l] == master_ptr) + { + store_jacob0((int)x[k]->master[0]->unknown->number, + (int)x[i]->number, -elt_list[j].coef); + store_sum_deltas(&delta[i], + &x[k]->master[0]->unknown-> + delta, elt_list[j].coef); + stop = TRUE; + break; + } + } + if (stop == TRUE) + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_solution_phase_boundaries(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class master *master_ptr; + class rxn_token *rxn_ptr; +/* + * Build into sums the logic to calculate inverse saturation indices for + * solution phase boundaries + */ + if (solution_phase_boundary_unknown == NULL) + return (OK); +/* + * Calculate inverse saturation index + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SOLUTION_PHASE_BOUNDARY) + continue; + + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + store_mb(&(x[i]->si), &(x[i]->f), 1.0); + if (x[i]->phase->in != TRUE) + { + error_string = sformatf( + "Solution does not contain all elements for phase-boundary mineral, %s.", + x[i]->phase->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Put coefficients into array + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SOLUTION_PHASE_BOUNDARY) + continue; + for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr->unknown == NULL) + continue; + store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number, + rxn_ptr->coef); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_species_list(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Builds a list that includes an entry for each master species in each + * secondary reaction. Used for summing species of each element and + * printing results. + */ + int j; + class master *master_ptr; +/* + * Treat species made only with H+, e-, and H2O specially + */ + if (is_special(s[n]) == TRUE) + { + size_t count_species_list = species_list.size(); + species_list.resize(count_species_list + 1); + species_list[count_species_list].master_s = s_hplus; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = 0.0; + return (OK); + } +/* + * Treat exchange species specially + */ + if (s[n]->type == EX) + { + if (s[n]->primary != NULL) + return (OK); /* master species has zero molality */ + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->master->s->type != EX) + continue; + master_ptr = elt_list[j].elt->master; + size_t count_species_list = species_list.size(); + species_list.resize(count_species_list + 1); + species_list[count_species_list].master_s = + elt_list[j].elt->master->s; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + } + return (OK); + } +/* + * Treat surface species specially + */ + if (s[n]->type == SURF_PSI) + return (OK); + if (s[n]->type == SURF) + { + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->master->s->type != SURF) + continue; + master_ptr = elt_list[j].elt->master; + size_t count_species_list = species_list.size(); + species_list.resize(count_species_list + 1); + species_list[count_species_list].master_s = + elt_list[j].elt->master->s; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + } + return (OK); + } +/* + * Other aqueous species + */ + for (j = 0; j < count_elts; j++) + { + if (is_special(elt_list[j].elt->master->s) == TRUE) + continue; + if (elt_list[j].elt->master->s->secondary != NULL) + { + master_ptr = elt_list[j].elt->master->s->secondary; + } + else + { + master_ptr = elt_list[j].elt->master->s->primary; + } + size_t count_species_list = species_list.size(); + species_list.resize(count_species_list + 1); + species_list[count_species_list].master_s = master_ptr->s; + species_list[count_species_list].s = s[n]; +/* + * Find coefficient for element represented by master species + */ + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +clear(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Resets information for setting up a new model + */ + cxxSolution *solution_ptr; +/* + * Clear species solution-dependent data + */ + solution_ptr = use.Get_solution_ptr(); + + for (i = 0; i < (int)s.size(); i++) + { + s[i]->in = FALSE; + } +/* + * Set pe structure + */ + pe_x.clear(); + default_pe_x.clear(); + if (solution_ptr->Get_initial_data()) + { + pe_x = solution_ptr->Get_initial_data()->Get_pe_reactions(); + default_pe_x = solution_ptr->Get_initial_data()->Get_default_pe(); + } + else + { + default_pe_x = "pe"; + CReaction chem_rxn; + pe_x[default_pe_x] = chem_rxn; + } + +/* + * Clear master species solution-dependent data + */ + const char * pe_str = string_hsave("pe"); + for (i = 0; i < (int)master.size(); i++) + { + master[i]->in = FALSE; + master[i]->unknown = NULL; + if (solution_ptr->Get_initial_data()) + { + master[i]->pe_rxn = solution_ptr->Get_initial_data()->Get_default_pe(); + } + else + { + master[i]->pe_rxn = pe_str; + } +/* + * copy primary reaction to secondary reaction + */ + master[i]->rxn_secondary = master[i]->rxn_primary; + } + + if (state == INITIAL_SOLUTION) + { + s_h2o->secondary->in = TRUE; + s_hplus->secondary->in = TRUE; + } + else + { + s_h2o->primary->in = TRUE; + s_hplus->primary->in = TRUE; + } + s_eminus->primary->in = TRUE; +/* + * Set all unknown pointers to NULL + */ + mb_unknown = NULL; + ah2o_unknown = NULL; + mass_hydrogen_unknown = NULL; + mass_oxygen_unknown = NULL; + mu_unknown = NULL; + alkalinity_unknown = NULL; + carbon_unknown = NULL; + ph_unknown = NULL; + pe_unknown = NULL; + charge_balance_unknown = NULL; + solution_phase_boundary_unknown = NULL; + pure_phase_unknown = NULL; + exchange_unknown = NULL; + surface_unknown = NULL; + gas_unknown = NULL; + ss_unknown = NULL; +/* + * Free arrays used in model + */ + free_model_allocs(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +convert_units(cxxSolution *solution_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Converts solution concentrations to moles/kg water + * Uses totals.input conc to calculate totals.moles. + */ + LDBLE sum_solutes; + class master *master_ptr; + std::string token; + if (!solution_ptr->Get_new_def() || !solution_ptr->Get_initial_data()) + { + input_error++; + error_msg("Missing data for convert_units", 1); + } +/* + * Convert units + */ +#ifdef ORIGINAL + sum_solutes = exp(-solution_ptr->Get_ph() * LOG_10); +#else + double g_h, g_oh; + compute_gfw("H", &g_h); + compute_gfw("OH", &g_oh); + if (density_iterations == 0) + { + sum_solutes = exp(-solution_ptr->Get_ph() * LOG_10) * g_h; + sum_solutes += exp((-14 + solution_ptr->Get_ph()) * LOG_10) * g_oh; + } + else + { + double soln_vol = calc_solution_volume(); + sum_solutes = s_hplus->moles / soln_vol * g_h; + species* s_oh = s_search("OH-"); + sum_solutes += s_oh->moles / soln_vol * g_oh; + } +#endif + cxxISolution *initial_data_ptr = solution_ptr->Get_initial_data(); + std::map::iterator jit = initial_data_ptr->Get_comps().begin(); + for ( ; jit != initial_data_ptr->Get_comps().end(); jit++) + { + cxxISolutionComp &comp_ref = jit->second; + LDBLE moles; + master_ptr = master_bsearch(comp_ref.Get_description().c_str()); + if (master_ptr != NULL) + { + if (master_ptr->minor_isotope == TRUE) + continue; + } + + // initially store 0.0 for totals + solution_ptr->Get_totals()[comp_ref.Get_description()] = 0.0; + + if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 || + strcmp(comp_ref.Get_description().c_str(), "E") == 0) + { + continue; + } + if (comp_ref.Get_input_conc() <= 0) + continue; +/* + * Get gfw + */ + /* use given gfw if gfw > 0.0 */ + /* use formula give with "as" */ + if (comp_ref.Get_gfw() <= 0.0) + { + if (comp_ref.Get_as().size() > 0) + { + /* use given chemical formula to calculate gfw */ + if (compute_gfw(comp_ref.Get_as().c_str(), &dummy) == ERROR) + { + error_string = sformatf( "Could not compute gfw, %s.", + comp_ref.Get_as().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + comp_ref.Set_gfw(dummy); + } + if (strcmp(comp_ref.Get_description().c_str(), "Alkalinity") == 0 && + strcmp(comp_ref.Get_as().c_str(), "CaCO3") == 0) + { + comp_ref.Set_gfw(comp_ref.Get_gfw() / 2.0); + error_string = sformatf( + "Equivalent wt for alkalinity should be Ca.5(CO3).5. Using %g g/eq.", + (double) comp_ref.Get_gfw()); + warning_msg(error_string); + } + /* use gfw of master species */ + } + else + { + const char* cptr = comp_ref.Get_description().c_str(); + copy_token(token, &cptr); + master_ptr = master_bsearch(token.c_str()); + if (master_ptr != NULL) + { + /* use gfw for element redox state */ + comp_ref.Set_gfw(master_ptr->gfw); + } + else + { + error_string = sformatf( "Could not find gfw, %s.", + comp_ref.Get_description().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } + } +/* + * Convert liters to kg solution + */ + moles = comp_ref.Get_input_conc(); + if (strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL) + { + moles *= 1.0 / (solution_ptr->Get_density()); + } +/* + * Convert milli or micro + */ + char c = comp_ref.Get_units()[0]; + if (c == 'm') + { + moles *= 1e-3; + } + else if (c == 'u') + { + moles *= 1e-6; + } +/* + * Sum grams of solute, convert from moles necessary + */ + if (strstr(comp_ref.Get_units().c_str(), "g/kgs") != NULL || + strstr(comp_ref.Get_units().c_str(), "g/l") != NULL) + { + sum_solutes += moles; + } + else if (strstr(comp_ref.Get_units().c_str(), "Mol/kgs") != NULL || + strstr(comp_ref.Get_units().c_str(), "Mol/l") != NULL || + strstr(comp_ref.Get_units().c_str(), "eq/l") != NULL) + { + sum_solutes += moles * comp_ref.Get_gfw(); + } +/* + * Convert grams to moles, if necessary + */ + if (strstr(comp_ref.Get_units().c_str(), "g/") != NULL && comp_ref.Get_gfw() != 0.0) + { + moles /= comp_ref.Get_gfw(); + } + solution_ptr->Get_totals()[comp_ref.Get_description()] = moles; + } +/* + * Convert /kgs to /kgw + */ + if (strstr(initial_data_ptr->Get_units().c_str(), "kgs") != NULL || + strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL) + { + mass_water_aq_x = 1.0 - 1e-3 * sum_solutes; + if (mass_water_aq_x <= 0) + { + error_string = sformatf( "Solute mass exceeds solution mass in conversion from /kgs to /kgw.\n" + "Mass of water is negative."); + error_msg(error_string, CONTINUE); + input_error++; + } + cxxNameDouble::iterator it; + for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++) + { + it->second = it->second / mass_water_aq_x; + } + } +/* + * Scale by mass of water in solution + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + cxxNameDouble::iterator it; + for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++) + { + it->second = it->second * mass_water_aq_x; + } + + initial_data_ptr->Set_units(moles_per_kilogram_string); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +std::vector Phreeqc:: +get_list_master_ptrs(const char* cptr, class master *master_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input: cptr contains a list of one or more master species names + * Output: space is allocated and a list of master species pointers is + * returned. + */ + int j, l, count_list; + char token[MAX_LENGTH]; + std::vector master_ptr_list; + class master *master_ptr0; +/* + * Make list of master species pointers + */ + count_list = 0; + //master_ptr_list = unknown_alloc_master(); + master_ptr0 = master_ptr; + if (master_ptr0 == master_ptr->s->primary) + { +/* + * First in list is primary species + */ + for (j = 0; j < (int)master.size(); j++) + { + if (master[j] == master_ptr0) + break; + } + j++; +/* + * Element has only one valence + */ + if (j >= (int)master.size() || master[j]->elt->primary != master_ptr0) + { + master_ptr_list.push_back(master_ptr0); +/* + * Element has multiple valences + */ + } + else + { + if (master_ptr0->s->secondary == NULL) + { + error_string = sformatf( + "Master species for valence states of element %s are not correct.\n\tPossibly related to master species for %s.", + master_ptr0->elt->name, master[j]->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + } + master_ptr_list.push_back(master_ptr0->s->secondary); + while (j < (int)master.size() && master[j]->elt->primary == master_ptr0) + { + if (master[j]->s->primary == NULL) + { + master_ptr_list.push_back(master[j]); + } + j++; + } + } + } + else + { +/* + * First in list is secondary species, Include all valences from input + */ + master_ptr_list.push_back(master_ptr0); + while (copy_token(token, &cptr, &l) != EMPTY) + { + master_ptr = master_bsearch(token); + if (master_ptr != NULL) + { + master_ptr_list.push_back(master_ptr); + } + } + } + return (master_ptr_list); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inout(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class rxn_token_temp *token_ptr; +/* + * Routine goes through trxn to determine if each master species is + * in this model. + * Assumes equation is written in terms of primary and secondary species + * Checks to see if in is TRUE or REWRITE for each species + * Returns TRUE if in model + * FALSE if not + */ + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + /* Check primary master species in */ + if (token_ptr->s->primary != NULL + && (token_ptr->s->primary->in == TRUE)) + continue; + /* Check secondary master species */ + if ((token_ptr->s->secondary != NULL) + && (token_ptr->s->secondary->in != FALSE)) + { + continue; + } + /* Must be primary master species that is out */ + return (FALSE); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +is_special(class species *l_spec) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks to see if a species is composed of only H, O, and e- + * Returns TRUE if true + * FALSE if not + */ + int special; + class rxn_token *token_ptr; + + special = TRUE; + for (token_ptr = &l_spec->rxn_s.token[0] + 1; token_ptr->s != NULL; + token_ptr++) + { + if (token_ptr->s != s_hplus && + token_ptr->s != s_h2o && token_ptr->s != s_eminus) + { + special = FALSE; + break; + } + } + return (special); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_mb_unknowns(class unknown *unknown_ptr, LDBLE * LDBLE_ptr, LDBLE coef, + LDBLE * gamma_ptr) +/* ---------------------------------------------------------------------- */ +/* + * Takes an unknown pointer and a coefficient and puts in + * list of mb_unknowns + */ +{ + if (equal(coef, 0.0, TOL) == TRUE) + return (OK); + size_t count_mb_unknowns = mb_unknowns.size(); + mb_unknowns.resize(count_mb_unknowns + 1); + mb_unknowns[count_mb_unknowns].unknown = unknown_ptr; + mb_unknowns[count_mb_unknowns].source = LDBLE_ptr; + mb_unknowns[count_mb_unknowns].gamma_source = gamma_ptr; + mb_unknowns[count_mb_unknowns].coef = coef; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_aq(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i, j; + class master *master_ptr; + class unknown *unknown_ptr; + + mb_unknowns.clear(); +/* + * e- does not appear in any mass balances + */ + if (s[n]->type == EMINUS) + return (OK); +/* + * Do not include diffuse layer in cb, alk, ah2o, mu + */ + if (charge_balance_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } + if (alkalinity_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(alkalinity_unknown, &s[n]->moles, s[n]->alk, + &s[n]->dg); + } + if (ah2o_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(ah2o_unknown, &s[n]->moles, 1.0, &s[n]->dg); + } + if (mu_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(mu_unknown, &s[n]->moles, s[n]->z * s[n]->z, + &s[n]->dg); + } +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (mass_hydrogen_unknown != NULL) + { + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg_total_g); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles, + s[n]->h, &s[n]->dg_total_g); +#endif + } + else + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + } + if (mass_oxygen_unknown != NULL) + { + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->tot_g_moles, + s[n]->o, &s[n]->dg_total_g); + } + else + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } + } +/* + * Sum diffuse layer charge into (surface + DL) charge balance + */ + if (use.Get_surface_ptr() != NULL && s[n]->type < H2O && dl_type_x != cxxSurface::NO_DL) + { + j = 0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + unknown_ptr = x[i]; + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + unknown_ptr = x[(size_t)i + 2]; + + store_mb_unknowns(unknown_ptr, s_diff_layer[n][charge_ptr->Get_name()].Get_g_moles_address(), + s[n]->z, s_diff_layer[n][charge_ptr->Get_name()].Get_dg_g_moles_address()); + j++; + } + } + } +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == charge_balance_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } + else if (master_ptr->unknown == NULL) + { + //std::cerr << "NULL: " << master_ptr->s->name << std::endl; + continue; + } + else if (master_ptr->unknown->type == SOLUTION_PHASE_BOUNDARY) + { + continue; + } + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + store_mb_unknowns(master_ptr->unknown, + &s[n]->tot_g_moles, + elt_list[i].coef * master_ptr->coef, + &s[n]->dg_total_g); + } + else + { + store_mb_unknowns(master_ptr->unknown, + &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_ex(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert exchange species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i; + class master *master_ptr; + + mb_unknowns.clear(); +/* + * Master species for exchange do not appear in any mass balances + */ + if (s[n]->type == EX && s[n]->primary != NULL) + return (OK); +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (charge_balance_unknown != NULL) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } + if (mass_hydrogen_unknown != NULL) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + if (mass_oxygen_unknown != NULL) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } +/* + * Special for ph_unknown, pe_unknown, and alkalinity_unknown + */ + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } +/* + * EX, sum exchange species only into EXCH mass balance in initial calculation + * into all mass balances in reaction calculation + */ + if (state >= REACTION || master_ptr->s->type == EX) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_surf(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i; + class master *master_ptr; + + mb_unknowns.clear(); +/* + * Include in charge balance, if diffuse_layer_x == FALSE + */ + if (charge_balance_unknown != NULL && dl_type_x == cxxSurface::NO_DL) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (mass_hydrogen_unknown != NULL) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + if (mass_oxygen_unknown != NULL) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } +/* + * Other mass balances + */ +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { +/* Skip H+, e-, and H2O */ + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } +/* + * SURF_PSI, sum surface species in (surface + DL) charge balance + */ + if (master_ptr->s->type == SURF_PSI + && use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[0], + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI1) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[1], + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI2) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2], + &s[n]->dg); + /* + if (diffuse_layer_x == TRUE) { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, &s[n]->dg ); + } else { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2], &s[n]->dg ); + } + */ + continue; + } +/* + * Special for ph_unknown, pe_unknown, and alkalinity_unknown + */ + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } +/* + * SURF, sum surface species only into SURFACE mass balance in initial calculation + * into all mass balances in reaction calculation + */ + if (state >= REACTION || master_ptr->s->type == SURF) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reprep(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * If a basis species has been switched, makes new model. + * Unknowns are not changed, but mass-action equations are + * rewritten and lists for mass balance and jacobian are regenerated + */ + int i; +/* + * Initialize s, master, and unknown pointers + */ + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->in == FALSE) + continue; + master[i]->rxn_secondary = master[i]->rxn_primary; + } + resetup_master(); +/* + * Set unknown pointers, unknown types, validity checks + */ + tidy_redox(); + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Free arrays built in build_model + */ + s_x.clear(); + sum_mb1.clear(); + sum_mb2.clear(); + sum_jacob0.clear(); + sum_jacob1.clear(); + sum_jacob2.clear(); + sum_delta.clear(); +/* + * Build model again + */ + build_model(); + k_temp(tc_x, patm_x); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +resetup_master(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * For basis switch, rewrite equations for master species + * Set master_ptr->rxn_secondary, + * master_ptr->pe_rxn, + * and special cases for alkalinity, carbon, and pH. + */ + int i, j; + class master *master_ptr, *master_ptr0; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != MB) + continue; + master_ptr0 = x[i]->master[0]; + for (j = 0; j < x[i]->master.size(); j++) + { + master_ptr = x[i]->master[j]; +/* + * Set flags + */ + if (j == 0) + { + if (master_ptr->s->primary == NULL) + { + master_ptr->rxn_secondary = master_ptr->s->rxn_s; + } + } + else + { + if (master_ptr0->s->primary == NULL) + { + rewrite_master_to_secondary(master_ptr, master_ptr0); + trxn_copy(master_ptr->rxn_secondary); + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mass_action_eqn_x(int stop) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reduce mass-action equation to the master species that are in the model + */ + LDBLE coef_e; + int count, repeat; + int i; + size_t count_rxn_orig; +/* + * Rewrite any secondary master species flagged REWRITE + * Replace pe if necessary + */ + count = 0; + repeat = TRUE; + while (repeat == TRUE) + { + count++; + if (count > MAX_ADD_EQUATIONS) + { + std::string name; + name = "Unknown"; + if (trxn.token[0].s != NULL) + { + name = trxn.token[0].s->name; + } + + input_error++; + error_string = sformatf( "Could not reduce equation " + "to primary and secondary species that are " + "in the model. Species: %s.", name.c_str()); + if (stop == STOP) + { + error_msg(error_string, CONTINUE); + } + else + { + warning_msg(error_string); + } + return (ERROR); + } + repeat = FALSE; + count_rxn_orig = count_trxn; + for (i = 1; i < count_rxn_orig; i++) + { + if (trxn.token[i].s->secondary == NULL) + continue; + if (trxn.token[i].s->secondary->in == REWRITE) + { + repeat = TRUE; + coef_e = + rxn_find_coef(trxn.token[i].s->secondary->rxn_secondary, + "e-"); + trxn_add(trxn.token[i].s->secondary->rxn_secondary, + trxn.token[i].coef, false); + if (equal(coef_e, 0.0, TOL) == FALSE) + { + std::map < std::string, CReaction >::iterator chemRxnIt = pe_x.find(trxn.token[i].s->secondary->pe_rxn); + if ( chemRxnIt == pe_x.end() ) + { + CReaction& rxn_ref = pe_x[trxn.token[i].s->secondary->pe_rxn]; + trxn_add(rxn_ref, trxn.token[i].coef * coef_e, FALSE); + // Create temporary rxn object and add reactions together + CReaction rxn; + trxn_add(rxn, trxn.token[i].coef * coef_e, FALSE); + } + else + { + // Get reaction referred to by iterator and add reactions together + trxn_add(chemRxnIt->second, trxn.token[i].coef * coef_e, FALSE); + } + } + } + } + trxn_combine(); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_potential_factor(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factor to surface mass-action equations. + * Factor is essentially the activity coefficient, representing + * the work required to bring charged ions to the surface + */ + int i; + std::string token; + LDBLE sum_z; + class master *master_ptr; + class unknown *unknown_ptr; + + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + return (OK); + sum_z = 0.0; + master_ptr = NULL; +/* + * Find sum of charge of aqueous species and surface master species + */ + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == AQ || trxn.token[i].s == s_hplus || + trxn.token[i].s == s_eminus) + { + sum_z += trxn.token[i].s->z * trxn.token[i].coef; + } + if (trxn.token[i].s->type == SURF) + { + master_ptr = trxn.token[i].s->primary; + } + } +/* + * Find potential unknown for surface species + */ + if (master_ptr == NULL) + { + error_string = sformatf( + "Did not find a surface species in equation defining %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "One of the following must be defined with SURFACE_SPECIES:"); + error_msg(error_string, CONTINUE); + for (i = 1; i < count_trxn; i++) + { + error_string = sformatf( " %s", trxn.token[i].name); + error_msg(error_string, CONTINUE); + } + input_error++; + return (ERROR); + } + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + } + else + { + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + } +/* + * Make sure there is space + */ + if (count_trxn + 1 > trxn.token.size()) + trxn.token.resize(count_trxn + 1); +/* + * Include psi in mass action equation + */ + if (master_ptr != NULL) + { + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + trxn.token[count_trxn].coef = -2.0 * sum_z; + count_trxn++; + } + else + { + output_msg(sformatf( + "How did this happen in add potential factor?\n")); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_cd_music_factors(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factors for cd_music to surface mass-action equations. + * Factors are essentially the activity coefficient, representing + * the work required to bring charged ions to the three charge layers + * of the cd_music model + */ + int i; + std::string token; + class master *master_ptr; + class unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + master_ptr = NULL; +/* + * Find sum of charge of aqueous species and surface master species + */ + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == SURF) + { + master_ptr = trxn.token[i].s->primary; + } + } +/* + * Find potential unknown for surface species + */ + if (master_ptr == NULL) + { + error_string = sformatf( + "Did not find a surface species in equation defining %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "One of the following must be defined with SURFACE_SPECIES:"); + error_msg(error_string, CONTINUE); + for (i = 1; i < count_trxn; i++) + { + error_string = sformatf( " %s", trxn.token[i].name); + error_msg(error_string, CONTINUE); + } + input_error++; + return (ERROR); + } + token = master_ptr->elt->name; + /* + * Plane 0 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Make sure there is space + */ + if (count_trxn + 3 > trxn.token.size()) + trxn.token.resize(count_trxn + 3); + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[0];*/ + trxn.token[count_trxn].coef = trxn.dz[0]; + + count_trxn++; + + /* + * Plane 1 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[1];*/ + trxn.token[count_trxn].coef = trxn.dz[1]; + count_trxn++; + /* + * Plane 2 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[2];*/ + trxn.token[count_trxn].coef = trxn.dz[2]; + count_trxn++; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_surface_charge_balance(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Include charge balance in list for mass-balance equations + */ + int i; + const char* cptr; + std::string token; + + class master *master_ptr; + class unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + return (OK); + master_ptr = NULL; +/* + * Find master species + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->primary->s->type == SURF) + { + master_ptr = elt_list[i].elt->primary; + break; + } + } + if (i >= count_elts) + { + error_string = sformatf( + "No surface master species found for surface species."); + error_msg(error_string, STOP); + return(OK); + } +/* + * Find potential unknown for surface species + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return(OK); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ +/* + * Include charge balance in list for mass-balance equations + */ + cptr = master_ptr->elt->name; + get_secondary_in_species(&cptr, 1.0); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_cd_music_charge_balances(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factor to surface mass-action equations. + * Factor is essentially the activity coefficient, representing + * the work required to bring charged ions to the surface + */ + int i; + std::string token; + + class master *master_ptr; + class unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + master_ptr = NULL; +/* + * Find master species + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->primary->s->type == SURF) + { + master_ptr = elt_list[i].elt->primary; + break; + } + } + if (i >= count_elts || master_ptr == NULL) + { + error_string = sformatf( + "No surface master species found for surface species."); + error_msg(error_string, STOP); + return ERROR; + } + /* + * Find potential unknown for plane 0 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + const char* cptr = master_ptr->elt->name; + get_secondary_in_species(&cptr, s[n]->dz[0]); + } + /* + * Find potential unknown for plane 1 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + const char* cptr = master_ptr->elt->name; + get_secondary_in_species(&cptr, s[n]->dz[1]); + } + /* + * Find potential unknown for plane 2 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + const char* cptr = master_ptr->elt->name; + get_secondary_in_species(&cptr, s[n]->dz[2]); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_master_to_secondary(class master *master_ptr1, + class master *master_ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for secondary master species in terms of another secondary master species + * Store result in rxn_secondary of master_ptr. + */ + LDBLE coef1, coef2; + class master *master_ptr_p1, *master_ptr_p2; +/* + * Check that the two master species have the same primary master species + */ + master_ptr_p1 = master_ptr1->elt->primary; + master_ptr_p2 = master_ptr2->elt->primary; + if (master_ptr_p1 != master_ptr_p2 || master_ptr_p1 == NULL) + { + error_string = sformatf( + "All redox states must be for the same element. %s\t%s.", + master_ptr1->elt->name, master_ptr2->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Find coefficient of primary master in reaction + */ + coef1 = rxn_find_coef(master_ptr1->rxn_primary, master_ptr_p1->s->name); + coef2 = rxn_find_coef(master_ptr2->rxn_primary, master_ptr_p1->s->name); + if (equal(coef1, 0.0, TOL) == TRUE || equal(coef2, 0.0, TOL) == TRUE) + { + error_string = sformatf( + "One of these equations does not contain master species for element, %s or %s.", + master_ptr1->s->name, master_ptr2->s->name); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Rewrite equation to secondary master species + */ + count_trxn = 0; + trxn_add(master_ptr1->rxn_primary, 1.0, false); + trxn_add(master_ptr2->rxn_primary, -coef1 / coef2, true); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for exchanger in unknowns structures + */ + class master *master_ptr; + std::vector master_ptr_list; + + if (use.Get_exchange_ptr() == NULL) + return (OK); + for (size_t j = 0; j < use.Get_exchange_ptr()->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = use.Get_exchange_ptr()->Get_exchange_comps()[j]; + //{ + // element * elt_ptr = element_store(comp_ref.Get_formula().c_str()); + // if (elt_ptr == NULL || elt_ptr->master == NULL) + // { + // error_string = sformatf( "Component not in database, %s", comp_ref.Get_formula().c_str()); + // input_error++; + // error_msg(error_string, CONTINUE); + // continue; + // } + //} + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { +/* + * Find master species + */ + element * elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + it->first.c_str()); + input_error++; + error_msg(error_string, CONTINUE); + continue; + } + master_ptr = elt_ptr->master; + if (master_ptr->type != EX) + continue; +/* + * Check for data already given + */ + if (master_ptr->in != FALSE) + { + x[master_ptr->unknown->number]->moles += + it->second; + } + else + { +/* + * Set flags + */ + master_ptr_list.clear(); + master_ptr_list.push_back(master_ptr); + master_ptr->in = TRUE; +/* + * Set unknown data + */ + x[count_unknowns]->type = EXCH; + x[count_unknowns]->exch_comp = string_hsave(it->first.c_str()); + x[count_unknowns]->description = elt_ptr->name; + x[count_unknowns]->moles = it->second; + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + count_unknowns++; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for gas phase unknown (sum of partial pressures) + * in unknown structure + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && ( + gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + return setup_fixed_volume_gas(); + } + +/* + * One for total moles in gas + */ + x[count_unknowns]->type = GAS_MOLES; + x[count_unknowns]->description = string_hsave("gas moles"); + x[count_unknowns]->moles = 0.0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + x[count_unknowns]->moles += gc_ptr->Get_moles(); + } + if (x[count_unknowns]->moles <= 0) + x[count_unknowns]->moles = MIN_TOTAL; + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + gas_unknown = x[count_unknowns]; + count_unknowns++; + return (OK); +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for solid solution unknowns (sum of partial pressures) + * in unknown structure + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); +/* + * One for each component in each solid solution + */ + ss_unknown = NULL; + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + for (size_t i = 0; i < ss_ptrs[j]->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[i]); + int l; + class phase* phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + x[count_unknowns]->type = SS_MOLES; + x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->moles = 0.0; + if (comp_ptr->Get_moles() <= 0) + { + comp_ptr->Set_moles(MIN_TOTAL_SS); + } + x[count_unknowns]->moles = comp_ptr->Get_moles(); + comp_ptr->Set_initial_moles(x[count_unknowns]->moles); + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + x[count_unknowns]->ss_name = string_hsave(ss_ptrs[j]->Get_name().c_str()); + x[count_unknowns]->ss_ptr = ss_ptrs[j]; + x[count_unknowns]->ss_comp_name = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->ss_comp_ptr = comp_ptr; + x[count_unknowns]->ss_comp_number = (int) i; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->phase->dn = comp_ptr->Get_dn(); + x[count_unknowns]->phase->dnb = comp_ptr->Get_dnb(); + x[count_unknowns]->phase->dnc = comp_ptr->Get_dnc(); + x[count_unknowns]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + x[count_unknowns]->phase->log10_lambda =comp_ptr->Get_log10_lambda(); + if (ss_unknown == NULL) + ss_unknown = x[count_unknowns]; + count_unknowns++; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_surface(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Fill in data for surface assemblage in unknown structure + */ + std::vector master_ptr_list; + size_t mb_unknown_number; + + if (use.Get_surface_ptr() == NULL) + return (OK); + + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[i]); + /* + * Find master species for each surface, setup unknown structure + */ + cxxNameDouble::iterator jit; + for (jit = comp_ptr->Get_totals().begin(); jit != comp_ptr->Get_totals().end(); jit++) + { + class element *elt_ptr = element_store(jit->first.c_str()); + class master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species not in database for %s, skipping element.", + elt_ptr->name); + warning_msg(error_string); + continue; + } + if (master_ptr->type != SURF) + continue; + /* + * Check that data not already given + */ + if (master_ptr->in != FALSE) + { + error_string = sformatf( + "Analytical data entered twice for %s.", + master_ptr->s->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + /* + * Set flags + */ + master_ptr_list.clear(); + master_ptr_list.push_back(master_ptr); + master_ptr->in = TRUE; + /* + * Setup mass balance unknown + */ + x[count_unknowns]->type = SURFACE; + x[count_unknowns]->description = string_hsave(jit->first.c_str()); + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->surface_comp = string_hsave(comp_ptr->Get_formula().c_str()); + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + x[count_unknowns]->moles = jit->second; + if (surface_unknown == NULL) + surface_unknown = x[count_unknowns]; + x[count_unknowns]->potential_unknown = NULL; + count_unknowns++; + /*if (use.Get_surface_ptr()->edl == FALSE) continue; */ + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + /* + * Setup surface-potential unknown + */ + std::string token = master_ptr->elt->name; + class unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr != NULL) + { + x[count_unknowns - 1]->potential_unknown = unknown_ptr; + } + else + { + /* + * Find master species + */ + replace("_CB", "_psi", token); + master_ptr = master_bsearch(token.c_str()); + master_ptr_list.clear(); + master_ptr_list.push_back(master_ptr); + master_ptr->in = TRUE; + /* + * Find surface charge structure + */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()-> + Find_charge(comp_ptr->Get_charge_name()); + if (charge_ptr == NULL) + { + input_error++; + error_msg(sformatf("Charge structure not defined for surface, %s", use.Get_surface_ptr()->Get_description().c_str()), CONTINUE); + continue; + } + x[count_unknowns]->type = SURFACE_CB; + x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str()); + x[count_unknowns]->related_moles = charge_ptr->Get_grams(); + x[count_unknowns]->mass_water = charge_ptr->Get_mass_water(); + replace("_psi", "_CB", token); + x[count_unknowns]->description = string_hsave(token.c_str()); + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + x[count_unknowns]->moles = 0.0; + x[count_unknowns - 1]->potential_unknown = x[count_unknowns]; + x[count_unknowns]->surface_comp = x[count_unknowns - 1]->surface_comp; + count_unknowns++; + } + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + /* + * Setup 3 surface-potential unknowns + */ + mb_unknown_number = count_unknowns - 1; + std::string token(master_ptr->elt->name); + std::string mass_balance_name(token); + int plane; + for (plane = SURF_PSI; plane <= SURF_PSI2; plane++) + { + std::string cb_suffix("_CB"); + std::string psi_suffix("_psi"); + class unknown **unknown_target; + unknown_target = NULL; + int type = SURFACE_CB; + switch (plane) + { + case SURF_PSI: + type = SURFACE_CB; + unknown_target = + &(x[mb_unknown_number]->potential_unknown); + break; + case SURF_PSI1: + cb_suffix.append("b"); + psi_suffix.append("b"); + type = SURFACE_CB1; + unknown_target = &(x[mb_unknown_number]->potential_unknown1); + break; + case SURF_PSI2: + cb_suffix.append("d"); + psi_suffix.append("d"); + type = SURFACE_CB2; + unknown_target = &(x[mb_unknown_number]->potential_unknown2); + break; + } + class unknown *unknown_ptr = find_surface_charge_unknown(token, plane); + if (unknown_ptr != NULL) + { + *unknown_target = unknown_ptr; + } + else + { + /* + * Find master species + */ + replace(cb_suffix.c_str(), psi_suffix.c_str(), token); + master_ptr = master_bsearch(token.c_str()); + master_ptr_list.clear(); + master_ptr_list.push_back(master_ptr); + master_ptr->in = TRUE; + /* + * Find surface charge structure + */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()-> + Find_charge(comp_ptr->Get_charge_name()); + x[count_unknowns]->type = type; + x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str()); + x[count_unknowns]->related_moles = charge_ptr->Get_grams(); + x[count_unknowns]->mass_water = charge_ptr->Get_mass_water(); + replace(psi_suffix.c_str(), cb_suffix.c_str(), token); + x[count_unknowns]->description = string_hsave(token.c_str()); + x[count_unknowns]->master = master_ptr_list; + /* + * Find surface charge structure + */ + if (plane == SURF_PSI) + { + /*use.Get_surface_ptr()->charge[k].psi_master = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown = + x[count_unknowns]; + } + else if (plane == SURF_PSI1) + { + /*use.Get_surface_ptr()->charge[k].psi_master1 = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown1 = + x[count_unknowns]; + } + else if (plane == SURF_PSI2) + { + /*use.Get_surface_ptr()->charge[k].psi_master2 = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown2 = + x[count_unknowns]; + } + x[count_unknowns]->master[0]->unknown = + x[count_unknowns]; + x[count_unknowns]->moles = 0.0; + x[count_unknowns]->surface_comp = + x[mb_unknown_number]->surface_comp; + count_unknowns++; + } + } + /* Add SURFACE unknown to a list for SURF_PSI */ + class unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + unknown_ptr->comp_unknowns.push_back(x[mb_unknown_number]); + + } + } + } + /* + * check related phases + */ + if (use.Get_surface_ptr()->Get_related_phases()) + { + cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user()); + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0) + { + if (pp_ptr == NULL || + (pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) == + pp_ptr->Get_pp_assemblage_comps().end())) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_min_surface(); + return (FALSE); + } + } + } + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->potential_unknown != x[i]) + continue; + cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp); + std::string name1, name2; + if (comp_j_ptr->Get_phase_name() != + comp_i_ptr->Get_phase_name()) + { + if (comp_i_ptr->Get_phase_name().size() == 0) + { + name1 = "None"; + } + else + { + name1 = comp_i_ptr->Get_phase_name(); + } + if (comp_j_ptr->Get_phase_name().size() == 0) + { + name2 = "None"; + } + else + { + name2 = comp_j_ptr->Get_phase_name(); + } + input_error++; + + error_string = sformatf( + "All surface sites for a single component must be related to the same phase.\n\tSite: %s is related to %s, Site: %s is related to %s", + comp_i_ptr->Get_master_element().c_str(), name1.c_str(), + comp_j_ptr->Get_master_element().c_str(), name2.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + /* + * check related kinetics + */ + if (use.Get_surface_ptr()->Get_related_rate()) + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user()); + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0) + { + if (kinetics_ptr == NULL || + (kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL)) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + this->tidy_kin_surface(); + return (FALSE); + } + } + } + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->potential_unknown != x[i]) + continue; + cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp); + if (comp_j_ptr->Get_rate_name() != + comp_i_ptr->Get_rate_name()) + { + std::string name1, name2; + if (comp_i_ptr->Get_rate_name().size() == 0) + { + name1 = "None"; + } + else + { + name1 = comp_i_ptr->Get_rate_name(); + } + if (comp_j_ptr->Get_rate_name().size() == 0) + { + name2 = "None"; + } + else + { + name2 = comp_j_ptr->Get_rate_name(); + } + input_error++; + error_string = sformatf( + "All surface sites for a single component must be related to the same kinetic reaction.\n\tSite: %s is related to %s, Site: %s is related to %s", + comp_i_ptr->Get_master_element().c_str(), name1.c_str(), + comp_j_ptr->Get_master_element().c_str(), name2.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +class unknown * Phreeqc:: +find_surface_charge_unknown(std::string &str, int plane) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes name for the potential unknown and returns in str_ptr + * Returns NULL if this unknown not in unknown list else + * returns a pointer to the potential unknown + */ + std::string token; + Utilities::replace("_", " ", str); + std::string::iterator b = str.begin(); + std::string::iterator e = str.end(); + CParser::copy_token(token, b, e); + if (plane == SURF_PSI) + { + token.append("_CB"); + } + else if (plane == SURF_PSI1) + { + token.append("_CBb"); + } + else if (plane == SURF_PSI2) + { + token.append("_CBd"); + } + str = token; + for (int i = 0; i < count_unknowns; i++) + { + if (strcmp(str.c_str(), x[i]->description) == 0) + { + return (x[i]); + } + } + return (NULL); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_master_rxn(const std::vector &master_ptr_list, const std::string &pe_rxn) +/* ---------------------------------------------------------------------- */ +{ +/* + * Rewrites rxn_secondary for all redox states in list + * First, in = TRUE; others, in = REWRITE + */ + class master *master_ptr, *master_ptr0; +/* + * Set master_ptr->in, master_ptr->rxn + */ + master_ptr0 = master_ptr_list[0]; + for (size_t j = 0; j < master_ptr_list.size(); j++) + { + master_ptr = master_ptr_list[j]; +/* + * Check that data not already given + */ + if (master_ptr->s == s_h2o) + { + error_string = sformatf( + "Cannot enter concentration data for O(-2),\n\tdissolved oxygen is O(0),\n\tfor mass of water, use -water identifier."); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + + if (master_ptr->in != FALSE) + { + if (master_ptr->s != s_eminus && master_ptr->s != s_hplus) + { + error_string = sformatf( + "Analytical data entered twice for %s.", + master_ptr->s->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } +/* + * Set flags + */ + if (j == 0) + { + master_ptr->in = TRUE; + if (master_ptr->s->primary == NULL) + { + master_ptr->rxn_secondary = master_ptr->s->rxn_s; + } + } + else + { + master_ptr->in = REWRITE; + if (master_ptr0->s->primary == NULL) + { + rewrite_master_to_secondary(master_ptr, master_ptr0); + trxn_copy(master_ptr->rxn_secondary); + } + } + master_ptr->pe_rxn = string_hsave(pe_rxn.c_str()); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_PR(std::vector phase_ptrs, LDBLE P, LDBLE TK, LDBLE V_m) +/* ---------------------------------------------------------------------- */ +/* Calculate fugacity and fugacity coefficient for gas pressures if critical T and P + are defined. + 1) Solve molar volume V_m or total pressure P from Peng-Robinson's EOS: + P = R * T / (V_m - b) - a * aa / (V_m^2 + 2 * b * V_m - b^2) + a = 0.457235 * (R * T_c)^2 / P_c + b = 0.077796 * R * T_c / P_c + aa = (1 + kk * (1 - T_r^0.5))^2 + kk = 0.37464 + 1.54226 * omega - 0.26992 * omega^2 + T_r = T / T_c + multicomponent gas phase: + use: b_sum = Sum(x_i * b), x_i is mole-fraction + a_aa_sum = Sum_i( Sum_j(x_i * x_j * (a_i * aa_i * a_j * aa_j)^0.5) ) + 2) Find the fugacity coefficient phi for gas i: + log(phi_i) = B_ratio * (z - 1) - log(z - B) + A / (2.8284 * B) * (B_ratio - 2 / a_aa_sum * a_aa_sum2) *\ + log((z + 2.4142 * B) / (z - 0.4142 * B)) + B_ratio = b_i / b_sum + A = a_aa_sum * P / R_TK^2 + B = b_sum * P / R_TK + a_aa_sum2 = Sum_j(x_j * (a_aa_i * a_aa_j)^0.5 + 3) correct the solubility of gas i with: + pr_si_f = log10(phi_i) - Delta_V_i * (P - 1) / (2.303 * R * TK); +*/ +{ + int i, i1, n_g = (int) phase_ptrs.size(); + LDBLE T_c, P_c; + LDBLE A, B, B_r, /*b2,*/ kk, oo, a_aa, T_r; + LDBLE m_sum, /*b_sum, a_aa_sum,*/ a_aa_sum2; + LDBLE phi; + LDBLE /*R_TK,*/ R = R_LITER_ATM; /* L atm / (K mol) */ + LDBLE r3[4], r3_12, rp, rp3, rq, rz, ri, ri1, one_3 = 0.33333333333333333; + LDBLE disct, vinit, v1, ddp, dp_dv, dp_dv2; + int it; + class phase *phase_ptr, *phase_ptr1; + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + bool halved; + R_TK = R * TK; + m_sum = b_sum = a_aa_sum = 0.0; + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (n_g > 1) + { + if (phase_ptr->moles_x == 0) + continue; + m_sum += phase_ptr->moles_x; + } + if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + error_msg("Cannot calculate a mixture of ideal and Peng_Robinson gases,\n please define Tc and Pc for the active gases in PHASES.", STOP); + //continue; + if (!phase_ptr->pr_a) + { + T_c = phase_ptr->t_c; + P_c = phase_ptr->p_c; + phase_ptr->pr_a = 0.457235 * R * R * T_c * T_c / P_c; + phase_ptr->pr_b = 0.077796 * R * T_c / P_c; + T_r = TK / T_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; +// phase_ptr->pr_in = true; + } + if (phase_ptr->pr_tk != TK) + { + T_r = TK / phase_ptr->t_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; +// phase_ptr->pr_in = true; + } + } + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (n_g == 1) + { + phase_ptr->fraction_x = 1.0; + break; + } + if (m_sum == 0) + return (OK); + phase_ptr->fraction_x = phase_ptr->moles_x / m_sum; + } + + for (i = 0; i < n_g; i++) + { + a_aa_sum2 = 0.0; + phase_ptr = phase_ptrs[i]; + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + // continue; + b_sum += phase_ptr->fraction_x * phase_ptr->pr_b; + for (i1 = 0; i1 < n_g; i1++) + { + phase_ptr1 = phase_ptrs[i1]; + //if (phase_ptr1->t_c == 0.0 || phase_ptr1->p_c == 0.0) + // continue; + if (phase_ptr1->fraction_x == 0) + continue; + a_aa = sqrt(phase_ptr->pr_a * phase_ptr->pr_alpha * + phase_ptr1->pr_a * phase_ptr1->pr_alpha); + if (!strcmp(phase_ptr->name, "H2O(g)")) + { + if (!strcmp(phase_ptr1->name, "CO2(g)")) + a_aa *= 0.81; // Soreide and Whitson, 1992, FPE 77, 217 + else if (!strcmp(phase_ptr1->name, "H2S(g)") || !strcmp(phase_ptr1->name, "H2Sg(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr1->name, "CH4(g)") || !strcmp(phase_ptr1->name, "Mtg(g)") || !strcmp(phase_ptr1->name, "Methane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "N2(g)") || !strcmp(phase_ptr1->name, "Ntg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "Ethane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "Propane(g)")) + a_aa *= 0.45; + } + if (!strcmp(phase_ptr1->name, "H2O(g)")) + { + if (!strcmp(phase_ptr->name, "CO2(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "H2S(g)") || !strcmp(phase_ptr->name, "H2Sg(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "CH4(g)") || !strcmp(phase_ptr->name, "Mtg(g)") || !strcmp(phase_ptr->name, "Methane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "N2(g)") || !strcmp(phase_ptr->name, "Ntg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Ethane(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Propane(g)")) + a_aa *= 0.45; + } + a_aa_sum += phase_ptr->fraction_x * phase_ptr1->fraction_x * a_aa; + a_aa_sum2 += phase_ptr1->fraction_x * a_aa; + } + phase_ptr->pr_aa_sum2 = a_aa_sum2; + } + b2 = b_sum * b_sum; + + if (V_m) + { + P = R_TK / (V_m - b_sum) - a_aa_sum / (V_m * (V_m + 2 * b_sum) - b2); + if (iterations > 0 && P < 150 && V_m < 1.01) + { + // check for 3-roots... + r3[1] = b_sum - R_TK / P; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // the discriminant of the cubic eqn... + disct = 18. * r3[1] * r3[2] * r3[3] - + 4. * pow(r3[1], 3) * r3[3] + + r3[1] * r3[1] * r3[2] * r3[2] - + 4. * pow(r3[2], 3) - + 27. * r3[3] * r3[3]; + //if (iterations > 50) + // it = 0; // debug + if (disct > 0) + { + // 3-roots, find the largest P... + it = 0; + halved = false; + ddp = 1e-9; + v1 = vinit = 0.729; + dp_dv = f_Vm(v1, this); + while (fabs(dp_dv) > 1e-11 && it < 40) + { + it +=1; + dp_dv2 = f_Vm(v1 - ddp, this); + v1 -= (dp_dv * ddp / (dp_dv - dp_dv2)); + if (!halved && (v1 > vinit || v1 < 0.03)) + { + if (vinit > 0.329) + vinit -= 0.1; + else + vinit -=0.05; + if (vinit < 0.03) + { + vinit = halve(f_Vm, 0.03, 1.0, 1e-3); + if (f_Vm(vinit - 2e-3, this) < 0) + vinit = halve(f_Vm, vinit + 2e-3, 1.0, 1e-3); + halved = true; + } + v1 = vinit; + } + dp_dv = f_Vm(v1, this); + if (fabs(dp_dv) < 1e-11) + { + if (f_Vm(v1 - 1e-4, this) < 0) + { + v1 = halve(f_Vm, v1 + 1e-4, 1.0, 1e-3); + dp_dv = f_Vm(v1, this); + } + } + } + if (it == 40) + { +// accept a (possible) whobble in the curve... +// error_msg("No convergence when calculating P in Peng-Robinson.", STOP); + } + if (V_m < v1 && it < 40) + P = R_TK / (v1 - b_sum) - a_aa_sum / (v1 * (v1 + 2 * b_sum) - b2); + } + } + if (P <= 0) // iterations = -1 + P = 1; + } else + { + if (P < 1e-10) + P = 1e-10; + r3[1] = b_sum - R_TK / P; + r3_12 = r3[1] * r3[1]; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // solve t^3 + rp*t + rq = 0. + // molar volume V_m = t - r3[1] / 3... + rp = r3[2] - r3_12 / 3; + rp3 = rp * rp * rp; + rq = (2.0 * r3_12 * r3[1] - 9.0 * r3[1] * r3[2]) / 27 + r3[3]; + rz = rq * rq / 4 + rp3 / 27; + if (rz >= 0) // Cardono's method... + { + ri = sqrt(rz); + if (ri + rq / 2 <= 0) + { + V_m = pow(ri - rq / 2, one_3) + pow(- ri - rq / 2, one_3) - r3[1] / 3; + } + else + { + ri = - pow(ri + rq / 2, one_3); + V_m = ri - rp / (3.0 * ri) - r3[1] / 3; + } + } + else // use complex plane... + { + ri = sqrt(- rp3 / 27); // rp < 0 + ri1 = acos(- rq / 2 / ri); + V_m = 2.0 * pow(ri, one_3) * cos(ri1 / 3) - r3[1] / 3; + } + } + // calculate the fugacity coefficients... + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (phase_ptr->fraction_x == 0.0) + { + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1; + phase_ptr->pr_si_f = 0.0; + continue; + } + phase_ptr->pr_p = phase_ptr->fraction_x * P; + rz = P * V_m / R_TK; + A = a_aa_sum * P / (R_TK * R_TK); + B = b_sum * P / R_TK; + B_r = phase_ptr->pr_b / b_sum; + if (rz > B) + { + phi = B_r * (rz - 1) - log(rz - B) + A / (2.828427 * B) * (B_r - 2.0 * phase_ptr->pr_aa_sum2 / a_aa_sum) * + log((rz + 2.41421356 * B) / (rz - 0.41421356 * B)); + //phi = (phi > 4.44 ? 4.44 : (phi < -3 ? -3 : phi)); + //if (phi > 4.44) + // phi = 4.44; + } + else + phi = -3.0; // fugacity coefficient = 0.05 + //if (/*!strcmp(phase_ptr->name, "H2O(g)") && */phi < -3) + //{ + //// avoid such phi... + // phi = -3; + //} + phase_ptr->pr_phi = exp(phi); + phase_ptr->pr_si_f = phi / LOG_10; + // for initial equilibrations, adapt log_k of the gas phase... + if (state < REACTION) + { + rho_0 = calc_rho_0(TK - 273.15, P); + calc_dielectrics(TK - 273.15, P); + phase_ptr->lk = calc_lk_phase(phase_ptr, TK, P); + } + phase_ptr->pr_in = true; + } + if (gas_phase_ptr && iterations > 2) + { + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_p(P); + } + gas_phase_ptr->Set_v_m(V_m); + return (OK); + } + return (V_m); +} + +LDBLE Phreeqc:: +f_Vm(LDBLE v1, void *cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE ff; + Phreeqc * pThis; + pThis = (Phreeqc *) cookie; + + ff = v1 * (v1 + 2 * pThis->b_sum) - pThis->b2; + LDBLE dp_dv = -pThis->R_TK / pow(v1 - pThis->b_sum, 2) + + pThis->a_aa_sum * 2 * (v1 + pThis->b_sum) / (ff * ff); + return dp_dv; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ + //LDBLE si_org; +/* + * Fills in data for pure_phase assemglage in unknown structure + */ + + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); +/* + * Setup unknowns + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + int j; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + assert(phase_ptr); + x[count_unknowns]->type = PP; + x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->pp_assemblage_comp_name = x[count_unknowns]->description; + x[count_unknowns]->pp_assemblage_comp_ptr = comp_ptr; + x[count_unknowns]->moles = comp_ptr->Get_moles(); + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->si = comp_ptr->Get_si(); + //si_org = comp_ptr->Get_si_org(); + /* si_org is used for Peng-Robinson gas, with the fugacity + coefficient added later in adjust_pure_phases, + when rxn_x has been defined for each phase in the model */ + x[count_unknowns]->delta = comp_ptr->Get_delta(); + x[count_unknowns]->dissolve_only = comp_ptr->Get_dissolve_only(); + if (pure_phase_unknown == NULL) + pure_phase_unknown = x[count_unknowns]; + count_unknowns++; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +adjust_setup_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class phase *phase_ptr; + LDBLE si_org, p, t; +/* + * Fills in data for pure_phase assemglage in unknown structure + */ + + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); +/* + * Adjust si for gases + */ + for (i = 0; i < count_unknowns; i++) + { + std::vector phase_ptrs; + if (x[i]->type == PP) + { + phase_ptr = x[i]->phase; + phase_ptrs.push_back(phase_ptr); + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + si_org = comp_ptr->Get_si_org(); + if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0) + { + if (si_org > 3.5) + si_org = 3.5; + p = exp(si_org * LOG_10); + patm_x = p; + t = use.Get_solution_ptr()->Get_tc() + 273.15; + if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk) + { + calc_PR(phase_ptrs, p, t, 0); + } + x[i]->si = si_org + phase_ptr->pr_si_f; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in data in unknown structure for the solution + */ + class master *master_ptr; + cxxSolution *solution_ptr; + const char* cptr; + std::string token; + class master_isotope *master_isotope_ptr; + class phase *phase_ptr; + + solution_ptr = use.Get_solution_ptr(); + count_unknowns = 0; + + /* + * Treat minor isotopes as special in initial solution calculation + */ + if (solution_ptr->Get_initial_data()) + { + std::map::iterator comp_it = solution_ptr->Get_initial_data()->Get_comps().begin(); + for ( ; comp_it != solution_ptr->Get_initial_data()->Get_comps().end(); comp_it++) + { + master_ptr = master_bsearch(comp_it->first.c_str()); + if ((master_ptr != NULL) + && (master_ptr->minor_isotope == TRUE) + && (initial_solution_isotopes == FALSE)) + { + master_isotope_ptr = master_isotope_search(comp_it->first.c_str()); + if (master_isotope_ptr != NULL) + { + master_isotope_ptr->ratio = comp_it->second.Get_input_conc(); + } + } + } + } + cxxNameDouble::iterator it = solution_ptr->Get_totals().begin(); + for ( ; it != solution_ptr->Get_totals().end(); it++) + { + cxxISolutionComp *comp_ptr = NULL; + if (solution_ptr->Get_initial_data()) + { + std::map::iterator comp_it; + comp_it = solution_ptr->Get_initial_data()->Get_comps().find(it->first.c_str()); + comp_ptr = &(comp_it->second); + } + cptr = it->first.c_str(); + copy_token(token, &cptr); + master_ptr = master_bsearch(token.c_str()); +/* + * Check that total not <= zero + */ + if (it->second <= 0.0) + { + if (strcmp(token.c_str(), "H(1)") != 0 && strcmp(token.c_str(), "E") != 0) + { + continue; + } + } +/* + * Find master species + */ + master_ptr = master_bsearch(token.c_str()); + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species not in database for %s, skipping element.", + it->first.c_str()); + warning_msg(error_string); + continue; + } + if (master_ptr->type != AQ) + { + /* solution_ptr->totals[i].skip = TRUE; */ + error_string = sformatf( + "Only aqueous concentrations are allowed in solution data, ignoring %s.", + it->first.c_str()); + warning_msg(error_string); + continue; + } +/* + * Store list of master species pointers, set master[i].in and master[i].rxn for list + */ + x[count_unknowns]->master = get_list_master_ptrs(cptr, master_ptr); + if (comp_ptr) + { + setup_master_rxn(x[count_unknowns]->master, comp_ptr->Get_pe_reaction()); + } + else + { + setup_master_rxn(x[count_unknowns]->master, "pe"); + } + +/* + * Set default unknown data + */ + x[count_unknowns]->type = MB; + x[count_unknowns]->description = string_hsave(it->first.c_str()); + for (size_t j = 0; j < x[count_unknowns]->master.size(); j++) + { + x[count_unknowns]->master[j]->unknown = x[count_unknowns]; + } + x[count_unknowns]->moles = it->second; +/* + * Set pointers + */ + cptr = it->first.c_str(); + copy_token(token, &cptr); + Utilities::str_tolower(token); + if (strstr(token.c_str(), "alk") != NULL) + { + if (alkalinity_unknown == NULL) + { + x[count_unknowns]->type = ALK; + alkalinity_unknown = x[count_unknowns]; + } + else + { + error_msg("Alkalinity entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "c") == 0 || strcmp(token.c_str(), "c(4)") == 0) + { + if (carbon_unknown == NULL) + { + carbon_unknown = x[count_unknowns]; + } + else + { + error_msg("Carbon entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "h(1)") == 0) + { + if (ph_unknown == NULL) + { + ph_unknown = x[count_unknowns]; + } + else + { + error_msg("pH entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "e") == 0) + { + if (pe_unknown == NULL) + { + pe_unknown = x[count_unknowns]; + } + else + { + error_msg("pe entered more than once.", CONTINUE); + input_error++; + } + } +/* + * Charge balance unknown + */ + if (comp_ptr && comp_ptr->Get_equation_name().size() > 0) + { + cptr = comp_ptr->Get_equation_name().c_str(); + copy_token(token, &cptr); + Utilities::str_tolower(token); + if (strstr(token.c_str(), "charge") != NULL) + { + if (charge_balance_unknown == NULL) + { + charge_balance_unknown = x[count_unknowns]; + x[count_unknowns]->type = CB; + if (charge_balance_unknown == ph_unknown) + { + x[count_unknowns]->moles = solution_ptr->Get_cb(); + } + } + else + { + error_msg("Charge balance specified for more" + " than one species.", CONTINUE); + input_error++; + } + } + else + { +/* + * Solution phase boundaries + */ + int l; + phase_ptr = phase_bsearch(comp_ptr->Get_equation_name().c_str(), &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Expected a mineral name, %s.", + comp_ptr->Get_equation_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->si = comp_ptr->Get_phase_si(); + /* For Peng-Robinson gas, the fugacity + coefficient is added later in adjust_setup_solution, + when rxn_x has been defined for each phase in the model */ + if (solution_phase_boundary_unknown == NULL) + { + solution_phase_boundary_unknown = x[count_unknowns]; + } + } + } + count_unknowns++; + } +/* + * Set mb_unknown + */ + if (count_unknowns > 0) + mb_unknown = x[0]; +/* + * Special for alkalinity + */ + if (alkalinity_unknown != NULL) + { + if (carbon_unknown != NULL) + { +/* + * pH adjusted to obtain given alkalinity + */ + if (ph_unknown == NULL) + { + output_msg(sformatf("\npH will be adjusted to obtain desired alkalinity.\n\n")); + ph_unknown = alkalinity_unknown; + master_ptr = master_bsearch("H(1)"); + alkalinity_unknown->master[0] = master_ptr; + master_ptr->in = TRUE; + master_ptr->unknown = ph_unknown; + ph_unknown->master[0] = master_ptr; + ph_unknown->description = string_hsave("H(1)"); + } + else + { + error_msg("pH adjustment is needed for alkalinity but" + " charge balance or a phase boundary was also specified.", + CONTINUE); + input_error++; + } +/* + * Carbonate ion adjusted to obtain given alkalintiy + */ + } + else + { + if (alkalinity_unknown->master[0]->s->secondary != NULL) + { + alkalinity_unknown->master[0]->s->secondary->in = TRUE; + alkalinity_unknown->master[0]->s->secondary->unknown = + alkalinity_unknown; + } + else + { + error_msg + ("Error in definition of Alkalinity in SOLUTION_MASTER_SPECIES and SOLUTION_SPECIES.\n\tAlkalinity master species should be same as master species for C(4).", + CONTINUE); + input_error++; + } + } + } + //if (pitzer_model == FALSE && sit_model == FALSE) + { + /* + * Ionic strength + */ + mu_unknown = x[count_unknowns]; + x[count_unknowns]->description = string_hsave("Mu"); + x[count_unknowns]->type = MU; + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->moles = 0.0; + count_unknowns++; + } + /* + * Activity of water + */ + ah2o_unknown = x[count_unknowns]; + ah2o_unknown->description = string_hsave("A(H2O)"); + ah2o_unknown->type = AH2O; + ah2o_unknown->number = count_unknowns; + ah2o_unknown->master.push_back(master_bsearch("O")); + ah2o_unknown->master[0]->unknown = ah2o_unknown; + ah2o_unknown->moles = 0.0; + count_unknowns++; + + if (state >= REACTION) + { +/* + + * Reaction: pH for charge balance + */ + ph_unknown = x[count_unknowns]; + ph_unknown->description = string_hsave("pH"); + ph_unknown->type = CB; + ph_unknown->moles = solution_ptr->Get_cb(); + ph_unknown->number = count_unknowns; + ph_unknown->master.push_back(s_hplus->primary); + ph_unknown->master[0]->unknown = ph_unknown; + charge_balance_unknown = ph_unknown; + count_unknowns++; +/* + * Reaction: pe for total hydrogen + */ + pe_unknown = x[count_unknowns]; + mass_hydrogen_unknown = x[count_unknowns]; + mass_hydrogen_unknown->description = string_hsave("Hydrogen"); + mass_hydrogen_unknown->type = MH; +#ifdef COMBINE + mass_hydrogen_unknown->moles = + solution_ptr->Get_total_h() - 2 * solution_ptr->Get_total_o(); +#else + mass_hydrogen_unknown->moles = solution_ptr->total_h; +#endif + mass_hydrogen_unknown->number = count_unknowns; + mass_hydrogen_unknown->master.push_back(s_eminus->primary); + mass_hydrogen_unknown->master[0]->unknown = mass_hydrogen_unknown; + count_unknowns++; +/* + * Reaction H2O for total oxygen + */ + mass_oxygen_unknown = x[count_unknowns]; + mass_oxygen_unknown->description = string_hsave("Oxygen"); + mass_oxygen_unknown->type = MH2O; + mass_oxygen_unknown->moles = solution_ptr->Get_total_o(); + mass_oxygen_unknown->number = count_unknowns; + mass_oxygen_unknown->master.push_back(s_h2o->primary); + count_unknowns++; + } +/* + * Validity tests + */ + if ((ph_unknown != NULL) && + (ph_unknown == charge_balance_unknown) + && (alkalinity_unknown != NULL)) + { + error_msg("pH adustment cannot attain charge balance" + " when alkalinity is fixed.", CONTINUE); + input_error++; + } + if ((alkalinity_unknown != NULL) && + (alkalinity_unknown->type == CB || + alkalinity_unknown->type == SOLUTION_PHASE_BOUNDARY)) + { + error_msg("Alkalinity cannot be used with charge balance" + " or solution phase boundary constraints.", CONTINUE); + input_error++; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +adjust_setup_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in data in unknown structure for the solution + */ + int i; + class phase *phase_ptr; + LDBLE p, t; + + for (i = 0; i < count_unknowns; i++) + { + std::vector phase_ptrs; + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY; + phase_ptr = x[i]->phase; + phase_ptrs.push_back(phase_ptr); + if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0) + { + if (x[i]->si > 3.5) + x[i]->si = 3.5; + p = exp(x[i]->si * LOG_10); + patm_x = p; + t = use.Get_solution_ptr()->Get_tc() + 273.15; + if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk) + { + calc_PR(phase_ptrs, p, t, 0); + } + x[i]->si += phase_ptr->pr_si_f; + } + } + } + return (OK); + +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_unknowns(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Counts unknowns and allocates space for unknown structures + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); +/* + * Calculate maximum number of unknowns + */ + max_unknowns = 0; +/* + * Count mass balance in solution + */ + if (solution_ptr->Get_initial_data()) + { + max_unknowns += (int) solution_ptr->Get_initial_data()->Get_comps().size(); + } + else + { + max_unknowns += (int) solution_ptr->Get_totals().size(); + } +/* + * Add 5 for ionic strength, activity of water, charge balance, total H, total O + */ + max_unknowns += 5; +/* + * Count pure phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + max_unknowns += (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size(); + } +/* + * Count exchange + */ + if (use.Get_exchange_ptr() != NULL) + { + cxxExchange *exchange_ptr = use.Get_exchange_ptr(); + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + element * elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + error_string = sformatf( + "Master species missing for element %s", + it->first.c_str()); + error_msg(error_string, STOP); + } + if (elt_ptr->master->type == EX) + { + max_unknowns++; + } + } + } + } +/* + * Count surfaces + */ + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + { + max_unknowns += (int) use.Get_surface_ptr()->Get_surface_comps().size() + + (int) use.Get_surface_ptr()->Get_surface_charges().size(); + } + else + { + max_unknowns += (int)(use.Get_surface_ptr()->Get_surface_comps().size() + + 4 * use.Get_surface_ptr()->Get_surface_charges().size()); + } + } +/* + * Count gas components + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + max_unknowns += (int) gas_phase_ptr->Get_gas_comps().size(); + } + else + { + max_unknowns++; + } + } +/* + * Count solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + max_unknowns += (int) ss_ptrs[i]->Get_ss_comps().size(); + } + } +/* + * Pitzer/Sit + */ + max_unknowns++; + if (pitzer_model == TRUE || sit_model == TRUE) + { + max_unknowns += (int)s.size(); + } + +/* + * Allocate space for pointer array and structures + */ + x.resize(max_unknowns); + for (i = 0; i < max_unknowns; i++) + { + x[i] = (class unknown *) unknown_alloc(); + x[i]->number = i; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_dn(int k, LDBLE * source, int row, LDBLE coef_in, LDBLE * gamma_source) +/* ---------------------------------------------------------------------- */ +{ +/* + * Stores the terms for d moles of species k in solution into row, multiplied + * by coef_in + */ + size_t col; + LDBLE coef; + class rxn_token *rxn_ptr; + class master *master_ptr; + + if (equal(coef_in, 0.0, TOL) == TRUE) + { + return (OK); + } +/* Gamma term for d molality of species */ +/* Note dg includes molality as a factor */ + + row = row * ((int)count_unknowns + 1); + if (s[k]->type != SURF && s[k] != s_h2o) + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + "Activity coefficient", (double) (-1.0 * coef_in), + row / (count_unknowns + 1), mu_unknown->number)); + } + /* mu term */ + if (gamma_source != NULL) + { + store_jacob(gamma_source, &my_array[(size_t)row + (size_t)mu_unknown->number], + -1.0 * coef_in); + } + } +/* + * Mass of water factor + */ + if (mass_oxygen_unknown != NULL && s[k]->type != EX && s[k]->type != SURF) + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + mass_oxygen_unknown->master[0]->s->name, + (double) coef_in, row / (count_unknowns + 1), + mass_oxygen_unknown->number)); + } + store_jacob(source, &(my_array[(size_t)row + (size_t)mass_oxygen_unknown->number]), + coef_in); + } + if (s[k] == s_h2o) + return (OK); + for (rxn_ptr = &s[k]->rxn_x.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + //if (debug_prep == TRUE) + //{ + // output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + //} + if (master_ptr == NULL ||master_ptr->unknown == NULL) + continue; + col = master_ptr->unknown->number; + coef = coef_in * rxn_ptr->coef; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + store_jacob(source, &(my_array[(size_t)row + (size_t)col]), coef); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_jacob(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds a new item to either sum_jacob1 or sum_jacob2 + * If coef is 1.0, adds to sum_jacob1, which does not require a multiply + * Otherwise, adds to sum_jacob2, which allows multiply by coef + */ + if (equal(coef, 1.0, TOL) == TRUE) + { + size_t count_sum_jacob1 = sum_jacob1.size(); + sum_jacob1.resize(count_sum_jacob1 + 1); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\tjacob1 %d\n", (int)count_sum_jacob1)); + } + sum_jacob1[count_sum_jacob1].source = source; + sum_jacob1[count_sum_jacob1].target = target; + } + else + { + size_t count_sum_jacob2 = sum_jacob2.size(); + sum_jacob2.resize(count_sum_jacob2 + 1); + if (debug_prep == TRUE) + { + output_msg(sformatf("\tjacob2 %d\n", count_sum_jacob2)); + } + sum_jacob2[count_sum_jacob2].source = source; + sum_jacob2[count_sum_jacob2].target = target; + sum_jacob2[count_sum_jacob2].coef = coef; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_jacob0(int row, int column, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Stores in list a constant coef which will be added into jacobian array + */ + size_t count_sum_jacob0 = sum_jacob0.size(); + sum_jacob0.resize(count_sum_jacob0 + 1); + sum_jacob0[count_sum_jacob0].target = + &(my_array[(size_t)row * (count_unknowns + 1) + (size_t)column]); + sum_jacob0[count_sum_jacob0].coef = coef; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_mb(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds item to list sum_mb1 or sum_mb2 + * If coef is 1.0, adds to sum_mb1, which does not require a multiply + * else, adds to sum_mb2, which will multiply by coef + */ + if (equal(coef, 1.0, TOL) == TRUE) + { + size_t count_sum_mb1 = sum_mb1.size(); + sum_mb1.resize(count_sum_mb1 + 1); + sum_mb1[count_sum_mb1].source = source; + sum_mb1[count_sum_mb1].target = target; + } + else + { + size_t count_sum_mb2 = sum_mb2.size(); + sum_mb2.resize(count_sum_mb2 + 1); + sum_mb2[count_sum_mb2].source = source; + sum_mb2[count_sum_mb2].coef = coef; + sum_mb2[count_sum_mb2].target = target; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_sum_deltas(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * List sum_delta is summed to determine the change in the mass of + * each element due to mass transfers of minerals, changes show up + * in x[i]->delta. These may be multiplied by a factor under some + * situations where the entire calculated step is not taken + */ + size_t count_sum_delta = sum_delta.size(); + sum_delta.resize(count_sum_delta + 1); + sum_delta[count_sum_delta].source = source; + sum_delta[count_sum_delta].target = target; + sum_delta[count_sum_delta].coef = coef; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +switch_bases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check if activity of first master species is predominant among activities of + * secondary master species included in mass balance. + */ + int i; + int first; + int return_value; + LDBLE la, la1; + class master *master_ptr; + + return_value = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != MB) + continue; + if (x[i]->type == PITZER_GAMMA) + break; + first = 0; + la = x[i]->master[0]->s->la; + for (size_t j = 1; j < x[i]->master.size(); j++) + { + la1 = x[i]->master[j]->s->lm + x[i]->master[j]->s->lg; + if (first == 0 && la1 > la + 10.) + { + la = la1; + first = (int)j; + } + else if (first != 0 && la1 > la) + { + la = la1; + first = (int)j; + } + } + if (first != 0) + { + master_ptr = x[i]->master[0]; + x[i]->master[0] = x[i]->master[first]; + x[i]->master[0]->in = TRUE; + x[i]->master[first] = master_ptr; + x[i]->master[first]->in = REWRITE; +/* + fprintf(stderr, "Switching bases to %s.\tIteration %d\n", + x[i]->master[0]->s->name, iterations, la, x[i]->master[0]->s->la); + */ + x[i]->master[0]->s->la = la; + x[i]->la = la; + log_msg(sformatf( "Switching bases to %s.\tIteration %d\n", + x[i]->master[0]->s->name, iterations)); + return_value = TRUE; + } + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_redox(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write pe redox reactions (rxn in struct pe_data) in terms of master species + * defined in analytical data + * + */ + std::string token, tok1, tok2; + class master *master_ptr1, *master_ptr2; +/* + * Keep valences of oxygen and hydrogen in model, if not already in + */ + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->primary == TRUE && + (master[i]->s == s_hplus || master[i]->s == s_h2o)) + { + int j = i + 1; + while (j < (int)master.size() && master[j]->elt->primary == master[i]) + { + if (master[j]->in == FALSE && master[j]->s != master[i]->s) + { + master[j]->in = REWRITE; + master[j]->pe_rxn = master[i]->pe_rxn; + } + j++; + } + } + } +/* + * Writes equations for e- for each redox couple used in solution n + */ + std::map < std::string, CReaction >::iterator it; + for (it = pe_x.begin(); it != pe_x.end(); it++) + { + if (strcmp_nocase(it->first.c_str(), "pe") == 0) + { + CReaction temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + token = it->first; + replace("/", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + +/* + * Get redox states and elements from redox couple + */ + CParser::copy_token(tok1, b, e); + CParser::copy_token(tok2, b, e); +/* + * Find master species + */ + master_ptr1 = master_bsearch(tok1.c_str()); + master_ptr2 = master_bsearch(tok2.c_str()); + if (master_ptr1 != NULL && master_ptr2 != NULL) + { + rewrite_master_to_secondary(master_ptr1, master_ptr2); +/* + * Rewrite equation to e- + */ + trxn_swap("e-"); + } + else + { + error_string = sformatf( + "Cannot find master species for redox couple, %s.", + it->first.c_str()); + error_msg(error_string, STOP); + } + if (inout() == FALSE) + { + error_string = sformatf( + "Analytical data missing for redox couple, %s\n\t Using pe instead.", + it->first.c_str()); + warning_msg(error_string); + CReaction temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + CReaction rxn(count_trxn + 1); + trxn_copy(rxn); + CReaction temp_rxn(rxn); + it->second = temp_rxn; + } + } + } +/* + * Rewrite equations to master species that are "in" the model + */ + for (it = pe_x.begin(); it != pe_x.end(); it++) + { + count_trxn = 0; + trxn_add(it->second, 1.0, FALSE); + if (write_mass_action_eqn_x(CONTINUE) == FALSE) + { + error_string = sformatf( "Could not rewrite redox " + "couple equation for %s\n\t Possibly missing data for one " + "of the redox states.", it->first.c_str()); + warning_msg(error_string); + error_string = sformatf( "Using pe instead of %s.", + it->first.c_str()); + warning_msg(error_string); + CReaction temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + CReaction rxn(count_trxn + 1); + trxn_copy(rxn); + CReaction temp_rxn(rxn); + it->second = temp_rxn; + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mb_eqn_x(void) +/* ---------------------------------------------------------------------- */ +{ + int count, repeat; + int i; + size_t count_rxn_orig; + class master *master_ptr; +/* + * Rewrite any secondary master species flagged REWRITE + * Don`t add in any pe reactions + */ + count = 0; + repeat = TRUE; + while (repeat == TRUE) + { + count++; + if (count > MAX_ADD_EQUATIONS) + { + std::string name; + name = "Unknown"; + if (trxn.token[0].s != NULL) + { + name = trxn.token[0].s->name; + } + error_string = sformatf( "Could not reduce equation " + "to primary and secondary species that are " + "in the model. Species: %s.", name.c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + repeat = FALSE; + count_rxn_orig = count_trxn; + for (i = 1; i < count_rxn_orig; i++) + { + if (trxn.token[i].s->secondary == NULL) + continue; + if (trxn.token[i].s->secondary->in == REWRITE) + { + repeat = TRUE; + trxn_add(trxn.token[i].s->secondary->rxn_secondary, + trxn.token[i].coef, false); + } + } + trxn_combine(); + } +/* + * + */ + count_elts = 0; + paren_count = 0; + for (size_t i = 1; i < count_trxn; i++) + { + size_t j = count_elts; + const char* cptr = trxn.token[i].s->name; + get_elts_in_species(&cptr, trxn.token[i].coef); + for (size_t k = j; k < count_elts; k++) + { + if (trxn.token[i].s->secondary != NULL) + { + master_ptr = trxn.token[i].s->secondary->elt->primary; + } + else + { + master_ptr = trxn.token[i].s->primary; + } + if (elt_list[k].elt == master_ptr->elt) + { + elt_list[k].coef = 0.0; + break; + } + } + if (trxn.token[i].s->secondary == NULL) + { + const char* cptr = trxn.token[i].s->primary->elt->name; + get_secondary_in_species(&cptr, trxn.token[i].coef); + } + else + { + cptr = trxn.token[i].s->secondary->elt->name; + get_secondary_in_species(&cptr, trxn.token[i].coef); + } + } + elt_list_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mb_for_species_list(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets up data to add to species_list + * Original secondary redox states are retained + */ + int i; +/* + * Start with secondary reaction + */ + count_trxn = 0; + trxn_add(s[n]->rxn_s, 1.0, false); +/* + * Copy to elt_list + */ + count_elts = 0; + paren_count = 0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->secondary == NULL) + { + const char* cptr = trxn.token[i].s->primary->elt->name; + get_secondary_in_species(&cptr, trxn.token[i].coef); + } + else + { + const char* cptr = trxn.token[i].s->secondary->elt->name; + if (get_secondary_in_species(&cptr, trxn.token[i].coef) == ERROR) + { + input_error++; + error_string = sformatf( "Error parsing %s.", trxn.token[i].s->secondary->elt->name); + error_msg(error_string, CONTINUE); + } + } + } + for (i = 0; i < count_elts; i++) + { + if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) + { + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + elt_list[count_elts].elt = element_h_one; + elt_list[count_elts].coef = elt_list[i].coef * 2; + count_elts++; + } + } + elt_list_combine(); + s[n]->next_sys_total.clear(); + s[n]->next_sys_total = elt_list_vsave(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_phase_sys_total(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets up data to add to species_list + * Original secondary redox states are retained + */ + int i; +/* + * Start with secondary reaction + */ + count_trxn = 0; + trxn_add_phase(phases[n]->rxn_s, 1.0, false); +/* + * Copy to elt_list + */ + count_elts = 0; + paren_count = 0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->secondary == NULL) + { + const char* cptr = trxn.token[i].s->primary->elt->name; + get_secondary_in_species(&cptr, trxn.token[i].coef); + } + else + { + const char* cptr = trxn.token[i].s->secondary->elt->name; + get_secondary_in_species(&cptr, trxn.token[i].coef); + } + } + for (i = 0; i < count_elts; i++) + { + if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) + { + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + elt_list[count_elts].elt = element_h_one; + elt_list[count_elts].coef = elt_list[i].coef * 2; + count_elts++; + } + } + elt_list_combine(); + phases[n]->next_sys_total.clear(); + phases[n]->next_sys_total = elt_list_vsave(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_lk_phase(phase *p_ptr, LDBLE TK, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ +/* + * calculate log_k for a single phase, correct for pressure + * see calc_vm (below) for details. + */ + + CReaction *r_ptr = (p_ptr->rxn_x.size() ? &p_ptr->rxn_x :\ + (p_ptr->rxn_s.size() ? &p_ptr->rxn_s : NULL)); + if (!r_ptr) + return 0.0; + if (!r_ptr->logk[vm0]) // in case Vm of the phase is 0... + return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM); + + LDBLE tc = TK - 273.15; + LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x); + LDBLE d_v = 0.0; + species * s_ptr; + + for (size_t i = 0; r_ptr->token[i].name; i++) + { + if (!r_ptr->token[i].s) + continue; + s_ptr = r_ptr->token[i].s; + //if (!strcmp(s_ptr->name, "H+")) + if (s_ptr == s_hplus) + continue; + //if (!strcmp(s_ptr->name, "e-")) + if (s_ptr == s_eminus) + continue; + //if (!strcmp(s_ptr->name, "H2O")) + if (s_ptr == s_h2o) + { + d_v += r_ptr->token[i].coef * 18.016 / calc_rho_0(tc, pa); + continue; + } + else if (s_ptr->logk[vma1]) + { + /* supcrt volume at I = 0... */ + d_v += r_ptr->token[i].coef * + (s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s + + (s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s - + s_ptr->logk[wref] * QBrn); + //if (dgdP && s_ptr->z) + //{ + // LDBLE re = s_ptr->z * s_ptr->z / (s_ptr->logk[wref] / 1.66027e5 + s_ptr->z / 3.082); + // LDBLE Z3 = fabs(pow(s_ptr->z, 3)) / re / re - s_ptr->z / 9.498724; + // d_v += r_ptr->token[i].coef * ZBrn * 1.66027e5 * Z3 * dgdP; + //} + if (s_ptr->z) + { + /* the ionic strength term * I^0.5... */ + if (s_ptr->logk[b_Av] < 1e-5) + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0) + { + LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s; + if (s_ptr->logk[vmi4] == 1.0) + d_v += bi * mu_x; + else + d_v += bi * pow(mu_x, s_ptr->logk[vmi4]); + } + } + } + //else if (s_x[i]->millero[0]) + else if (s_ptr->millero[0]) + { + /* Millero volume at I = 0... */ + d_v += s_ptr->millero[0] + tc * (s_ptr->millero[1] + tc * s_ptr->millero[2]); + if (s_ptr->z) + { + /* the ionic strength terms... */ + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu + + (s_ptr->millero[3] + tc * (s_ptr->millero[4] + tc * s_ptr->millero[5])) * mu_x; + } + } + else + continue; + } + d_v -= p_ptr->logk[vm0]; + r_ptr->logk[delta_v] = d_v; + if (r_ptr->token[0].name && !strcmp(r_ptr->token[0].name, "H2O(g)")) + r_ptr->logk[delta_v] = 0.0; + + return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_vm(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculate molar volumes for aqueous species with a Redlich type eqn: + Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4). + * Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2 + * for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221. + Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av. + If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5). + DH_Av and DH_B are from calc_dielectrics(tc, pa). + * coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228). + * b4 = logk[vmi4], or + * coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2 + */ + if (llnl_temp.size() > 0) return OK; + LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x); + for (int i = 0; i < (int)this->s_x.size(); i++) + { + //if (!strcmp(s_x[i]->name, "H2O")) + if (s_x[i] == s_h2o) + { + s_x[i]->logk[vm_tc] = 18.016 / rho_0; + continue; + } + if (s_x[i]->logk[vma1]) + { + /* supcrt volume at I = 0... */ + s_x[i]->rxn_x.logk[vm_tc] = s_x[i]->logk[vma1] + s_x[i]->logk[vma2] / pb_s + + (s_x[i]->logk[vma3] + s_x[i]->logk[vma4] / pb_s) / TK_s - + s_x[i]->logk[wref] * QBrn; + /* A (small) correction by Shock et al., 1992, for 155 < tc < 255, P_sat < P < 1e3. + The vma1..a4 and wref numbers are refitted for major cations and anions on xpts, + probably invalidates the correction. */ + //if (dgdP && s_x[i]->z) + //{ + // LDBLE re = s_x[i]->z * s_x[i]->z / (s_x[i]->logk[wref] / 1.66027e5 + s_x[i]->z / 3.082); + // LDBLE Z3 = fabs(pow(s_x[i]->z, 3)) / re / re - s_x[i]->z / 9.498724; + // s_x[i]->rxn_x.logk[vm_tc] += ZBrn * 1.66027e5 * Z3 * dgdP; + //} + if (s_x[i]->z) + { + /* the ionic strength term * I^0.5... */ + if (s_x[i]->logk[b_Av] < 1e-5) + s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + /* pitzer... */ + //s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * + // log(1 + s_x[i]->logk[b_Av] * sqrt(mu_x)) / s_x[i]->logk[b_Av]; + /* extended DH... */ + s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_x[i]->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_x[i]->logk[vmi1] != 0.0 || s_x[i]->logk[vmi2] != 0.0 || s_x[i]->logk[vmi3] != 0.0) + { + LDBLE bi = s_x[i]->logk[vmi1] + s_x[i]->logk[vmi2] / TK_s + s_x[i]->logk[vmi3] * TK_s; + if (s_x[i]->logk[vmi4] == 1.0) + s_x[i]->rxn_x.logk[vm_tc] += bi * mu_x; + else + s_x[i]->rxn_x.logk[vm_tc] += bi * pow(mu_x, s_x[i]->logk[vmi4]); + } + } + } + else if (s_x[i]->millero[0]) + { + /* Millero volume at I = 0... */ + s_x[i]->rxn_x.logk[vm_tc] = s_x[i]->millero[0] + tc * (s_x[i]->millero[1] + tc * s_x[i]->millero[2]); + if (s_x[i]->z) + { + /* the ionic strength terms... */ + s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu + + (s_x[i]->millero[3] + tc * (s_x[i]->millero[4] + tc * s_x[i]->millero[5])) * mu_x; + } + } + else + continue; + + /* for calculating delta_v of the reaction... */ + s_x[i]->logk[vm_tc] = s_x[i]->rxn_x.logk[vm_tc]; + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +k_temp(LDBLE tc, LDBLE pa) /* pa - pressure in atm */ +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates log k's for all species and pure_phases + */ + + if (tc == current_tc && pa == current_pa && ((fabs(mu_x - current_mu) < 1e-3 * mu_x) || !mu_terms_in_logk)) + return OK; + + int i; + LDBLE tempk = tc + 273.15; +/* + * Calculate log k for all aqueous species + */ + /* calculate relative molar volumes for tc... */ + rho_0 = calc_rho_0(tc, pa); + + pa = patm_x; + calc_dielectrics(tc, pa); + + calc_vm(tc, pa); + + mu_terms_in_logk = false; + for (i = 0; i < (int)this->s_x.size(); i++) + { + //if (s_x[i]->rxn_x.logk[vm_tc]) + /* calculate delta_v for the reaction... */ + s_x[i]->rxn_x.logk[delta_v] = calc_delta_v(*&s_x[i]->rxn_x, false); + if (tc == current_tc && s_x[i]->rxn_x.logk[delta_v] == 0) + continue; + mu_terms_in_logk = true; + s_x[i]->lk = k_calc(s_x[i]->rxn_x.logk, tempk, pa * PASCAL_PER_ATM); + } +/* + * Calculate log k for all pure phases + */ + for (i = 0; i < (int)phases.size(); i++) + { + if (phases[i]->in == TRUE) + { + + phases[i]->rxn_x.logk[delta_v] = calc_delta_v(*&phases[i]->rxn_x, true) - + phases[i]->logk[vm0]; + if (phases[i]->rxn_x.logk[delta_v]) + mu_terms_in_logk = true; + phases[i]->lk = k_calc(phases[i]->rxn_x.logk, tempk, pa * PASCAL_PER_ATM); + + } + } +/* + * Calculate miscibility gaps for solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + if (fabs(tempk - ss_ptrs[i]->Get_tk()) > 0.01) + { + ss_prep(tempk, ss_ptrs[i], FALSE); + } + } + } + + current_tc = tc; + current_pa = pa; + current_mu = mu_x; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +k_calc(LDBLE * l_logk, LDBLE tempk, LDBLE presPa) +/* ---------------------------------------------------------------------- */ +{ + /* + * Calculates log k at specified temperature and pressure + * Returns calculated log k. + * + * delta_v is in cm3/mol. + */ + + /* Molar energy */ + LDBLE me = tempk * R_KJ_DEG_MOL; + + /* Pressure difference */ + LDBLE delta_p = presPa - REF_PRES_PASCAL; + + /* Calculate new log k value for this temperature and pressure */ + LDBLE lk = l_logk[logK_T0] + - l_logk[delta_h] * (298.15 - tempk) / (LOG_10 * me * 298.15) + + l_logk[T_A1] + + l_logk[T_A2] * tempk + + l_logk[T_A3] / tempk + + l_logk[T_A4] * log10(tempk) + + l_logk[T_A5] / (tempk * tempk) + + l_logk[T_A6] * tempk * tempk; + if (delta_p > 0) + /* cm3 * J /mol = 1e-9 m3 * kJ /mol */ + lk -= l_logk[delta_v] * 1E-9 * delta_p / (LOG_10 * me); + return lk; +} + + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +save_model(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * mark master species + */ + for (i = 0; i < (int)master.size(); i++) + { + master[i]->last_model = FALSE; + if (master[i]->total > 0) + { + if (master[i]->primary == TRUE) + { + master[i]->last_model = TRUE; + } + else + { + /* mark primary master */ + master[i]->s->secondary->elt->primary->last_model = TRUE; + } + } + } +/* + * save list of phase pointers for gas phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + last_model.gas_phase_type = gas_phase_ptr->Get_type(); + last_model.gas_phase.resize(gas_phase_ptr->Get_gas_comps().size()); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); + last_model.gas_phase[i] = phase_ptr; + } + } + else + { + last_model.gas_phase_type = cxxGasPhase::GP_UNKNOWN; + last_model.gas_phase.clear(); + } +/* + * save list of names of solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + last_model.ss_assemblage.resize(use.Get_ss_assemblage_ptr()->Get_SSs().size()); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + last_model.ss_assemblage[j] = string_hsave(ss_ptrs[j]->Get_name().c_str()); + } + } + else + { + last_model.ss_assemblage.clear(); + } +/* + * save list of phase pointers for pp_assemblage + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + last_model.pp_assemblage.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size()); + last_model.add_formula.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size()); + last_model.si.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size()); + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + i = 0; + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, false); + assert(phase_ptr); + last_model.pp_assemblage[i] = phase_ptr; + last_model.add_formula[i] = string_hsave(it->second.Get_add_formula().c_str()); + last_model.si[i] = it->second.Get_si(); + i++; + } + } + else + { + last_model.pp_assemblage.clear(); + last_model.add_formula.clear(); + last_model.si.clear(); + } +/* + * save data for surface + */ + if (use.Get_surface_ptr() != NULL) + { + /* comps */ + last_model.surface_comp.resize(use.Get_surface_ptr()->Get_surface_comps().size()); + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + last_model.surface_comp[i] = string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str()); + } + /* charge */ + last_model.surface_charge.resize(use.Get_surface_ptr()->Get_surface_charges().size()); + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + last_model.surface_charge[i] = string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str()); + } + last_model.dl_type = use.Get_surface_ptr()->Get_dl_type(); + /*last_model.edl = use.Get_surface_ptr()->edl; */ + last_model.surface_type = use.Get_surface_ptr()->Get_type(); + } + else + { + last_model.dl_type = cxxSurface::NO_DL; + /*last_model.edl = -1; */ + last_model.surface_type = cxxSurface::UNKNOWN_DL; + last_model.surface_comp.clear(); + last_model.surface_charge.clear(); + } + + current_tc = NAN; + current_pa = NAN; + current_mu = NAN; + mu_terms_in_logk = true; + + last_model.numerical_fixed_volume = numerical_fixed_volume; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_same_model(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Force new model to be built in prep + */ + if (last_model.force_prep) + { + last_model.force_prep = false; + return (FALSE); + } + if (state == TRANSPORT && cell_data[cell_no].same_model) + return TRUE; +/* + * Check master species + */ + for (i = 0; i < (int)master.size(); i++) + { +/* + output_msg(sformatf("%s\t%e\t%d\n", master[i]->elt->name, + master[i]->total, master[i]->last_model); + */ + if (master[i]->s == s_hplus || master[i]->s == s_h2o) + continue; + if (master[i]->total > MIN_TOTAL && master[i]->last_model == TRUE) + { + if (master[i]->s->secondary != NULL) + { + if (master[i]->s->secondary->unknown != NULL) + continue; + } + else + { + if (master[i]->unknown != NULL) + continue; + } + } + if (master[i]->total <= MIN_TOTAL && master[i]->last_model == FALSE) + continue; + return (FALSE); + } +/* + * Check gas_phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (last_model.gas_phase.size() != (int)gas_phase_ptr->Get_gas_comps().size()) + return (FALSE); + if (last_model.numerical_fixed_volume != numerical_fixed_volume) + return (FALSE); + if (last_model.gas_phase_type != gas_phase_ptr->Get_type()) + return (FALSE); + for (i = 0; i < (int) gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); + if (last_model.gas_phase[i] != phase_ptr) + { + return (FALSE); + } + } + } + else + { + if (last_model.gas_phase.size() > 0) + return (FALSE); + } +/* + * Check solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + if (last_model.ss_assemblage.size() != (int) use.Get_ss_assemblage_ptr()->Get_SSs().size()) + return (FALSE); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + if (last_model.ss_assemblage[i] != string_hsave(ss_ptrs[i]->Get_name().c_str())) + { + return (FALSE); + } + } + } + else + { + if (last_model.ss_assemblage.size() > 0) + return (FALSE); + } +/* + * Check pure_phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (last_model.pp_assemblage.size() != (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size()) + return (FALSE); + + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + i = 0; + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + assert(phase_ptr); + if (last_model.pp_assemblage[i] != phase_ptr) + { + return (FALSE); + } + if (last_model.add_formula[i] != + string_hsave(it->second.Get_add_formula().c_str())) + { + return (FALSE); + } + i++; + /* A. Crapsi + if (last_model.si[i] != use.Get_pp_assemblage_ptr()->pure_phases[i].si) + { + return (FALSE); + } + */ + } + } + else + { + if (last_model.pp_assemblage.size() > 0) + return (FALSE); + } +/* + * Check surface + */ + if (use.Get_surface_ptr() != NULL) + { + if (last_model.surface_comp.size() != (int) use.Get_surface_ptr()->Get_surface_comps().size()) + return (FALSE); + if (last_model.surface_charge.size() != (int) use.Get_surface_ptr()->Get_surface_charges().size()) + return (FALSE); + if (last_model.dl_type != use.Get_surface_ptr()->Get_dl_type()) + return (FALSE); + /*if (last_model.edl != use.Get_surface_ptr()->edl) return(FALSE); */ + if (last_model.surface_type != use.Get_surface_ptr()->Get_type()) + return (FALSE); + /* + if (last_model.only_counter_ions != use.Get_surface_ptr()->only_counter_ions) return(FALSE); + */ + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (last_model.surface_comp[i] != + string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str())) + return (FALSE); + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0) + { + cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user()); + if (pp_ptr == NULL || (pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) == + pp_ptr->Get_pp_assemblage_comps().end())) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_min_surface(); + return (FALSE); + } + } + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0) + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user()); + if (kinetics_ptr == NULL || + (kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL)) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_kin_surface(); + return (FALSE); + } + } + } + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + if (last_model.surface_charge[i] != + string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str())) + return (FALSE); + } + } + else + { + if (last_model.surface_comp.size() > 0) + return (FALSE); + } +/* + * Model is the same + */ + return (TRUE); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_min_exch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Defines proportionality factor between mineral and exchanger to + * jacob0 + */ + int j, k, jj; + size_t row; + class master *master_ptr; + class unknown *unknown_ptr; + LDBLE coef; + + if (use.Get_exchange_ptr() == NULL) + return (OK); + cxxExchange *ex_ptr = use.Get_exchange_ptr(); + int n_user = ex_ptr->Get_n_user(); + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user); + if (exchange_ptr == NULL) + { + input_error++; + error_string = sformatf( "Exchange %d not found.", + use.Get_n_exchange_user()); + error_msg(error_string, CONTINUE); + return ERROR; + } + n_user = exchange_ptr->Get_n_user(); + if (!exchange_ptr->Get_related_phases()) + return (OK); + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[i]; + if (comp_ref.Get_phase_name().size() == 0) + continue; + // Find exchange master + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + class master *exchange_master = NULL; + for ( ; it != nd.end(); it++) + { + element * elt_ptr = element_store(it->first.c_str()); + assert (elt_ptr); + if (elt_ptr->master->type == EX) + { + exchange_master = elt_ptr->master; + } + } + if (exchange_master == NULL) + { + input_error++; + error_string = sformatf( + "Did not find master exchange species for %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + /* find unknown number */ + for (j = (int)count_unknowns - 1; j >= 0; j--) + { + if (x[j]->type != EXCH) + continue; + if (x[j]->master[0] == exchange_master) + break; + } + for (k = (int)count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ref.Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ref.Get_phase_name().c_str()) == 0) + break; + } + if (j == -1) + { + input_error++; + error_string = sformatf( + "Did not find unknown for master exchange species %s", + exchange_master->s->name); + error_msg(error_string, CONTINUE); + } + if (j == -1 || k == -1) + continue; +/* + * Build jacobian + */ + + /* charge balance */ + store_jacob0((int)charge_balance_unknown->number, (int)x[k]->number, + comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion()); + store_sum_deltas(&delta[k], &charge_balance_unknown->delta, + -comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion()); + + + /* mole balance balance */ + count_elts = 0; + paren_count = 0; + { + const char* cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, 1.0); + } +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (jj = 0; jj < count_elts; jj++) + { + master_ptr = elt_list[jj].elt->primary; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find unknown for %s, exchange related to mineral %s", + elt_list[jj].elt->primary->elt->name, comp_ref.Get_phase_name().c_str()); + error_msg(error_string, STOP); + } + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr->s->type == EX) + { + if (equal + (x[j]->moles, + x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion(), + 5.0 * convergence_tolerance) == FALSE) + { + error_string = sformatf( + "Resetting number of sites in exchanger %s (=%e) to be consistent with moles of phase %s (=%e).\n%s", + master_ptr->s->name, (double) x[j]->moles, + comp_ref.Get_phase_name().c_str(), + (double) (x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion()), + "\tHas equilibrium_phase assemblage been redefined?\n"); + warning_msg(error_string); + x[j]->moles = + x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion(); + } + } + coef = elt_list[jj].coef; + if (master_ptr->s == s_hplus) + { + row = mass_hydrogen_unknown->number; + unknown_ptr = mass_hydrogen_unknown; + } + else if (master_ptr->s == s_h2o) + { + row = mass_oxygen_unknown->number; + unknown_ptr = mass_oxygen_unknown; + } + else + { + row = master_ptr->unknown->number; + unknown_ptr = master_ptr->unknown; + } + store_jacob0((int)row, (int)x[k]->number, + coef * comp_ref.Get_phase_proportion()); + store_sum_deltas(&delta[k], &unknown_ptr->delta, + -coef * comp_ref.Get_phase_proportion()); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_min_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Defines proportionality factor between mineral and surface to + * jacob0 + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + cxxSurface *surface_ptr = use.Get_surface_ptr(); + if (!surface_ptr->Get_related_phases()) + return (OK); + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + if (comp_ptr->Get_phase_name().size() == 0) + continue; + class element *elt_ptr = element_store(comp_ptr->Get_master_element().c_str()); + /* find unknown number */ + int j; + for (j = (int)count_unknowns - 1; j >= 0; j--) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->master[0] == elt_ptr->master) + break; + } + int k; + for (k = (int)count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (j == -1) + { + input_error++; + error_string = sformatf( + "Did not find unknown for master surface species %s", + elt_ptr->master->s->name); + error_msg(error_string, CONTINUE); + } + if (j == -1 || k == -1) + continue; + + /* update grams == moles in this case */ + if (j < count_unknowns - 1 && x[(size_t)j + 1]->type == SURFACE_CB) + { + store_sum_deltas(&delta[k], &(x[(size_t)j + 1]->related_moles), -1.0); + } + + /* charge balance */ + store_jacob0((int)charge_balance_unknown->number, (int)x[k]->number, + comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion()); + store_sum_deltas(&delta[k], &charge_balance_unknown->delta, + -comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion()); + count_elts = 0; + paren_count = 0; + { + /* Add specified formula for all types of surfaces */ + const char* cptr1 = comp_ptr->Get_formula().c_str(); + get_elts_in_species(&cptr1, 1.0); + } +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int jj = 0; jj < count_elts; jj++) + { + class master * master_ptr = elt_list[jj].elt->primary; + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find unknown for %s, surface related to mineral %s", + elt_list[jj].elt->primary->elt->name, comp_ptr->Get_phase_name().c_str()); + error_msg(error_string, STOP); + } + if (master_ptr->s->type == SURF) + { + if (equal + (x[j]->moles, + x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion(), + 5.0 * convergence_tolerance) == FALSE) + { + error_string = sformatf( + "Resetting number of sites in surface %s (=%e) to be consistent with moles of phase %s (=%e).\n%s", + master_ptr->s->name, (double) x[j]->moles, + comp_ptr->Get_phase_name().c_str(), + (double) (x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion()), + "\tHas equilibrium_phase assemblage been redefined?\n"); + warning_msg(error_string); + x[j]->moles = + x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion(); + } + } + LDBLE coef = elt_list[jj].coef; + size_t row; + class unknown *unknown_ptr; + if (master_ptr->s == s_hplus) + { + row = mass_hydrogen_unknown->number; + unknown_ptr = mass_hydrogen_unknown; + } + else if (master_ptr->s == s_h2o) + { + row = mass_oxygen_unknown->number; + unknown_ptr = mass_oxygen_unknown; + } + else + { + row = master_ptr->unknown->number; + unknown_ptr = master_ptr->unknown; + } + store_jacob0((int)row, (int)x[k]->number, + coef * comp_ptr->Get_phase_proportion()); + store_sum_deltas(&delta[k], &unknown_ptr->delta, + -coef * comp_ptr->Get_phase_proportion()); + } + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_related_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for surface assemblage in unknown structure + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + if (!use.Get_surface_ptr()->Get_related_phases()) + return (OK); + + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + int k; + for (k = (int)count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (k == -1) + continue; + + x[i]->phase_unknown = x[k]; +/* !!!!! */ + x[i]->moles = x[k]->moles * comp_ptr->Get_phase_proportion(); + + } + } + else if (x[i]->type == SURFACE_CB) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)i-1]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + int k; + for (k = (int)count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_i_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_i_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (k == -1) + continue; + + x[i]->phase_unknown = x[k]; + /* !!!! Added for security, not checked... */ + x[i]->related_moles = x[k]->moles * comp_i_ptr->Get_phase_proportion(); + } + } + } + return (OK); +} diff --git a/phreeqcpp/print.cpp b/phreeqcpp/print.cpp new file mode 100644 index 00000000..610b6c13 --- /dev/null +++ b/phreeqcpp/print.cpp @@ -0,0 +1,3634 @@ +// -*- coding: windows-1252 -*- +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include +#include +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "Surface.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +array_print(LDBLE * array_l, int row_count, int column_count, + int l_max_column_count) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + + for (i = 0; i < row_count; i++) + { + k = 0; + output_msg(sformatf("%d\n", i)); + for (j = 0; j < column_count; j++) + { + if (k > 7) + { + output_msg(sformatf("\n")); + k = 0; + } + output_msg(sformatf("%11.2e", + (double) array_l[i * l_max_column_count + j])); + k++; + } + if (k != 0) + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\n")); + } + output_msg(sformatf("\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_pr_in_false(void) +/* ---------------------------------------------------------------------- */ +{ + // set pr_in to false for subsequent steps... + if (use.Get_pp_assemblage_in()) + { + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + x[i]->phase->pr_in = false; + } + } + if (use.Get_gas_phase_ptr()) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr) + phase_ptr->pr_in = false; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_all(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print each block of results in sequence to file output + * also print selected output to punch_file + * Each routine is controlled by a variable in structure print. + * print.all == FALSE will turn off all prints. + */ + if (pr.all == FALSE) + { + set_pr_in_false(); + return (OK); + } +/* + * Makes sorted list including all species for each valence state + */ + if (pr.surface == TRUE || pr.exchange == TRUE || pr.species == TRUE) + { + species_list_sort(); + } +/* + * Print results + */ + s_h2o->lm = s_h2o->la; + print_using(); + print_mix(); + print_reaction(); + print_kinetics(); + print_user_print(); + print_gas_phase(); + print_pp_assemblage(); + print_ss_assemblage(); + print_surface(); + print_exchange(); + print_initial_solution_isotopes(); + print_isotope_ratios(); + print_isotope_alphas(); + print_totals(); + print_eh(); + print_species(); + print_alkalinity(); + print_saturation_indices(); + if (!pr.saturation_indices) + set_pr_in_false(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_all(void) +/* ---------------------------------------------------------------------- */ +{ +//#ifndef PHREEQ98 /* if not PHREEQ98 use the standard declaration */ +// if (pr.hdf == FALSE && (punch.in == FALSE || pr.punch == FALSE) && user_graph->commands == NULL) +// return (OK); +///* +// * Punch results +// */ +// if (state == TRANSPORT || state == PHAST || state == ADVECTION) +// { +// use.Get_kinetics_ptr() = kinetics_bsearch(use.Get_n_kinetics_user(), &i); +// } +// else if (use.Get_kinetics_in() != FALSE) +// { +// use.Get_kinetics_ptr() = kinetics_bsearch(-2, &i); +// } +//#else /* if PHREEQ98 execute punch_user_graph first, that is, before punch.in and pr.punch is checked */ + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else if (use.Get_kinetics_in() != FALSE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } +#if defined PHREEQ98 + if (pr.user_graph == TRUE) + { + punch_user_graph(); + } +#elif defined MULTICHART + if (pr.user_graph == TRUE) + { + chart_handler.Punch_user_graph(this); + } +#endif + //if (pr.hdf == FALSE && (punch.in == FALSE || pr.punch == FALSE)) + if (pr.hdf == FALSE && (SelectedOutput_map.size() == 0 || pr.punch == FALSE)) + return (OK); + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_active() /* || + current_selected_output->Get_punch_ostream() == NULL*/) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + // UserPunch + std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user()); + current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second); + punch_identifiers(); + punch_totals(); + punch_molalities(); + punch_activities(); + punch_pp_assemblage(); + punch_saturation_indices(); + punch_gas_phase(); + punch_kinetics(); + punch_ss_assemblage(); + punch_isotopes(); + punch_calculate_values(); + punch_user_punch(); + /* + * new line for punch_file + */ + if (current_selected_output->Get_new_line() && this->Get_output_newline()) + punch_msg("\n"); + this->Set_output_newline(true); + + /* + * signal end of row + */ + fpunchf_end_row("\n"); + punch_flush(); + } + current_selected_output = NULL; + current_user_punch = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_diffuse_layer(cxxSurfaceCharge *charge_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints total moles of each element in diffuse layer + * Remove comment to print total moles of each species + */ + LDBLE mass_water_surface, r, sum_surfs; + LDBLE molality, moles_excess, moles_surface, d; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Find position of component in surface charge data + */ + int j; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge * charge_ptr_search = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (charge_ptr->Get_name() == charge_ptr_search->Get_name()) + { + break; + } + } + if (j >= count_unknowns) + { + error_string = sformatf( + "In print_diffuse_layer: component not found, %s.", + charge_ptr->Get_name().c_str()); + error_msg(error_string, STOP); + } +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + + if (mass_water_surfaces_x != 0) + { + d = 100 * charge_ptr->Get_mass_water() / mass_water_surfaces_x; + } + else + { + d = 0.0; + } + output_msg(sformatf( + "\tWater in diffuse layer: %8.3e kg, %4.1f%% of total DDL-water.\n", + (double) charge_ptr->Get_mass_water(), (double) d)); + if (use.Get_surface_ptr()->Get_debye_lengths() > 0 && d > 0) + { + sum_surfs = 0.0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge * charge_ptr_search = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + sum_surfs += + charge_ptr_search->Get_specific_area() * + charge_ptr_search->Get_grams(); + } + r = 0.002 * mass_water_bulk_x / sum_surfs; + output_msg(sformatf( + "\tRadius of total pore: %8.3e m; of free pore: %8.3e m.\n", + (double) r, (double) (r - use.Get_surface_ptr()->Get_thickness()))); + } + + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\n\t\tDistribution of species in diffuse layer\n\n")); + output_msg(sformatf( + "\n\tSpecies \t Moles \tMoles excess\t g\n")); + } + if ((mass_water_surface = charge_ptr->Get_mass_water())) + { + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int)this->s_x.size(); j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + moles_excess = mass_water_aq_x * molality * (charge_ptr->Get_g_map()[s_x[j]->z].Get_g() * + s_x[j]->erm_ddl + + mass_water_surface / + mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + moles_surface = mass_water_surface * molality + moles_excess; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf("\t%-12s\t%12.3e\t%12.3e\t%12.3e\n", + s_x[j]->name, moles_surface, moles_excess, + charge_ptr->Get_g_map()[s_x[j]->z].Get_g())); + } + /* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + /* + strcpy(token, s_h2o->name); + ptr = &(token[0]); + get_elts_in_species (&ptr, mass_water_surface / gfw_water); + */ + elt_list_combine(); + /* + * Print totals + */ + if (use.Get_surface_ptr()->Get_dl_type() != cxxSurface::DONNAN_DL) + { + output_msg(sformatf( + "\n\tTotal moles in diffuse layer (excluding water)\n\n")); + } + else + { + LDBLE exp_g = charge_ptr->Get_g_map()[1].Get_g() * mass_water_aq_x / mass_water_surface + 1; + LDBLE psi_DL = -log(exp_g) * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ; + output_msg(sformatf( + "\n\tTotal moles in diffuse layer (excluding water), Donnan calculation.")); + output_msg(sformatf( + "\n\tDonnan Layer potential, psi_DL = %10.3e V.\n\tBoltzmann factor, exp(-psi_DL * F / RT) = %9.3e (= c_DL / c_free if z is +1).\n\n", + psi_DL, exp_g)); + } + output_msg(sformatf("\tElement \t Moles\n")); + for (j = 0; j < count_elts; j++) + { + output_msg(sformatf("\t%-14s\t%12.4e\n", + elt_list[j].elt->name, (double)elt_list[j].coef)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_eh(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints eh calculated from redox couples + * Only calculates eh if two redox states of an element have mass balance + * equations. + */ + int i, j, k, first; + LDBLE pe, eh; + class master *master_ptr0, *master_ptr1; + char token[MAX_LENGTH]; + + if (pr.eh == FALSE || pr.all == FALSE) + return (OK); + + tk_x = tc_x + 273.15; + + first = TRUE; + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->in != TRUE) + continue; + if (master[i]->primary == TRUE) + continue; +/* + * Secondary master species has mass balance equation + */ + master_ptr0 = master[i]->elt->primary; + for (k = i + 1; k < (int)master.size(); k++) + { + if (master[k]->in != TRUE) + continue; + master_ptr1 = master[k]->elt->primary; + if (master_ptr1 != master_ptr0) + break; +/* + * Another secondary master species of same element has mass balance equation + * Rewrite equations to calculate pe + */ + rewrite_master_to_secondary(master[k], master[i]); + trxn_swap("e-"); +/* debug + trxn_print(); + */ +/* + * Calculate pe, eh + */ + pe = -k_calc(trxn.logk, tk_x, patm_x * PASCAL_PER_ATM); + for (j = 1; j < count_trxn; j++) + { + pe -= trxn.token[j].s->la * trxn.token[j].coef; + } + eh = ((LOG_10 * R_KJ_DEG_MOL * tk_x) / F_KJ_V_EQ) * pe; +/* + * Print heading + */ + if (first == TRUE) + { + print_centered("Redox couples"); + output_msg(sformatf("\t%-15s%12s%12s\n\n", + "Redox couple", "pe", "Eh (volts)")); + first = FALSE; + } +/* + * Print result + */ + strcpy(token, master[i]->elt->name); + strcat(token, "/"); + strcat(token, master[k]->elt->name); + output_msg(sformatf("\t%-15s%12.4f%12.4f\n", token, + (double) pe, (double) eh)); + } + } + if (first == FALSE) + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print moles of each exchange species + */ + int i; + cxxExchange * exchange_ptr; + const char *name, *name1; + class master *master_ptr; + LDBLE dum, dum2; +/* + * Print exchange data + */ + exchange_ptr = use.Get_exchange_ptr(); + if (exchange_ptr == NULL || pr.exchange == FALSE || pr.all == FALSE) + return (OK); + + if (state >= REACTION) + { + print_centered("Exchange composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + name = s_hplus->secondary->elt->name; + for (i = 0; i < (int)species_list.size(); i++) + { +/* + * Get name of master species + */ + if (species_list[i].s->type != EX) + continue; + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + name1 = species_list[i].master_s->secondary->elt->name; + } + else + { + master_ptr = species_list[i].master_s->primary; + name1 = species_list[i].master_s->primary->elt->name; + } +/* + * Check if new master species, print total molality + */ + if (name1 != name) + { + name = name1; + output_msg(sformatf("%-14s%12.3e mol", name, + (double) master_ptr->unknown->moles)); + cxxExchange *exchange_ptr = (cxxExchange *) (use.Get_exchange_ptr()); + if (master_ptr->unknown->exch_comp == NULL) + { + error_string = sformatf("Exchange unknown has no exchange component for exchanger %s." + "\nIs the same name used for a SURFACE and an EXCHANGER?", + master_ptr->unknown->description); + error_msg(error_string, STOP); + } + const cxxExchComp *exchange_comp_ptr = exchange_ptr->Find_comp(master_ptr->unknown->exch_comp); + assert(exchange_comp_ptr); + if (exchange_comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g (mol %s)/(mol %s)]", + (double) exchange_comp_ptr->Get_phase_proportion(), + exchange_comp_ptr->Get_formula().c_str(), + exchange_comp_ptr->Get_phase_name().c_str())); + } + else if (exchange_comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g (mol %s)/(mol kinetic reactant %s)]", + (double) exchange_comp_ptr->Get_phase_proportion(), + exchange_comp_ptr->Get_formula().c_str(), + exchange_comp_ptr->Get_rate_name().c_str())); + } + output_msg(sformatf("\n\n")); + /* Heading for species */ + output_msg(sformatf("\t%-15s%12s%12s%12s%10s\n", " ", " ", + "Equiv- ", "Equivalent", "Log ")); + output_msg(sformatf("\t%-15s%12s%12s%12s%10s\n\n", + "Species", "Moles ", "alents ", "Fraction", "Gamma")); + } +/* + * Print species data + */ + if (master_ptr->total > 1.0e-16) + { + if (species_list[i].s->equiv != 0.0) + { + dum = fabs(species_list[i].s->equiv) / master_ptr->total; + } + else + { + if (species_list[i].master_s->z == 0) + { + dum = 1 / master_ptr->total; + } + else + { + dum = 1; + } + } + if (species_list[i].master_s->z != 0.0) + { + dum2 = fabs(species_list[i].master_s->z); + } + else + { + dum2 = 1; + } + output_msg(sformatf("\t%-15s%12.3e%12.3e%12.3e%10.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) (species_list[i].s->moles * dum2 * + species_list[i].s->equiv), + (double) (species_list[i].s->moles * + dum /* / dum2 */ ), + (double) (species_list[i].s->lg - log10(dum)))); + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints gas phase composition if present + */ + LDBLE lp, moles, initial_moles, delta_moles; + class rxn_token *rxn_ptr; + char info[MAX_LENGTH]; + bool PR = false; + + if (pr.gas_phase == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_v_m() >= 0.01) + PR = true; + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (OK); + if (gas_unknown->moles < 1e-12) + { + sprintf(info, "Fixed-pressure gas phase %d dissolved completely", + use.Get_n_gas_phase_user()); + print_centered(info); + return (OK); + } + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (PR) + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } +/* + * Print heading + */ + print_centered("Gas phase"); + output_msg(sformatf("Total pressure: %5.2f atmospheres", + (double) gas_phase_ptr->Get_total_p())); + if (gas_phase_ptr->Get_total_p() >= MAX_P_NONLLNL && llnl_temp.size() == 0) + output_msg(" WARNING: Program limit.\n"); + else if (PR) + output_msg(" (Peng-Robinson calculation)\n"); + else + output_msg(" \n"); + output_msg(sformatf(" Gas volume: %10.2e liters\n", + (double) gas_phase_ptr->Get_volume())); + if(gas_phase_ptr->Get_total_moles() > 0) + { + if (PR) + { + output_msg(sformatf(" Molar volume: %10.2e liters/mole", + (double) (gas_phase_ptr->Get_v_m()))); + } + else + { + output_msg(sformatf(" Molar volume: %10.2e liters/mole", + (double) (gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles()))); + } + } + if (/*!numerical_fixed_volume && */((PR && gas_phase_ptr->Get_v_m() <= 0.016))) + output_msg(" WARNING: Program limit for Peng-Robinson.\n"); + else + output_msg("\n"); + if (PR) + output_msg(sformatf( " P * Vm / RT: %8.5f (Compressibility Factor Z) \n", + (double) (gas_phase_ptr->Get_total_p() * gas_phase_ptr->Get_v_m() / (R_LITER_ATM * tk_x)))); + + + output_msg(sformatf("\n%68s\n%78s\n", "Moles in gas", + "----------------------------------")); + if (PR) + output_msg(sformatf( "%-11s%12s%12s%7s%12s%12s%12s\n\n", "Component", + "log P", "P", "phi", "Initial", "Final", "Delta")); + else + output_msg(sformatf("%-18s%12s%12s%12s%12s%12s\n\n", "Component", + "log P", "P", "Initial", "Final", "Delta")); + + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { +/* + * Calculate partial pressure + */ + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[j]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = + &phase_ptr->rxn_x.token[0] + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + lp -= phase_ptr->pr_si_f; + moles = phase_ptr->moles_x; + } + else + { + lp = -99.99; + moles = 0; + phase_ptr->p_soln_x = 0; + } +/* + * Print gas composition + */ + if (state != TRANSPORT && state != PHAST) + { + initial_moles = gc_ptr->Get_moles(); + delta_moles = phase_ptr->moles_x - gc_ptr->Get_moles(); + } + else + { + initial_moles = gc_ptr->Get_initial_moles(); + delta_moles = gc_ptr->Get_initial_moles() - + gc_ptr->Get_moles(); + } + if (moles <= MIN_TOTAL) + moles = 0.0; + if (fabs(delta_moles) <= MIN_TOTAL) + delta_moles = 0.0; + if (PR) + { + output_msg(sformatf("%-11s%12.2f%12.3e%7.3f%12.3e%12.3e%12.3e\n", + phase_ptr->name, + (double) lp, + (double) phase_ptr->p_soln_x, + (double) phase_ptr->pr_phi, + (double) initial_moles, + (double) moles, + (double) delta_moles)); + } + else + output_msg(sformatf("%-18s%12.2f%12.3e%12.3e%12.3e%12.3e\n", + phase_ptr->name, + (double) lp, + (double) phase_ptr->p_soln_x, + (double) initial_moles, + (double) moles, + (double) delta_moles)); + //if (!strcmp(phase_ptr->name, "H2O(g)") && phase_ptr->p_soln_x == 90) + // output_msg(" WARNING: The pressure of H2O(g) is fixed to the program limit.\n"); + + } + output_msg("\n"); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Prints solid solution composition if present + */ + int i, j; + LDBLE delta_moles; + LDBLE nb, nc, xb, xb1, xb2, xb1moles, xb2moles; + + if (pr.ss_assemblage == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + /* + * Print heading + */ + print_centered("Solid solutions"); + output_msg(sformatf("\n")); + output_msg(sformatf("%-15s %22s %11s %11s %11s\n\n", + "Solid solution", "Component", "Moles", "Delta moles", + "Mole fract")); + /* + * Print solid solutions + */ + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + if (ss_ptr->Get_ss_in()) + { + /* solid solution name, moles */ + output_msg(sformatf("%-15s %22s %11.2e\n", + ss_ptr->Get_name().c_str(), " ", + (double) ss_ptr->Get_total_moles())); + /* component name, moles, delta moles, mole fraction */ + + for (i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_initial_moles() - + comp_ptr->Get_delta(); + } + else + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_init_moles(); + } + output_msg(sformatf( + "%15s %22s %11.2e %11.2e %11.2e\n", " ", + comp_ptr->Get_name().c_str(), + (double) comp_ptr->Get_moles(), (double) delta_moles, + (double) (comp_ptr->Get_moles() / + ss_ptr->Get_total_moles()))); + } + if (ss_ptr->Get_miscibility()) + { + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + nc = comp0_ptr->Get_moles(); + nb = comp1_ptr->Get_moles(); + xb = nb / (nb + nc); + xb1 = ss_ptr->Get_xb1(); + xb2 = ss_ptr->Get_xb2(); + + if (xb > xb1 && xb < xb2) + { + xb2moles = (xb1 - 1) / xb1 * nb + nc; + xb2moles = xb2moles / ((xb1 - 1) / xb1 * xb2 + (1 - xb2)); + xb1moles = (nb - xb2moles * xb2) / xb1; + output_msg(sformatf( + "\n%14s Solid solution is in miscibility gap\n", + " ")); + output_msg(sformatf( + "%14s End members in pct of %s\n\n", " ", + comp1_ptr->Get_name().c_str())); + output_msg(sformatf("%22s %11g pct %11.2e\n", + " ", (double) xb1, (double) xb1moles)); + output_msg(sformatf("%22s %11g pct %11.2e\n", + " ", (double) xb2, (double) xb2moles)); + } + } + } + else + { + /* solid solution name, moles */ + output_msg(sformatf("%-15s %22s %11.2e\n", + ss_ptr->Get_name().c_str(), " ", + (double) 0.0)); + /* component name, moles, delta moles, mole fraction */ + for (i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_initial_moles() - + comp_ptr->Get_delta(); + } + else + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_init_moles(); + } + output_msg(sformatf( + "%15s %22s %11.2e %11.2e %11.2e\n", " ", + comp_ptr->Get_name().c_str(), + (double) 0, (double) delta_moles, (double) 0)); + } + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_reaction(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints irreversible reaction as defined and as + * relative moles of each element and total amount + * of reaction + */ + cxxReaction *reaction_ptr; + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION || use.Get_reaction_in() == FALSE) + return (OK); + if (state == TRANSPORT && transport_step == 0) + return (OK); + reaction_ptr = use.Get_reaction_ptr(); +/* + * Print amount of reaction + */ + output_msg(sformatf("Reaction %d.\t%s\n\n", use.Get_n_reaction_user(), + reaction_ptr->Get_description().c_str())); + output_msg(sformatf( + "\t%11.3e moles of the following reaction have been added:\n\n", + (double) step_x)); +/* + * Print reaction + */ + output_msg(sformatf("\t%-15s%10s\n", " ", "Relative")); + output_msg(sformatf("\t%-15s%10s\n\n", "Reactant", "moles")); + cxxNameDouble::const_iterator cit = reaction_ptr->Get_reactantList().begin(); + for ( ; cit != reaction_ptr->Get_reactantList().end(); cit++) + { + output_msg(sformatf("\t%-15s%13.5f\n", + cit->first.c_str(), (double) cit->second)); + } + output_msg(sformatf("\n")); +/* + * Debug + */ + + output_msg(sformatf("\t%-15s%10s\n", " ", "Relative")); + output_msg(sformatf("\t%-15s%10s\n", "Element", "moles")); + cit = reaction_ptr->Get_elementList().begin(); + for ( ; cit != reaction_ptr->Get_elementList().end(); cit++) + { + class element * elt_ptr = element_store(cit->first.c_str()); + assert(elt_ptr); + output_msg(sformatf("\t%-15s%13.5f\n", + elt_ptr->name, + (double) cit->second)); + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints kinetic reaction, + * should be called only on final kinetic step + */ + LDBLE sim_time; + cxxKinetics *kinetics_ptr; + if (pr.kinetics == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION) + return (OK); + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + } + if (kinetics_ptr == NULL) + return (OK); +/* + * determine time step + */ + if (state == TRANSPORT || state == PHAST) + { + kin_time_x = timest; + } + else if (state == ADVECTION) + { + kin_time_x = advection_kin_time; + } + sim_time = 0.; + if (run_info.Get_run_cells()) + { + sim_time = rate_sim_time; + } + else + { + if (incremental_reactions == TRUE) + { + if (!kinetics_ptr->Get_equalIncrements()) + { + for (int i = 0; i < reaction_step; i++) + { + if (i < (int) kinetics_ptr->Get_steps().size()) + { + sim_time += kinetics_ptr->Get_steps()[i]; + } + else + { + sim_time += kinetics_ptr->Get_steps().back(); + } + } + } + else + { + if (reaction_step > kinetics_ptr->Get_count()) + { + sim_time = kinetics_ptr->Get_steps().front(); + } + else + { + sim_time = + reaction_step * kinetics_ptr->Get_steps().front() / + ((LDBLE) (kinetics_ptr->Get_count())); + } + } + } + } +/* + * Print amount of reaction + */ + if (phast == FALSE) + { + output_msg(sformatf("Kinetics %d.\t%s\n\n", + use.Get_n_kinetics_user(), kinetics_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Kinetics.\n\n")); + } +/* + * Print reaction + */ + if (state == TRANSPORT) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) (initial_total_time + transport_step * timest))); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == ADVECTION) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) (initial_total_time + + advection_step * advection_kin_time))); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == PHAST) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) rate_sim_time_end)); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == REACTION) + { + if (incremental_reactions == FALSE) + { + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else + { + output_msg(sformatf( + "\tTime step: %g seconds (Incremented time: %g seconds)\n\n", + (double) kin_time_x, (double) sim_time)); + } + } + output_msg(sformatf("\t%-15s%12s%12s %-15s%12s\n\n", + "Rate name", "Delta Moles", "Total Moles", "Reactant", + "Coefficient")); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + kinetics_comp_ptr->Get_rate_name().c_str(), + (double) -kinetics_comp_ptr->Get_moles(), + (double) kinetics_comp_ptr->Get_m())); + } + else + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + kinetics_comp_ptr->Get_rate_name().c_str(), + (double) (kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles()), + (double) kinetics_comp_ptr->Get_m())); + } + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef = it->second; + if (it == kinetics_comp_ptr->Get_namecoef().begin()) + { + output_msg(sformatf(" %-15s%12g\n", + name.c_str(), + (double) coef)); + } + else + { + output_msg(sformatf("\t%39s %-15s%12g\n", " ", + name.c_str(), + (double) coef)); + } + } + } + output_msg(sformatf("\n")); + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_master_reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Debugging print routine to test primary and secondary reactions + */ + int i; + class rxn_token *next_token; + + for (i = 0; i < (int)master.size(); i++) + { + output_msg(sformatf("%s\t%s\n\tPrimary reaction\n", + master[i]->elt->name, master[i]->s->name)); + next_token = master[i]->rxn_primary.token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", next_token->s->name, + (double) next_token->coef)); + } + output_msg(sformatf("\n\tSecondary reaction:\n")); + if (master[i]->rxn_secondary != NULL) + { + next_token = master[i]->rxn_secondary.token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", + next_token->s->name, (double) next_token->coef)); + } + } + output_msg(sformatf("\n\tRedox reaction:\n")); + if (*(master[i]->pe_rxn) != NULL) + { + next_token = (*(master[i]->pe_rxn))->token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", + next_token->s->name, (double) next_token->coef)); + } + } + output_msg(sformatf("\n")); + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_mix(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints definition of mixing, solution number and multiplier + */ + cxxMix * mix_ptr; + cxxSolution *solution_ptr; + + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_mix_in() == FALSE || state < REACTION) + return (OK); + if (state == TRANSPORT) + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user()); + } + else + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user_orig()); + } + if (mix_ptr == NULL) + { + mix_ptr = use.Get_mix_ptr(); + } +/* + * Print mixture data + */ + if (mix_ptr == NULL) + { + return (OK); + + } + if (state == TRANSPORT) + { + output_msg(sformatf("Mixture %d.\t%s\n\n", use.Get_n_mix_user(), + mix_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Mixture %d.\t%s\n\n", mix_ptr->Get_n_user(), + mix_ptr->Get_description().c_str())); + } + std::map::const_iterator cit; + for (cit = mix_ptr->Get_mixComps().begin(); cit != mix_ptr->Get_mixComps().end(); cit++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, cit->first); + if (solution_ptr == NULL) + { + input_error++; + return (ERROR); + } + output_msg(sformatf("\t%11.3e Solution %d\t%-55s\n", + (double) cit->second, + cit->first, solution_ptr->Get_description().c_str())); + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_saturation_indices(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices of all applicable pure_phases + */ + int i; + LDBLE si, iap; + LDBLE lk; + LDBLE la_eminus; + class rxn_token *rxn_ptr; + CReaction *reaction_ptr; + bool gas = true; + + if (pr.saturation_indices == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + { + iap = 0; + for (size_t tok = 1; tok < pe_x[default_pe_x].Get_tokens().size() - 1; tok++) + { + iap += pe_x[default_pe_x].Get_tokens()[tok].coef * pe_x[default_pe_x].Get_tokens()[tok].s->la; + /* fprintf(output,"\t%s\t%f\t%f\n", rxn_ptr->s->name, rxn_ptr->coef, rxn_ptr->s->la ); */ + } + lk = k_calc(pe_x[default_pe_x].Get_logk(), tk_x, patm_x * PASCAL_PER_ATM); + la_eminus = lk + iap; + /* fprintf(output,"\t%s\t%f\n", "pe", si ); */ + } + else + { + la_eminus = s_eminus->la; + } +/* If a fixed pressure gas-phase disappeared, no PR for the SI's of gases... */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL || gas_unknown->moles < 1e-12) + gas = false; + } + } + +/* + * Print heading + */ + print_centered("Saturation indices"); + output_msg(sformatf(" %-15s%9s%8s%9s%3d%4s%3d%4s\n\n", "Phase", "SI**", + "log IAP", "log K(", int(tk_x), " K, ", int(floor(patm_x + 0.5)), " atm)")); + + for (i = 0; i < (int)phases.size(); i++) + { + if (phases[i]->in == FALSE || phases[i]->type != SOLID) + continue; + /* check for solids and gases in equation */ + if (phases[i]->replaced) + reaction_ptr = &phases[i]->rxn_s; + else + reaction_ptr = &phases[i]->rxn; +/* + * Print saturation index + */ + reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) - + phases[i]->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM); + iap = 0.0; + for (rxn_ptr = &reaction_ptr->token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s != s_eminus) + { + iap += (rxn_ptr->s->lm + rxn_ptr->s->lg) * rxn_ptr->coef; + } + else + { + iap += la_eminus * rxn_ptr->coef; + } + } + si = -lk + iap; + + output_msg(sformatf(" %-15s%7.2f %8.2f%8.2f %s", + phases[i]->name, (double) si, (double) iap, (double) lk, + phases[i]->formula)); + if (gas && phases[i]->pr_in && phases[i]->pr_p) + { + if (phases[i]->moles_x || state == INITIAL_SOLUTION) + { + output_msg(sformatf("\t%s%5.1f%s%5.3f", + " Pressure ", (double) phases[i]->pr_p, " atm, phi ", (double) phases[i]->pr_phi)); + } else + { + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (!strcmp(x[j]->phase->name, phases[i]->name)) + { + if (x[j]->moles) + output_msg(sformatf("\t%s%5.1f%s%5.3f", + " Pressure ", (double) phases[i]->pr_p, " atm, phi ", (double) phases[i]->pr_phi)); + break; + } + } + } + } + phases[i]->pr_in = false; + output_msg("\n"); + } + output_msg(sformatf("\n%s\n%s", + "**For a gas, SI = log10(fugacity). Fugacity = pressure * phi / 1 atm.", + " For ideal gases, phi = 1.")); + output_msg("\n\n"); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices and masses of pure_phases in pp_assemblage + */ + int j, k; + LDBLE si, iap, lk; + char token[MAX_LENGTH]; + class rxn_token *rxn_ptr; + class phase *phase_ptr; + + if (pr.pp_assemblage == FALSE || pr.all == FALSE) + return (OK); + if (pure_phase_unknown == NULL) + return (OK); +/* + * Print heading + */ + print_centered("Phase assemblage"); + output_msg(sformatf("%73s\n", "Moles in assemblage")); + output_msg(sformatf("%-14s%8s%2s%7s %11s", "Phase", "SI", " ", "log IAP", + "log K(T, P)")); + output_msg(sformatf(" %8s%12s%12s", " Initial", " Final", + " Delta")); + output_msg("\n\n"); + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + //cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user()); + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[j]->pp_assemblage_comp_ptr; // appt, is sometimes lost?? +/* + * Print saturation index + */ + iap = 0.0; + phase_ptr = x[j]->phase; + if (x[j]->phase->rxn_x.token.size() == 0 || phase_ptr->in == FALSE) + { + output_msg(sformatf("%-18s%23s", x[j]->phase->name, + "Element not present.")); + } + else + { + phase_ptr = x[j]->phase; + phase_ptr->rxn.logk[delta_v] = calc_delta_v(*&phase_ptr->rxn, true) - + phase_ptr->logk[vm0]; + if (phase_ptr->rxn.logk[delta_v]) + mu_terms_in_logk = true; + lk = k_calc(phase_ptr->rxn.logk, tk_x, patm_x * PASCAL_PER_ATM); + for (rxn_ptr = &phase_ptr->rxn.token[0] + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s != s_eminus) + { + iap += (rxn_ptr->s->lm + rxn_ptr->s->lg) * rxn_ptr->coef; + } + else + { + iap += s_eminus->la * rxn_ptr->coef; + } + } + si = -lk + iap; + /* + for (rxn_ptr = x[j]->phase->rxn_x.token + 1; rxn_ptr->s != NULL; rxn_ptr++) { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -x[j]->phase->lk + iap; + output_msg(OUTPUT_MESSAGE,"\t%-15s%7.2f%8.2f%8.2f", x[j]->phase->name, (double) si, (double) iap, (double) x[j]->phase->lk); + */ + output_msg(sformatf("%-14s%8.2f %7.2f %8.2f", + x[j]->phase->name, (double) si, (double) iap, (double) lk)); + } +/* + * Print pure phase assemblage data + */ + if (x[j]->moles < 0.0) + x[j]->moles = 0.0; + if (state != TRANSPORT && state != PHAST) + { + sprintf(token, " %11.3e %11.3e %11.3e", + (double) (comp_ptr->Get_moles() + + comp_ptr->Get_delta()), (double) x[j]->moles, + (double) (x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta())); + } + else + { + sprintf(token, " %11.3e %11.3e %11.3e", + (double) comp_ptr->Get_initial_moles(), + (double) x[j]->moles, + (double) (x[j]->moles - comp_ptr->Get_initial_moles())); + } + if (x[j]->moles <= 0.0) + { + for (k = 0; k < 11; k++) + { + token[13 + k] = ' '; + } + } + if (comp_ptr->Get_add_formula().size() == 0) + { + output_msg(sformatf("%37s\n", token)); + } + else + { + output_msg(sformatf("\n %-18s%-15s%36s\n", + comp_ptr->Get_add_formula().c_str(), " is reactant", token)); + } + } + output_msg("\n"); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of solution, uses array species_list for + * order of aqueous species. + */ + int i; + const char *name, *name1; + class master *master_ptr; + LDBLE min; + LDBLE lm; + + if (pr.species == FALSE || pr.all == FALSE) + return (OK); + min = -1000; + print_centered("Distribution of species"); +/* + * Heading for species + */ + if (pitzer_model == TRUE) + { + if (ICON == TRUE) + { + output_msg(sformatf("%60s%10s\n", "MacInnes", "MacInnes")); + output_msg(sformatf("%40s%10s%10s%10s%10s\n", + "MacInnes", "Log", "Log", "Log", "mole V")); + } + else + { + output_msg(sformatf("%60s%10s\n", "Unscaled", "Unscaled")); + output_msg(sformatf("%40s%10s%10s%10s%10s\n", + "Unscaled", "Log", "Log", "Log", "mole V")); + } + } + else + { + output_msg(sformatf("%50s%10s%10s%10s\n", "Log", "Log", "Log", "mole V")); + } +#ifdef NO_UTF8_ENCODING + output_msg(sformatf(" %-13s%12s%12s%10s%10s%10s%10s\n\n", "Species", + "Molality", "Activity", "Molality", "Activity", "Gamma", "cm3/mol")); +#else + output_msg(sformatf(" %-13s%12s%12s%10s%10s%10s%11s\n\n", "Species", + "Molality", "Activity", "Molality", "Activity", "Gamma", "cm³/mol")); +#endif +/* + * Print list of species + */ + s_h2o->lm = s_h2o->la; + name = s_hplus->secondary->elt->name; + for (i = 0; i < (int)species_list.size(); i++) + { +/* + * Get name of master species + */ + if (species_list[i].s->type == EX) + continue; + if (species_list[i].s->type == SURF) + continue; + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + name1 = species_list[i].master_s->secondary->elt->name; + } + else + { + master_ptr = species_list[i].master_s->primary; + name1 = species_list[i].master_s->primary->elt->name; + } +/* + * Check if new master species, print total molality + */ + if (name1 != name) + { + name = name1; + output_msg(sformatf("%-11s%12.3e\n", name, + (double) (master_ptr->total / mass_water_aq_x))); + min = censor * master_ptr->total / mass_water_aq_x; + if (min > 0) + { + min = log10(min); + } + else + { + min = -1000.; + } + } +/* + * Print species data + */ + if (species_list[i].s->lm > min) + { + if (species_list[i].s == s_h2o) + { + lm = log10(s_h2o->moles / mass_water_aq_x); + } + else + { + lm = species_list[i].s->lm; + } + output_msg(sformatf( + " %-13s%12.3e%12.3e%10.3f%10.3f%10.3f", + species_list[i].s->name, + (double) ((species_list[i].s->moles) / + mass_water_aq_x), + (double) under(species_list[i].s->lm + + species_list[i].s->lg), (double) lm, + (double) (species_list[i].s->lm + + species_list[i].s->lg), + (double) species_list[i].s->lg)); + //if (species_list[i].s->logk[vm_tc] || !strcmp(species_list[i].s->name, "H+")) + if (species_list[i].s->logk[vm_tc] || species_list[i].s == s_hplus) + output_msg(sformatf("%10.2f\n", + (double) species_list[i].s->logk[vm_tc])); + else + output_msg(sformatf(" (0) \n")); + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of surface, including charge and potential, + * grams and specific area, moles of each species on surface sites, + * and description of diffuse layer if applicable. + */ + cxxSurface *surface_ptr; + std::string name, token; + class master *master_ptr; + LDBLE molfrac, charge; +/* + * Print surface speciation + */ + surface_ptr = use.Get_surface_ptr(); + if (surface_ptr == NULL || pr.surface == FALSE || pr.all == FALSE) + return (OK); + if (surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + return (print_surface_cd_music()); + + if (state >= REACTION) + { + print_centered("Surface composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL) + { + output_msg(sformatf("%-14s\n", "Diffuse Double Layer Surface-Complexation Model\n")); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + output_msg(sformatf("%-14s\n", "Constant Capacitance Surface-Complexation Model\n")); + } + for (int j = 0; j < count_unknowns; j++) + { + /*if (use.Get_surface_ptr()->edl == TRUE) { */ + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else + { + if (x[j]->type != SURFACE) + continue; + token = x[j]->master[0]->elt->name; + Utilities::replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + CParser::copy_token(name, b, e); + } + output_msg(sformatf("%-14s\n", name.c_str())); +/* + * Description of surface + */ + if (dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf( + "\t%11.3e Surface + diffuse layer charge, eq\n", + (double) x[j]->f)); + } + /*if (use.Get_surface_ptr()->edl == TRUE && diffuse_layer_x == FALSE) { */ + if ((use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) && dl_type_x == cxxSurface::NO_DL) + { + charge = x[j]->f; + } + else + { + charge = calc_surface_charge(name.c_str()); + } + output_msg(sformatf("\t%11.3e Surface charge, eq\n", + (double) charge)); + if (x[j]->type == SURFACE_CB) + { + cxxSurfaceCharge * charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\t%11.3e sigma, C/m2\n", +#else + output_msg(sformatf("\t%11.3e sigma, C/m²\n", +#endif + (double) (charge * F_C_MOL / + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())))); + } + else + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\tundefined sigma, C/m2\n")); +#else + output_msg(sformatf("\tundefined sigma, C/m²\n")); +#endif + } + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + output_msg(sformatf("\t%11.3e capacitance, F/m^2\n", + (double) (charge_ptr->Get_capacitance0()))); + } + output_msg(sformatf("\t%11.3e psi, V\n", + (double) (x[j]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e -F*psi/RT\n", + (double) (x[j]->master[0]->s->la * (-2) * LOG_10))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT)\n", + exp(x[j]->master[0]->s->la * (-2) * LOG_10))); + cxxSurfaceComp * comp_ptr = surface_ptr->Find_comp(x[j]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/mol %s\n", +#else + "\t%11.3e specific area, m²/mol %s\n", +#endif + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_phase_name().c_str())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e m2 for %11.3e moles of %s\n\n", +#else + "\t%11.3e m² for %11.3e moles of %s\n\n", +#endif + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_phase_name().c_str())); + } + else if (comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/mol %s\n", +#else + "\t%11.3e specific area, m²/mol %s\n", +#endif + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_rate_name().c_str())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e m2 for %11.3e moles of %s\n\n", +#else + "\t%11.3e m² for %11.3e moles of %s\n\n", +#endif + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/g\n", +#else + "\t%11.3e specific area, m²/g\n", +#endif + (double) charge_ptr->Get_specific_area())); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\t%11.3e m2 for %11.3e g\n\n", +#else + output_msg(sformatf("\t%11.3e m² for %11.3e g\n\n", +#endif + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()), + (double) charge_ptr->Get_grams())); + } + if (dl_type_x != cxxSurface::NO_DL) + print_diffuse_layer(charge_ptr); + output_msg(sformatf("\n")); +/* + * Heading for species + */ + for (int k = j - 1; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE) + continue; + if (x[j] != x[k]->potential_unknown) + continue; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", + x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles", + (double) x[k]->moles)); + cxxSurfaceComp * comp_k_ptr = surface_ptr->Find_comp(x[k]->surface_comp); + if (comp_k_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g mol/(mol %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_phase_name().c_str())); + } + else if (comp_k_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g mol/(mol kinetic reactant %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n", " ", + " ", "Mole", " ", "Log")); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < (int)species_list.size(); i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (LDBLE) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-15s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / + mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + else + { + int k = j; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles\n", + (double) x[k]->moles)); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n", " ", " ", + "Mole", " ", "Log")); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < (int)species_list.size(); i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (double) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-15s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_surface_cd_music(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of cd music surfaces, including charge and potential, + * grams and specific area, moles of each species on surface sites, + * and description of diffuse layer if applicable. + */ + cxxSurface *surface_ptr; + std::string name; + class master *master_ptr, *master_ptr0, *master_ptr1, *master_ptr2; + class unknown *unknown_ptr0, *unknown_ptr1, *unknown_ptr2; + LDBLE molfrac, charge0, charge1, charge2, sum; +/* + * Print surface speciation + */ + surface_ptr = use.Get_surface_ptr(); + if (surface_ptr == NULL || pr.surface == FALSE || pr.all == FALSE) + return (OK); + + if (state >= REACTION) + { + print_centered("Surface composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf("%-14s\n", name.c_str())); + cxxSurfaceCharge * charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); +/* + * Description of surface + */ + if (dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf("\t%11.3e Surface + diffuse layer charge, eq\n\n", + (double)(x[(size_t)j + 2]->f + (charge_ptr->Get_sigma0() + charge_ptr->Get_sigma1()) * (charge_ptr->Get_specific_area() * charge_ptr->Get_grams()) / F_C_MOL))); + } + master_ptr0 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI2); + unknown_ptr0 = x[master_ptr0->unknown->number]; + unknown_ptr1 = x[master_ptr1->unknown->number]; + unknown_ptr2 = x[master_ptr2->unknown->number]; + + charge0 = unknown_ptr0->f; + charge1 = unknown_ptr1->f; + if (dl_type_x != cxxSurface::NO_DL) + { + charge2 = + charge_ptr->Get_sigma2() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL; + } + else + { + charge2 = unknown_ptr2->f; + } + sum = 0; + for (size_t k = 0; k < x[j]->comp_unknowns.size(); k++) + { + sum += + x[j]->comp_unknowns[k]->moles * + x[j]->comp_unknowns[k]->master[0]->s->z; + } + output_msg(sformatf("\t%11.3e Surface charge, plane 0, eq\n", + (double) (charge0 + sum))); + output_msg(sformatf("\t%11.3e Surface charge, plane 1, eq\n", + (double) charge1)); + output_msg(sformatf("\t%11.3e Surface charge, plane 2, eq\n", + (double) charge2)); + output_msg(sformatf( + "\t%11.3e Sum of surface charge, all planes, eq\n\n", + (double) (charge0 + sum + charge1 + charge2))); + if (x[j]->type == SURFACE_CB) + { + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 0, C/m2\n", +#else + "\t%11.3e sigma, plane 0, C/m²\n", +#endif + (double) charge_ptr->Get_sigma0())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 1, C/m2\n", +#else + "\t%11.3e sigma, plane 1, C/m²\n", +#endif + (double) charge_ptr->Get_sigma1())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 2, C/m2\n", +#else + "\t%11.3e sigma, plane 2, C/m²\n", +#endif + (double) charge_ptr->Get_sigma2())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, diffuse layer, C/m2\n\n", +#else + "\t%11.3e sigma, diffuse layer, C/m²\n\n", +#endif + (double) charge_ptr->Get_sigmaddl())); + } + else + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\tundefined sigma, C/m2\n")); +#else + output_msg(sformatf("\tundefined sigma, C/m²\n")); +#endif + } + output_msg(sformatf("\t%11.3e psi, plane 0, V\n", + (double) (-master_ptr0->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e psi, plane 1, V\n", + (double) (-master_ptr1->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e psi, plane 2, V\n\n", + (double) (-master_ptr2->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT), plane 0\n", + (double) (exp(master_ptr0->s->la * LOG_10)))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT), plane 1\n", + (double) (exp(master_ptr1->s->la * LOG_10)))); + output_msg(sformatf( + "\t%11.3e exp(-F*psi/RT), plane 2\n\n", + (double) (exp(master_ptr2->s->la * LOG_10)))); + + output_msg(sformatf("\t%11.3e capacitance 0-1, F/m^2\n", + (double) (charge_ptr->Get_capacitance0()))); + output_msg(sformatf("\t%11.3e capacitance 1-2, F/m^2\n", + (double) (charge_ptr->Get_capacitance1()))); + cxxSurfaceComp * comp_ptr = surface_ptr->Find_comp(x[j]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/mol %s\n", + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_phase_name().c_str())); + output_msg(sformatf( + "\t%11.3e m^2 for %11.3e moles of %s\n\n", + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_phase_name().c_str())); + } + else if (comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/mol %s\n", + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_rate_name().c_str())); + output_msg(sformatf( + "\t%11.3e m^2 for %11.3e moles of %s\n\n", + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/g\n", + (double) charge_ptr->Get_specific_area())); + output_msg(sformatf("\t%11.3e m^2 for %11.3e g\n\n", + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()), + (double) charge_ptr->Get_grams())); + } + if (dl_type_x != cxxSurface::NO_DL) + print_diffuse_layer(charge_ptr); + output_msg(sformatf("\n")); +/* + * Heading for species + */ + for (int k = j - 1; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE) + continue; + if (x[j] != x[k]->potential_unknown) + continue; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", + x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles", + (double) x[k]->moles)); + cxxSurfaceComp * comp_k_ptr = surface_ptr->Find_comp(x[k]->surface_comp); + if (comp_k_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g mol/(mol %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_phase_name().c_str())); + } + else if (comp_k_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g mol/(mol kinetic reactant %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\t%-20s%12s%12s%12s%12s\n", " ", + " ", "Mole", " ", "Log")); + output_msg(sformatf("\t%-20s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < (int)species_list.size(); i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (LDBLE) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-20s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / + mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_totals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print total concentrations of elements, molality and moles. + */ + int i, pure_water; + LDBLE dens; + + if (pr.totals == FALSE || pr.all == FALSE) + return (OK); + print_centered("Solution composition"); + pure_water = TRUE; + output_msg(sformatf("\t%-15s%12s%12s\n\n", "Elements", "Molality", + "Moles")); + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == alkalinity_unknown) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e\n", + "Alkalinity", + (double) (x[i]->f / mass_water_aq_x), + (double) x[i]->f)); + pure_water = FALSE; + } + if (x[i] == ph_unknown) + continue; + if (x[i] == pe_unknown) + continue; + if (x[i] == charge_balance_unknown) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + output_msg(sformatf(" Charge balance\n")); + pure_water = FALSE; + continue; + } + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + output_msg(sformatf(" Equilibrium with %s\n", + x[i]->phase->name)); + pure_water = FALSE; + continue; + } + if (x[i]->type == MB) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e\n", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + pure_water = FALSE; + } + } + + if (pure_water == TRUE) + { + output_msg(sformatf("\t%-15s\n", "Pure water")); + } +/* + * Description of solution + */ + output_msg(sformatf("\n")); + print_centered("Description of solution"); +/* + * pH + */ + output_msg(sformatf("%45s%7.3f ", "pH = ", + (double) (-(s_hplus->la)))); + if (ph_unknown == NULL) + { + output_msg(sformatf("\n")); + } + else if (ph_unknown == charge_balance_unknown) + { + output_msg(sformatf(" Charge balance\n")); + } + else if (ph_unknown->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf(" Equilibrium with %s\n", + ph_unknown->phase->name)); + } + else if (ph_unknown->type == ALK) + { + output_msg(sformatf(" Adjust alkalinity\n")); + } +/* + * pe + */ + output_msg(sformatf("%45s%7.3f ", "pe = ", + (double) (-(s_eminus->la)))); + if (pe_unknown == NULL) + { + output_msg(sformatf("\n")); + } + else if (pe_unknown == charge_balance_unknown) + { + output_msg(sformatf(" Charge balance\n")); + } + else if (pe_unknown->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf(" Equilibrium with %s\n", + pe_unknown->phase->name)); + } + else if (pe_unknown->type == MH) + { + output_msg(sformatf(" Adjusted to redox equilibrium\n")); + } +/* + * Others + */ + calc_SC(); + if (SC > 0) + { + //output_msg(sformatf("%36s%i%7s%i\n", +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%35s%3.0f%7s%i\n", + "Specific Conductance (uS/cm, ", tc_x, "oC) = ", (int) SC)); +#else + output_msg(sformatf("%35s%3.0f%7s%i\n", + "Specific Conductance (µS/cm, ", tc_x, "°C) = ", (int) SC)); +#endif + } +/* VP: Density Start */ + if (print_density) + { + dens = calc_dens(); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%45s%9.5f", "Density (g/cm3) = ", +#else + output_msg(sformatf("%45s%9.5f", "Density (g/cm³) = ", +#endif + (double) dens)); + if (state == INITIAL_SOLUTION && use.Get_solution_ptr()->Get_initial_data()->Get_calc_density()) + { + output_msg(sformatf(" (Iterated) ")); + } + if (dens > 1.999) output_msg(sformatf("%18s", " (Program limit)")); + output_msg(sformatf("\n")); + output_msg(sformatf("%45s%9.5f\n", " Volume (L) = ", + (double) calc_solution_volume())); + } +/* VP: Density End */ +#ifdef NPP + if (print_viscosity) + { + output_msg(sformatf("%45s%9.5f", "Viscosity (mPa s) = ", + (double) viscos)); + if (tc_x > 200 && !pure_water) + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%18s\n", + " (solute contributions limited to 200 oC)")); +#else + output_msg(sformatf("%18s\n", + " (solute contributions limited to 200 °C)")); +#endif + } + else output_msg(sformatf("\n")); + } +#endif + output_msg(sformatf("%45s%7.3f\n", "Activity of water = ", + exp(s_h2o->la * LOG_10))); + output_msg(sformatf("%45s%11.3e\n", "Ionic strength (mol/kgw) = ", + (double) mu_x)); + output_msg(sformatf("%45s%11.3e\n", "Mass of water (kg) = ", + (double) mass_water_aq_x)); + if (alkalinity_unknown == NULL) + { + output_msg(sformatf("%45s%11.3e\n", + "Total alkalinity (eq/kg) = ", + (double) (total_alkalinity / mass_water_aq_x))); + } + if (carbon_unknown == NULL && total_carbon) + { + output_msg(sformatf("%45s%11.3e\n", + "Total carbon (mol/kg) = ", + (double) (total_carbon / mass_water_aq_x))); + } + if (total_co2) + output_msg(sformatf("%45s%11.3e\n", "Total CO2 (mol/kg) = ", + (double) (total_co2 / mass_water_aq_x))); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%45s%6.2f\n", "Temperature (oC) = ", +#else + output_msg(sformatf("%45s%6.2f\n", "Temperature (°C) = ", +#endif + (double) tc_x)); + + if (patm_x != 1.0) + { + /* only print if different than default */ + output_msg(sformatf("%45s%5.2f\n", "Pressure (atm) = ", + (double) patm_x)); + } + + if (potV_x) + { + output_msg(sformatf("%45s%5.2f\n", "Electrical Potential (Volt) = ", + (double)potV_x)); + } + + output_msg(sformatf("%45s%11.3e\n", "Electrical balance (eq) = ", + (double) cb_x)); + output_msg(sformatf("%45s%6.2f\n", + "Percent error, 100*(Cat-|An|)/(Cat+|An|) = ", + (double) (100 * cb_x / total_ions_x))); + if (iterations == overall_iterations) + output_msg(sformatf("%45s%3d\n", "Iterations = ", iterations)); + else + output_msg(sformatf("%45s%3d (%d overall)\n", "Iterations = ", iterations, overall_iterations)); + if (pitzer_model == TRUE || sit_model == TRUE) + { + if (always_full_pitzer == FALSE) + { + output_msg(sformatf("%45s%3d\n", "Gamma iterations = ", + gamma_iterations)); + } + else + { + output_msg(sformatf("%45s%3d\n", "Gamma iterations = ", + iterations)); + } + output_msg(sformatf("%45s%9.5f\n", "Osmotic coefficient = ", + (double) COSMOT)); + if (print_density) output_msg(sformatf("%45s%9.5f\n", "Density of water = ", + (double) DW0)); + } + output_msg(sformatf("%45s%e\n", "Total H = ", (double) total_h_x)); + output_msg(sformatf("%45s%e\n", "Total O = ", (double) total_o_x)); + output_msg(sformatf("\n")); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_user_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print with user defined BASIC print routine + */ + cxxKinetics *kinetics_ptr; + + char l_command[] = "run"; + + if (pr.user_print == FALSE || pr.all == FALSE) + return (OK); + if (user_print->commands.size() == 0) + return (OK); + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + kinetics_ptr = use.Get_kinetics_ptr(); + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } + } + print_centered("User print"); + if (user_print->new_def == TRUE) + { + /* basic_renumber(user_print->commands, &user_print->linebase, &user_print->varbase, &user_print->loopbase); */ + if (basic_compile + (user_print->commands.c_str(), &user_print->linebase, + &user_print->varbase, &user_print->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PRINT.", STOP); + } + user_print->new_def = FALSE; + } + if (basic_run + (l_command, user_print->linebase, user_print->varbase, + user_print->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PRINT.", STOP); + } + if (this->output_newline) { + output_msg(sformatf("\n")); + } + this->Set_output_newline(true); + if (use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(kinetics_ptr); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_using(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print entities used in calculation + */ + cxxMix * mix_ptr; + cxxSolution *solution_ptr; + + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION || phast == TRUE) + return (OK); +/* + * Mixture or Solution + */ + if (use.Get_mix_in() == TRUE) + { + if (state == TRANSPORT) + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user()); + } + else + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user_orig()); + } + if (mix_ptr == NULL) + { + mix_ptr = use.Get_mix_ptr(); + } + if (mix_ptr != NULL) + { + if (state == TRANSPORT) + { + output_msg(sformatf("Using mix %d.\t%s\n", + use.Get_n_mix_user(), mix_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Using mix %d.\t%s\n", + use.Get_n_mix_user_orig(), mix_ptr->Get_description().c_str())); + } + + } + } + else + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user()); + output_msg(sformatf("Using solution %d.\t%s\n", + use.Get_n_solution_user(), solution_ptr->Get_description().c_str())); + } +/* + * Exchange and surface + */ + if (use.Get_exchange_in()) + { + cxxExchange *exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + output_msg(sformatf("Using exchange %d.\t%s\n", + use.Get_n_exchange_user(), exchange_ptr->Get_description().c_str())); + } + if (use.Get_surface_in()) + { + cxxSurface *surface_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + output_msg(sformatf("Using surface %d.\t%s\n", + use.Get_n_surface_user(), surface_ptr->Get_description().c_str())); + } + if (use.Get_pp_assemblage_in() == TRUE) + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user()); + output_msg(sformatf("Using pure phase assemblage %d.\t%s\n", + use.Get_n_pp_assemblage_user(), pp_assemblage_ptr->Get_description().c_str())); + } + if (use.Get_ss_assemblage_in() == TRUE) + { + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user()); + output_msg(sformatf( + "Using solid solution assemblage %d.\t%s\n", + use.Get_n_ss_assemblage_user(), + ss_assemblage_ptr->Get_description().c_str())); + } + if (use.Get_gas_phase_in()) + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user()); + output_msg(sformatf("Using gas phase %d.\t%s\n", + use.Get_n_gas_phase_user(), gas_phase_ptr->Get_description().c_str())); + } + if (use.Get_temperature_in()) + { + cxxTemperature *temperature_ptr = Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user()); + output_msg(sformatf("Using temperature %d.\t%s\n", + use.Get_n_temperature_user(), temperature_ptr->Get_description().c_str())); + } + if (use.Get_pressure_in()) + { + cxxPressure *pressure_ptr = Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user()); + output_msg(sformatf("Using pressure %d.\t%s\n", + use.Get_n_pressure_user(), pressure_ptr->Get_description().c_str())); + } + if (use.Get_reaction_in()) + { + if (state != TRANSPORT || transport_step > 0) + { + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user()); + output_msg(sformatf("Using reaction %d.\t%s\n", + use.Get_n_reaction_user(), reaction_ptr->Get_description().c_str())); + } + } + if (use.Get_kinetics_in()) + { + cxxKinetics * kinetics_ptr; + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + output_msg(sformatf("Using kinetics %d.\t%s\n", + use.Get_n_kinetics_user(), kinetics_ptr->Get_description().c_str())); + } + output_msg(sformatf("\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints selected gas phase data + */ + //int i; + LDBLE p, total_moles, volume; + LDBLE moles; + bool PR = false; + + //if (punch.count_gases <= 0) + if (current_selected_output->Get_gases().size() == 0) + return (OK); + p = 0.0; + total_moles = 0.0; + volume = 0.0; + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + + if (gas_unknown != NULL && use.Get_gas_phase_ptr() != NULL) + { + if (gas_phase_ptr->Get_v_m() >= 0.01) + { + PR = true; + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown->moles >= 1e-12) + { + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume( + gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (PR) + { + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } + } + else + { + gas_phase_ptr->Set_volume(0); + } + } + p = gas_phase_ptr->Get_total_p(); + total_moles = gas_phase_ptr->Get_total_moles(); + //volume = total_moles * R_LITER_ATM * tk_x / gas_phase_ptr->Get_total_p(); + //if (gas_phase_ptr->Get_v_m() > 0.03) + // volume = 0.03 * gas_phase_ptr->Get_total_moles(); + volume = gas_phase_ptr->Get_volume(); + + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pressure", "%12.4e\t", (double) p); + fpunchf("total mol", "%12.4e\t", (double) total_moles); + fpunchf("volume", "%12.4e\t", (double) volume); + } + else + { + fpunchf("pressure", "%20.12e\t", (double) p); + fpunchf("total mol", "%20.12e\t", (double) total_moles); + fpunchf("volume", "%20.12e\t", (double) volume); + } + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + moles = 0.0; + if (gas_phase_ptr != NULL && current_selected_output->Get_gases()[i].second != NULL) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[j]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + if (phase_ptr != current_selected_output->Get_gases()[i].second) + continue; + moles = phase_ptr->moles_x; + if (moles <= MIN_TOTAL) + moles = 0.0; + break; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("g_%s", current_selected_output->Get_gases()[i].first.c_str()), "%12.4e\t", (double) moles); + } + else + { + fpunchf(sformatf("g_%s", current_selected_output->Get_gases()[i].first.c_str()), "%20.12e\t", + (double) moles); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints solid solution composition if present + */ + //int j, k; + int found; + LDBLE moles; + +/* + * Print solid solutions + */ + for (size_t k = 0; k < current_selected_output->Get_s_s().size(); k++) + { + found = FALSE; + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (int j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (int i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + + if (strcmp_nocase(current_selected_output->Get_s_s()[k].first.c_str(), comp_ptr->Get_name().c_str()) == 0) + { + if (ss_ptr->Get_ss_in()) + { + moles = comp_ptr->Get_moles(); + } + else + { + moles = 0; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), + "%12.4e\t", (double) moles); + } + else + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), + "%20.12e\t", (double) moles); + } + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + } + if (found == FALSE) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), "%12.4e\t", (double) 0.0); + } + else + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), "%20.12e\t", + (double) 0.0); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_totals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print total concentrations of elements, molality and moles. + */ + //int j; + LDBLE molality; + + for (size_t j = 0; j < current_selected_output->Get_totals().size(); j++) + { + if (current_selected_output->Get_totals()[j].second == NULL) + { + molality = 0.0; + } + else if (((class master *) current_selected_output->Get_totals()[j].second)->primary == TRUE) + { + if (strncmp(current_selected_output->Get_totals()[j].first.c_str(), "Alkalinity", 20) == 0) + { + molality = total_alkalinity / mass_water_aq_x; + } else + { + molality = ((class master *) current_selected_output->Get_totals()[j].second)->total_primary / mass_water_aq_x; + } + } + else + { + molality = ((class master *) current_selected_output->Get_totals()[j].second)->total / mass_water_aq_x; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("%s(mol/kgw)", current_selected_output->Get_totals()[j].first.c_str()), + "%12.4e\t", (double) molality); + } + else + { + fpunchf(sformatf("%s(mol/kgw)", current_selected_output->Get_totals()[j].first.c_str()), + "%20.12e\t", (double) molality); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_molalities(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print concentrations of species (aq, ex, surf) + */ + //int j; + LDBLE molality; + + for (size_t j = 0; j < current_selected_output->Get_molalities().size(); j++) + { + molality = 0.0; + if (current_selected_output->Get_molalities()[j].second != NULL + && ((class species *) current_selected_output->Get_molalities()[j].second)->in == TRUE) + { + molality = ((class species *) current_selected_output->Get_molalities()[j].second)->moles / mass_water_aq_x; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("m_%s(mol/kgw)", current_selected_output->Get_molalities()[j].first.c_str()), + "%12.4e\t", (double) molality); + } + else + { + fpunchf(sformatf("m_%s(mol/kgw)", current_selected_output->Get_molalities()[j].first.c_str()), + "%20.12e\t", (double) molality); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_activities(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print concentrations of species (aq, ex, surf) + */ + //int j; + LDBLE la; + + for (size_t j = 0; j < current_selected_output->Get_activities().size(); j++) + { + la = -999.999; + if (current_selected_output->Get_activities()[j].second != NULL + && ((class species *) current_selected_output->Get_activities()[j].second)->in == TRUE) + { + /*la = punch.activities[j].s->lm + punch.activities[j].s->lg; */ + la = log_activity(current_selected_output->Get_activities()[j].first.c_str()); + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("la_%s", current_selected_output->Get_activities()[j].first.c_str()), "%12.4e\t", + (double) la); + } + else + { + fpunchf(sformatf("la_%s", current_selected_output->Get_activities()[j].first.c_str()), + "%20.12e\t", (double) la); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints masses of selected pure_phases in pp_assemblage + */ + //int i, j; + LDBLE moles, delta_moles; + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + delta_moles = 0; + moles = 0.0; + if (current_selected_output->Get_pure_phases()[i].second != NULL) + { + for (int j = 0; j < count_unknowns; j++) + { + if (x.size() == 0 || x[j]->type != PP) + continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[j]->pp_assemblage_comp_ptr; +/* + * Print pure phase assemblage data + */ + if (current_selected_output->Get_pure_phases()[i].second != x[j]->phase) + continue; + if (state != TRANSPORT && state != PHAST) + { + moles = x[j]->moles; + delta_moles = + x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta(); + } + else + { + moles = x[j]->moles; + delta_moles = + x[j]->moles - comp_ptr->Get_initial_moles(); + } + break; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(current_selected_output->Get_pure_phases()[i].first.c_str(), "%12.4e\t", (double) moles); + fpunchf(sformatf("d_%s", current_selected_output->Get_pure_phases()[i].first.c_str()), "%12.4e\t", + (double) delta_moles); + } + else + { + fpunchf(current_selected_output->Get_pure_phases()[i].first.c_str(), "%20.12e\t", (double) moles); + fpunchf(sformatf("d_%s", current_selected_output->Get_pure_phases()[i].first.c_str()), + "%20.12e\t", (double) delta_moles); + } + } + return (OK); +} + +#define PHAST_NULL(x) (phast ? NULL : x) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_identifiers(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints series of integers to identify simulation number, + * state of calculations, reaction or transport step number, + * and temp, ph, pe, and mass of water for each line + * of selected output. + */ + const char *sformat; + const char *dformat; + const char *gformat; + int i; + char token[MAX_LENGTH]; + + //if (punch.in == FALSE) + // return (OK); + if (!current_selected_output->Get_high_precision()) + { + sformat = "%12s\t"; + dformat = "%12d\t"; + gformat = "%12g\t"; + } + else + { + sformat = "%20s\t"; + dformat = "%20d\t"; + gformat = "%20g\t"; + } + +/* + * simulation or simul_tr + */ + + if (current_selected_output->Get_sim()) + { + if (state != TRANSPORT && state != PHAST) + { + fpunchf(PHAST_NULL("sim"), dformat, simulation); + } + else + { + fpunchf(PHAST_NULL("sim"), dformat, simul_tr); + } + } + if (current_selected_output->Get_state()) + { + switch (state) + { + case 0: + strcpy(token, "init"); + break; + case 1: + strcpy(token, "i_soln"); + break; + case 2: + strcpy(token, "i_exch"); + break; + case 3: + strcpy(token, "i_surf"); + break; + case 4: + strcpy(token, "i_gas"); + break; + case 5: + strcpy(token, "react"); + break; + case 6: + strcpy(token, "inverse"); + break; + case 7: + strcpy(token, "advect"); + break; + case 8: + strcpy(token, "transp"); + break; + default: + strcpy(token, "unknown"); + break; + } + fpunchf(PHAST_NULL("state"), sformat, token); + + } +/* + * solution number or cell number and time + */ + if (current_selected_output->Get_soln()) + { + if (state == TRANSPORT || state == PHAST) + { + fpunchf(PHAST_NULL("soln"), dformat, cell); + } + else if (state == ADVECTION) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_solution_user()); + } + else if (state < REACTION) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_solution_ptr()->Get_n_user()); + } + else + { + if (use.Get_mix_in() == TRUE) + { + if (state != TRANSPORT) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_mix_user_orig()); + } + else + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_mix_user()); + } + + } + else + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_solution_user()); + } + } + } + if (current_selected_output->Get_dist()) + { + if (state == ADVECTION) + { + fpunchf(PHAST_NULL("dist_x"), gformat, (double)use.Get_n_solution_user()); + } + else if (state == TRANSPORT) + { + fpunchf(PHAST_NULL("dist_x"), gformat, + (double) cell_data[cell].mid_cell_x); + } + else + { + fpunchf(PHAST_NULL("dist_x"), gformat, (double)-99); + } + } + if (current_selected_output->Get_time()) + { + LDBLE reaction_time = kin_time_x; + if (state == REACTION && incremental_reactions == TRUE + && use.Get_kinetics_ptr() != NULL) + { + if (!use.Get_kinetics_ptr()->Get_equalIncrements()) + { + reaction_time = 0.0; + for (i = 0; i < reaction_step; i++) + { + if (i < (int) use.Get_kinetics_ptr()->Get_steps().size()) + { + reaction_time += use.Get_kinetics_ptr()->Get_steps()[i]; + } + else + { + reaction_time += + use.Get_kinetics_ptr()->Get_steps().back(); + } + } + } + else + { + if (reaction_step > use.Get_kinetics_ptr()->Get_count()) + { + reaction_time = use.Get_kinetics_ptr()->Get_steps().front(); + } + else + { + reaction_time = + reaction_step * use.Get_kinetics_ptr()->Get_steps().front() / + ((LDBLE) (use.Get_kinetics_ptr()->Get_count())); + } + } + } + if (state == REACTION) + { + fpunchf(PHAST_NULL("time"), gformat, (double) reaction_time); + } + else if (state == TRANSPORT || state == PHAST) + { + fpunchf(PHAST_NULL("time"), gformat, + (double) (initial_total_time + rate_sim_time)); + } + else if (state == ADVECTION) + { + if (advection_kin_time_defined == TRUE) + { + fpunchf(PHAST_NULL("time"), gformat, + (double) (initial_total_time + rate_sim_time)); + } + else + { + fpunchf(PHAST_NULL("time"), gformat, (double)advection_step); + } + } + else + { + fpunchf(PHAST_NULL("time"), gformat, (double)-99); + } + } + +/* + * reaction or transport step + */ + if (current_selected_output->Get_step()) + { + if (state == REACTION) + { + fpunchf(PHAST_NULL("step"), dformat, reaction_step); + } + else if (state == ADVECTION) + { + fpunchf(PHAST_NULL("step"), dformat, advection_step); + } + else if (state == TRANSPORT) + { + fpunchf(PHAST_NULL("step"), dformat, transport_step); + } + else + { + fpunchf(PHAST_NULL("step"), dformat, -99); + } + } + if (current_selected_output->Get_ph()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pH", "%12g\t", (double) (-s_hplus->la)); + } + else + { + fpunchf("pH", "%20.12e\t", (double) (-s_hplus->la)); + } + } + if (current_selected_output->Get_pe()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pe", "%12g\t", (double) (-s_eminus->la)); + } + else + { + fpunchf("pe", "%20.12e\t", (double) (-s_eminus->la)); + } + } + if (current_selected_output->Get_rxn()) + { + if (state >= REACTION && use.Get_reaction_in() == TRUE) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("reaction", "%12.4e\t", (double) step_x); + } + else + { + fpunchf("reaction", "%20.12e\t", (double) step_x); + } + } + else + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("reaction", "%12d\t", -99); + } + else + { + fpunchf("reaction", "%20d\t", -99); + } + } + } + if (current_selected_output->Get_temp()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("temp(C)", "%12.3f\t", (double) tc_x); + } + else + { + fpunchf("temp(C)", "%20.12e\t", (double) tc_x); + } + } + if (current_selected_output->Get_alk()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("Alk(eq/kgw)", "%12g\t", + (double) (total_alkalinity / mass_water_aq_x)); + } + else + { + fpunchf("Alk(eq/kgw)", "%20.12e\t", + (double) (total_alkalinity / mass_water_aq_x)); + } + } + if (current_selected_output->Get_mu()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("mu", "%12g\t", (double) mu_x); + } + else + { + fpunchf("mu", "%20.12e\t", (double) mu_x); + } + } + if (current_selected_output->Get_water()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("mass_H2O", "%12g\t", (double) mass_water_aq_x); + } + else + { + fpunchf("mass_H2O", "%20.12e\t", (double) mass_water_aq_x); + } + } + if (current_selected_output->Get_charge_balance()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("charge(eq)", "%12g\t", (double) cb_x); + } + else + { + fpunchf("charge(eq)", "%20.12e\t", (double) cb_x); + } + } + if (current_selected_output->Get_percent_error()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pct_err", "%12g\t", + (double) (100 * cb_x / total_ions_x)); + } + else + { + fpunchf("pct_err", "%20.12e\t", + (double) (100 * cb_x / total_ions_x)); + } + } + punch_flush(); + return (OK); +} + +#undef PHAST_NULL +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_saturation_indices(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices of selected phases + */ + //int i; + LDBLE si, iap; + class rxn_token *rxn_ptr; + + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + if (current_selected_output->Get_si()[i].second == NULL || ((class phase *) current_selected_output->Get_si()[i].second)->in == FALSE) + { + si = -999.999; + } + else + { +/* + * Print saturation index + */ + iap = 0.0; + for (rxn_ptr = &(((class phase *) current_selected_output->Get_si()[i].second)->rxn_x.token[0]) + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -((class phase *) current_selected_output->Get_si()[i].second)->lk + iap; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("si_%s", current_selected_output->Get_si()[i].first.c_str()), "%12.4f\t", (double) si); + } + else + { + fpunchf(sformatf("si_%s", current_selected_output->Get_si()[i].first.c_str()), "%20.12e\t", (double) si); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints kinetic reaction, + * should be called only on final kinetic step + */ + cxxKinetics *kinetics_ptr; + LDBLE moles, delta_moles; + + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + } + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + moles = 0.0; + delta_moles = 0.0; + if (kinetics_ptr != NULL) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (strcmp_nocase + (current_selected_output->Get_kinetics()[i].first.c_str(), + kinetics_comp_ptr->Get_rate_name().c_str()) == 0) + { + if (state != TRANSPORT && state != PHAST) + { + moles = kinetics_comp_ptr->Get_m(); + delta_moles = - kinetics_comp_ptr->Get_moles(); + } + else + { + moles = kinetics_comp_ptr->Get_m(); + delta_moles = + kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles(); + } + break; + } + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("k_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%12.4e\t", + (double) moles); + fpunchf(sformatf("dk_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%12.4e\t", + (double) delta_moles); + } + else + { + fpunchf(sformatf("k_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%20.12e\t", + (double) moles); + fpunchf(sformatf("dk_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%20.12e\t", + (double) delta_moles); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_punch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch with user defined BASIC print routine + */ + char l_command[] = "run"; + + n_user_punch_index = 0; + //if (punch.user_punch == FALSE) + // return (OK); + if (current_user_punch == NULL || !current_selected_output->Get_user_punch()) + return OK; + + class rate * user_punch = current_user_punch->Get_rate(); + + if (user_punch->commands.c_str() == 0) + return (OK); + if (user_punch->new_def == TRUE) + { + if (basic_compile + (user_punch->commands.c_str(), &user_punch->linebase, + &user_punch->varbase, &user_punch->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PUNCH.", STOP); + } + user_punch->new_def = FALSE; + } + if (basic_run + (l_command, user_punch->linebase, user_punch->varbase, + user_punch->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PUNCH.", STOP); + } + return (OK); +} + +#if defined PHREEQ98 +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_graph(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Graph with user defined BASIC print routine + */ + char command[] = "run"; + + colnr = 0; +/* if (pr.user_graph == FALSE || pr.all == FALSE) return(OK); */ +/* if (punch.user_punch == FALSE) return(OK); */ +/* if (punch.in == FALSE) return(OK); */ + if (user_graph->commands == NULL) + return (OK); + if (((state == INITIAL_SOLUTION) || (state == INITIAL_EXCHANGE) + || (state == INITIAL_SURFACE) || (state == INITIAL_GAS_PHASE)) + && (graph_initial_solutions == FALSE)) + return (OK); + if (FirstCallToUSER_GRAPH) + AddSeries = TRUE; + if (state == REACTION) + { + /*if (reaction_step == 1) AddSeries = TRUE; + else AddSeries = FALSE; */ + if (reaction_step == 1 && !connect_simulations) + AddSeries = TRUE; + if (reaction_step > 1) + AddSeries = FALSE; + } + if (state == ADVECTION) + { + if (advection_step == 0 && graph_initial_solutions == FALSE) + return (OK); + if (((chart_type == 1) && (advection_step == punch_ad_modulus)) || + ((chart_type == 0) && (advection_step != prev_advection_step))) + AddSeries = TRUE; + else + AddSeries = FALSE; + } + if (state == TRANSPORT) + { + if (transport_step == 0 && graph_initial_solutions == FALSE) + return (OK); + if (((chart_type == 1) && (transport_step == punch_modulus)) || + ((chart_type == 0) && (transport_step != prev_transport_step))) + AddSeries = TRUE; + else + AddSeries = FALSE; + } + if (user_graph->new_def == TRUE) + { + if (basic_compile + (user_graph->commands, &user_graph->linebase, + &user_graph->varbase, &user_graph->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + user_graph->new_def = FALSE; + } + if (basic_run + (command, user_graph->linebase, user_graph->varbase, + user_graph->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + if (state == ADVECTION) + prev_advection_step = advection_step; + if (state == TRANSPORT) + prev_transport_step = transport_step; + /*if (state == REACTION) prev_reaction_step = reaction_step; */ + + if (FirstCallToUSER_GRAPH) + { + start_chart(0); + } + + FirstCallToUSER_GRAPH = FALSE; + + return (OK); +} +#endif +#if defined(MULTICHART) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_graph(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Graph with user defined BASIC print routine + */ + char command[] = "run"; + ChartObject *chart = chart_handler.Get_current_chart(); + if (chart == NULL) return OK; + + chart->Set_AddSeries(false); + + if (chart->Get_rate_command_list().size() == 0) + return (OK); + + // Skip initial calculations if initial_solutions == false + if (((state == INITIAL_SOLUTION) || (state == INITIAL_EXCHANGE) + || (state == INITIAL_SURFACE) || (state == INITIAL_GAS_PHASE)) + && (chart->Get_graph_initial_solutions() == false)) + return (OK); + + if (state == REACTION) + { + /*if (reaction_step == 1) AddSeries = TRUE; + else AddSeries = FALSE; */ + if (reaction_step == 1) + chart->Set_AddSeries(true); + if (reaction_step > 1) + chart->Set_AddSeries(false); + } + + if (state == ADVECTION) + { + + if (advection_step == 0 && chart->Get_graph_initial_solutions() == false) + return (OK); + if ( + ((chart->Get_chart_type() == 1) && (advection_step == punch_ad_modulus)) || + ((chart->Get_chart_type() == 0) && (advection_step != chart->Get_prev_advection_step())) + ) + { + chart->Set_AddSeries(true); + } + else + chart->Set_AddSeries(false); + } + if (state == TRANSPORT) + { + if (transport_step == 0 && chart->Get_graph_initial_solutions() == FALSE) + return (OK); + if ( + ((chart->Get_chart_type() == 1) && (transport_step == punch_modulus)) || + ((chart->Get_chart_type() == 0) && (transport_step != chart->Get_prev_transport_step())) + ) + { + chart->Set_AddSeries(true); + } + else + { + chart->Set_AddSeries(false); + } + } + + // From cmdplot_xy merged into transport and advection above + + // From plotXY + if (chart->Get_FirstCallToUSER_GRAPH()) + chart->Set_prev_sim_no(simulation); + else + { + if (simulation != chart->Get_prev_sim_no()) + { + chart->Set_AddSeries(true); + } + } + chart->Set_prev_sim_no(simulation); + if (chart->Get_AddSeries() && !chart->Get_connect_simulations()) + { + chart->Add_new_series(); + } + + chart->Set_colnr(chart->Get_ColumnOffset()); + chart->Initialize_graph_pts(); + if (chart->Get_rate_new_def()) + { + if (basic_compile + (chart->Get_user_graph()->commands.c_str(), &chart->Get_user_graph()->linebase, + &chart->Get_user_graph()->varbase, &chart->Get_user_graph()->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + chart->Set_rate_new_def(false); + } + + // basic_run calculates points for all graph and plotxy curves + // colnr identifies the curve and is incremented as each Y/Y2 curve point is added + if (basic_run + (command, chart->Get_user_graph()->linebase, + chart->Get_user_graph()->varbase, chart->Get_user_graph()->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + chart->Finalize_graph_pts(); + + if (state == ADVECTION) + chart->Set_prev_advection_step(advection_step); + if (state == TRANSPORT) + chart->Set_prev_transport_step(transport_step); + + if (chart->Get_FirstCallToUSER_GRAPH()) + { + chart->start_chart(); + } + chart->Set_new_ug(false); + + chart->Set_FirstCallToUSER_GRAPH(false); + + return (OK); +} +#endif // MULTICHART + +char * Phreeqc:: +sformatf(const char *format, ...) +{ + bool success = false; + do + { + va_list args; + va_start(args, format); + int j = vsnprintf(sformatf_buffer, sformatf_buffer_size, format, args); + success = (j > 0 && j < (int) sformatf_buffer_size); + va_end(args); + if (!success) + { + sformatf_buffer_size *= 2; + sformatf_buffer = (char *) PHRQ_realloc(sformatf_buffer, sformatf_buffer_size * sizeof(char)); + if (sformatf_buffer == NULL) malloc_error(); + } + } + while (!success); + + return sformatf_buffer; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_alkalinity(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of solution, uses array species_list for + * order of aqueous species. + */ + int j; + std::vector alk_list; + LDBLE min; + + if (pr.alkalinity == FALSE || pr.all == FALSE) + return (OK); + print_centered("Distribution of alkalinity"); + alk_list.clear(); + j = 0; + for (size_t i = 0; i < this->s_x.size(); i++) + { + if (s_x[i]->alk == 0.0) + continue; + alk_list.resize(alk_list.size() + 1); + alk_list[j].master_s = s_hplus; + alk_list[j].s = s_x[i]; + alk_list[j].coef = s_x[i]->alk; + j++; + } + min = fabs(censor * total_alkalinity / mass_water_aq_x); + if (alk_list.size() > 0) + { + output_msg(sformatf("\t%26s%11.3e\n\n", + "Total alkalinity (eq/kgw) = ", + (double) (total_alkalinity / mass_water_aq_x))); + output_msg(sformatf("\t%-15s%12s%12s%10s\n\n", "Species", + "Alkalinity", "Molality", "Alk/Mol")); + if (alk_list.size() > 1) qsort(&alk_list[0], alk_list.size(), + (size_t) sizeof(class species_list), species_list_compare_alk); + for (size_t i = 0; i < alk_list.size(); i++) + { + if (fabs(alk_list[i].s->alk * (alk_list[i].s->moles) / + mass_water_aq_x) < min) + continue; + output_msg(sformatf("\t%-15s%12.3e%12.3e%10.2f\n", + alk_list[i].s->name, + (double) (alk_list[i].s->alk * + (alk_list[i].s->moles) / mass_water_aq_x), + (double) ((alk_list[i].s->moles) / mass_water_aq_x), + (double) (alk_list[i].s->alk))); + } + } + + output_msg(sformatf("\n")); + return (OK); +} + diff --git a/phreeqcpp/read.cpp b/phreeqcpp/read.cpp new file mode 100644 index 00000000..4ade4fe4 --- /dev/null +++ b/phreeqcpp/read.cpp @@ -0,0 +1,9698 @@ +#include +#include +#include + +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Pressure.h" +#include "Temperature.h" +#include "Parser.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "SelectedOutput.h" +#include "UserPunch.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_input(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l; + const char* cptr; + char token[2 * MAX_LENGTH]; +#define LAST_C_KEYWORD 61 + + parse_error = 0; + input_error = 0; + next_keyword = Keywords::KEY_NONE; + count_warnings = 0; + + Rxn_new_exchange.clear(); + Rxn_new_gas_phase.clear(); + Rxn_new_kinetics.clear(); // not used + Rxn_new_mix.clear(); // not used + Rxn_new_pp_assemblage.clear(); + Rxn_new_pressure.clear(); // not used + Rxn_new_reaction.clear(); // not used + Rxn_new_solution.clear(); + Rxn_new_ss_assemblage.clear(); + Rxn_new_surface.clear(); + Rxn_new_temperature.clear(); // not used + phrq_io->Set_echo_on(true); +/* + * Initialize keyword counters + */ + for (i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + { + keycount[i] = 0; + } +/* + * Initialize use and save pointers + */ + use.init(); + + save.solution = FALSE; + save.mix = FALSE; + save.reaction = FALSE; + save.kinetics = FALSE; + save.pp_assemblage = FALSE; + save.exchange = FALSE; + save.surface = FALSE; + save.gas_phase = FALSE; + save.ss_assemblage = FALSE; + title_x.clear(); + + while ((i = check_line("Subroutine Read", FALSE, TRUE, TRUE, TRUE)) != KEYWORD) + { + /* empty, eof, keyword, print */ + + if (i == EOF) + return (EOF); + error_string = sformatf( + "Unknown input, no keyword has been specified."); + warning_msg(error_string); + } + + for (;;) + { + if (next_keyword > 0) + { + if (next_keyword != Keywords::KEY_DATABASE && !reading_database()) + { + first_read_input = FALSE; + } + } + // mark keyword read + if (next_keyword > 0 && next_keyword < Keywords::KEY_COUNT_KEYWORDS) + { + keycount[next_keyword]++; + } + switch (next_keyword) + { + case Keywords::KEY_NONE: /* Have not read line with keyword */ + error_string = sformatf( "Unknown input, no keyword has been specified."); + warning_msg(error_string); + while ((j = check_line("No keyword", FALSE, TRUE, TRUE, TRUE)) != KEYWORD && j != EOF) + { + warning_msg(error_string); + } + break; + case Keywords::KEY_END: + goto END_OF_SIMULATION_INPUT; + case Keywords::KEY_SOLUTION_SPECIES: /* Read aqueous model */ + read_species(); + break; + case Keywords::KEY_SOLUTION_MASTER_SPECIES: /* Read master species */ + read_master_species(); + break; + case Keywords::KEY_SOLUTION: /* Read solution data */ + read_solution(); + break; + case Keywords::KEY_PHASES: + read_phases(); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: + read_pp_assemblage(); + break; + case Keywords::KEY_REACTION: + read_reaction(); + break; + case Keywords::KEY_MIX: + read_mix(); + break; + case Keywords::KEY_SOLUTION_MIX: + //read_solution_mix(); + read_entity_mix(Rxn_solution_mix_map); + break; + case Keywords::KEY_EXCHANGE_MIX: + read_entity_mix(Rxn_exchange_mix_map); + break; + case Keywords::KEY_GAS_PHASE_MIX: + read_entity_mix(Rxn_gas_phase_mix_map); + break; + case Keywords::KEY_KINETICS_MIX: + read_entity_mix(Rxn_kinetics_mix_map); + break; + case Keywords::KEY_PPASSEMBLAGE_MIX: + read_entity_mix(Rxn_pp_assemblage_mix_map); + break; + case Keywords::KEY_SSASSEMBLAGE_MIX: + read_entity_mix(Rxn_ss_assemblage_mix_map); + break; + case Keywords::KEY_SURFACE_MIX: + read_entity_mix(Rxn_surface_mix_map); + break; + case Keywords::KEY_USE: + read_use(); + break; + case Keywords::KEY_SAVE: + read_save(); + break; + case Keywords::KEY_EXCHANGE_SPECIES: + read_exchange_species(); + break; + case Keywords::KEY_EXCHANGE_MASTER_SPECIES: + read_exchange_master_species(); + break; + case Keywords::KEY_EXCHANGE: + read_exchange(); + break; + case Keywords::KEY_SURFACE_SPECIES: + read_surface_species(); + break; + case Keywords::KEY_SURFACE_MASTER_SPECIES: + read_surface_master_species(); + break; + case Keywords::KEY_SURFACE: + read_surface(); + break; + case Keywords::KEY_REACTION_TEMPERATURE: + read_temperature(); + break; + case Keywords::KEY_INVERSE_MODELING: + read_inverse(); + break; + case Keywords::KEY_GAS_PHASE: + read_gas_phase(); + break; + case Keywords::KEY_TRANSPORT: + read_transport(); + break; + case Keywords::KEY_KNOBS: + read_debug(); + break; + case Keywords::KEY_SELECTED_OUTPUT: + read_selected_output(); + break; + case Keywords::KEY_PRINT: + read_print(); + break; + case Keywords::KEY_TITLE: + read_title(); + break; + case Keywords::KEY_ADVECTION: + read_advection(); + break; + case Keywords::KEY_KINETICS: + read_kinetics(); + break; + case Keywords::KEY_INCREMENTAL_REACTIONS: + read_incremental_reactions(); + break; + case Keywords::KEY_RATES: + read_rates(); + break; + case Keywords::KEY_SOLUTION_SPREAD: + read_solution_spread(); + break; + case Keywords::KEY_USER_PRINT: + read_user_print(); + break; + case Keywords::KEY_USER_PUNCH: + read_user_punch(); + break; + case Keywords::KEY_SOLID_SOLUTIONS: + read_solid_solutions(); + break; + case Keywords::KEY_USER_GRAPH: +#if defined PHREEQ98 + read_user_graph(); +#elif defined MULTICHART + read_user_graph_handler(); +# else + for (;;) + { + j = check_line("Reading user_graph", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } + } +#endif + break; + case Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS: + read_llnl_aqueous_model_parameters(); + break; + case Keywords::KEY_DATABASE: + if (reading_database()) + { + /* warning_msg("DATABASE is ignored in the database file."); */ + } + else if (first_read_input == FALSE) + { + error_msg("DATABASE must be the first keyword in the input file.", CONTINUE); + input_error++; + } + else + { + cptr = line; + copy_token(token, &cptr, &l); +#if defined(SWIG_SHARED_OBJ) + warning_msg("DATABASE keyword is ignored by IPhreeqc."); +#else + user_database = cptr; + string_trim(user_database); + if (user_database.size() == 0) + { + error_msg("DATABASE file name is missing.", CONTINUE); + input_error++; + } + first_read_input = FALSE; +#endif + } + j = check_line("Reading after DATABASE", FALSE, TRUE, TRUE, TRUE); + break; + case Keywords::KEY_NAMED_EXPRESSIONS: + read_named_logk(); + break; + case Keywords::KEY_ISOTOPES: + read_isotopes(); + break; + case Keywords::KEY_CALCULATE_VALUES: + read_calculate_values(); + break; + case Keywords::KEY_ISOTOPE_RATIOS: + read_isotope_ratios(); + break; + case Keywords::KEY_ISOTOPE_ALPHAS: + read_isotope_alphas(); + break; + case Keywords::KEY_COPY: + read_copy(); + break; + case Keywords::KEY_PITZER: + read_pitzer(); + break; + case Keywords::KEY_SIT: + read_sit(); + break; + case Keywords::KEY_SOLUTION_RAW: + Utilities::Rxn_read_raw(Rxn_solution_map, Rxn_new_solution, this); + break; + case Keywords::KEY_EXCHANGE_RAW: + Utilities::Rxn_read_raw(Rxn_exchange_map, Rxn_new_exchange, this); + break; + case Keywords::KEY_SURFACE_RAW: + Utilities::Rxn_read_raw(Rxn_surface_map, Rxn_new_surface, this); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + Utilities::Rxn_read_raw(Rxn_pp_assemblage_map, Rxn_new_pp_assemblage, this); + break; + case Keywords::KEY_KINETICS_RAW: + Utilities::Rxn_read_raw(Rxn_kinetics_map, Rxn_new_kinetics, this); + break; + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + Utilities::Rxn_read_raw(Rxn_ss_assemblage_map, Rxn_new_ss_assemblage, this); + break; + case Keywords::KEY_GAS_PHASE_RAW: + Utilities::Rxn_read_raw(Rxn_gas_phase_map, Rxn_new_gas_phase, this); + break; + case Keywords::KEY_REACTION_RAW: + Utilities::Rxn_read_raw(Rxn_reaction_map, Rxn_new_reaction, this); + break; + case Keywords::KEY_MIX_RAW: + Utilities::Rxn_read_raw(Rxn_mix_map, Rxn_new_mix, this); + break; + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + Utilities::Rxn_read_raw(Rxn_temperature_map, Rxn_new_temperature, this); + break; + case Keywords::KEY_DUMP: + read_dump(); + break; + case Keywords::KEY_SOLUTION_MODIFY: + Utilities::Rxn_read_modify(Rxn_solution_map, Rxn_new_solution, this); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY: + Utilities::Rxn_read_modify(Rxn_pp_assemblage_map, Rxn_new_pp_assemblage, this); + break; + case Keywords::KEY_EXCHANGE_MODIFY: + Utilities::Rxn_read_modify(Rxn_exchange_map, Rxn_new_exchange, this); + break; + case Keywords::KEY_SURFACE_MODIFY: + Utilities::Rxn_read_modify(Rxn_surface_map, Rxn_new_surface, this); + break; + case Keywords::KEY_SOLID_SOLUTIONS_MODIFY: + Utilities::Rxn_read_modify(Rxn_ss_assemblage_map, Rxn_new_ss_assemblage, this); + break; + case Keywords::KEY_GAS_PHASE_MODIFY: + Utilities::Rxn_read_modify(Rxn_gas_phase_map, Rxn_new_gas_phase, this); + break; + case Keywords::KEY_KINETICS_MODIFY: + Utilities::Rxn_read_modify(Rxn_kinetics_map, Rxn_new_kinetics, this); + break; + case Keywords::KEY_DELETE: + read_delete(); + break; + case Keywords::KEY_RUN_CELLS: + read_run_cells(); + break; + case Keywords::KEY_REACTION_MODIFY: + Utilities::Rxn_read_modify(Rxn_reaction_map, Rxn_new_reaction, this); + break; + case Keywords::KEY_REACTION_TEMPERATURE_MODIFY: + Utilities::Rxn_read_modify(Rxn_temperature_map, Rxn_new_temperature, this); + break; + //case LAST_C_KEYWORD + 22: //reaction_temperature_modify + // keyword[LAST_C_KEYWORD + 22].keycount++; + // read_reaction_temperature_modify(); + // break; + case Keywords::KEY_REACTION_PRESSURE: + read_reaction_pressure(); + break; + case Keywords::KEY_REACTION_PRESSURE_RAW: + Utilities::Rxn_read_raw(Rxn_pressure_map, Rxn_new_pressure, this); + break; + case Keywords::KEY_REACTION_PRESSURE_MODIFY: + Utilities::Rxn_read_modify(Rxn_pressure_map, Rxn_new_pressure, this); + break; + default: + error_msg("Error in keyword switch", STOP); + break; + } + } + END_OF_SIMULATION_INPUT: + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for exchange species, parse equations + */ + int i; + int association; + char token[MAX_LENGTH]; + const char* cptr; + class phase *phase_ptr; + + class species *s_ptr; + const class elt_list *next_elt; + //LDBLE exchange_coef; + LDBLE offset; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "mb", /* 2 */ + "mass_balance", /* 3 */ + "log_k", /* 4 */ + "logk", /* 5 */ + "delta_h", /* 6 */ + "deltah", /* 7 */ + "analytical_expression", /* 8 */ + "a_e", /* 9 */ + "ae", /* 10 */ + "mole_balance", /* 11 */ + "gamma", /* 12 */ + "davies", /* 13 */ + "offset", /* 14 */ + "llnl_gamma", /* 15 */ + "add_logk", /* 16 */ + "add_log_k", /* 17 */ + "add_constant", /* 18 */ + "vm" /* 19, molar volume, must replace delta_v */ + }; + int count_opt_list = 20; + + association = TRUE; + s_ptr = NULL; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EXCHANGE_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* mb */ + case 3: /* mass_balance */ + case 11: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + cptr = token; + get_secondary_in_species(&cptr, 1.0); + s_ptr->next_secondary.clear(); + s_ptr->next_secondary = elt_list_vsave(); +/* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 4: /* log_k */ + case 5: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 6: /* delta_h */ + case 7: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 8: /* analytical_expression */ + case 9: /* a_e */ + case 10: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 12: /* gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 2; + s_ptr->a_f = 0; + i = sscanf(next_char, SCANFORMAT SCANFORMAT SCANFORMAT, &s_ptr->dha, + &s_ptr->dhb, &s_ptr->a_f); + if (i < 2) + { + error_string = sformatf( "Expecting 2 activity-" + "coefficient parameters, a and b."); + warning_msg(error_string); + } + if (s_ptr->dha == 0 && s_ptr->dhb == 0) + { + s_ptr->dhb = 99.9; + s_ptr->exch_gflag = 1; + } + opt_save = OPTION_DEFAULT; + break; + case 13: /* davies */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 1; + s_ptr->dha = 0; + s_ptr->dhb = 99.9; + opt_save = OPTION_DEFAULT; + break; + case 14: /* offset */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (sscanf(next_char, SCANFORMAT, &offset) != 1) + { + error_msg("No offset for log K given", STOP); + } + s_ptr->logk[0] += offset; + opt_save = OPTION_DEFAULT; + break; + case 15: /* llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 7; /* llnl D-H */ + i = sscanf(next_char, SCANFORMAT, &s_ptr->dha); + if (i < 1) + { + error_string = sformatf( + "Expecting activity-coefficient parameter, a."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 16: /* add_logk */ + case 17: /* add_log_k */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[count_add_logk].coef = 1; + } + opt_save = OPTION_DEFAULT; + } + break; + case 18: /* add_constant */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + opt_save = OPTION_DEFAULT; + } + break; + case 19: /* vm, molar volume */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &s_ptr->logk[vm0], + &s_ptr->original_deltav_units); + opt_save = OPTION_DEFAULT; + break; + + case OPTION_DEFAULT: + /* + * Get exchange species information and parse equation + */ + { + s_ptr = NULL; + std::vector new_elt_list; + if (parse_eq(line, new_elt_list, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + /* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } + /* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = new_elt_list; + next_elt = &trxn.token[0].s->next_elt[0]; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } + /* + * Copy reaction to reaction for species + */ + trxn_copy(trxn.token[0].s->rxn); + /* + * Set type for species + */ + trxn.token[0].s->type = EX; + s_ptr = trxn.token[0].s; + /* + * Set gamma data + */ + s_ptr->gflag = 4; + s_ptr->exch_gflag = 3; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + opt_save = OPTION_DEFAULT; + /* + * Save as a phase for inverse modeling only + */ + phase_ptr = phase_store(s_ptr->name); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf("Copying exchange to phases."); + error_msg(error_string, CONTINUE); + } + else + { + phase_ptr->formula = s_ptr->name; + phase_ptr->check_equation = FALSE; + phase_ptr->type = EX; + phase_ptr->next_elt = s_ptr->next_elt; + phase_ptr->rxn = s_ptr->rxn; + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads exchange data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user; + LDBLE conc; + const char* cptr; + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "equilibrate", /* 0 */ + "equil", /* 1 */ + "pitzer_exchange_gammas", /* 2 */ + "exchange_gammas", /* 3 */ + "gammas", /* 4 */ + "equilibrium" /* 5 */ + }; + int count_opt_list = 6; +/* + * kin_exch is for exchangers, related to kinetically reacting minerals + * they are defined if "sites" is followed by mineral name: + * Z Manganite ('equi' or 'kine') 0.25 + * ^Name ^equi or kinetic mineral ^switch ^prop.factor + */ +/* + * Default values + n_user, description + */ + cxxExchange temp_exchange; + cptr = line; + temp_exchange.read_number_description(cptr); + n_user = temp_exchange.Get_n_user(); + cxxExchComp *comp_ptr = NULL; + temp_exchange.Set_new_def(true); +/* + * Set use data + */ + if (use.Get_exchange_in() == FALSE) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(n_user); + } +/* + * Read exchange data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EXCHANGE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* equilibrate */ + case 1: /* equil */ + case 5: /* equilibrium */ + /* + * Read solution to equilibrate with + */ + for (;;) + { + std::string token; + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + int n_solution; + (void)sscanf(token.c_str(), "%d", &n_solution); + temp_exchange.Set_n_solution(n_solution); + temp_exchange.Set_new_def(true); + temp_exchange.Set_solution_equilibria(true); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate exchanger.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case 2: /* pitzer_exchange_gammas */ + case 3: /* exchange_gammas */ + case 4: /* gammas */ + temp_exchange.Set_pitzer_exchange_gammas( + get_true_false(next_char, TRUE) == TRUE); + break; + case OPTION_DEFAULT: + { + std::string token; + cptr = line; + int i = copy_token(token, &cptr); + /* + * Species formula is stored in token + */ + if (i != UPPER && token[0] != '[') + { /* maybe a bit more clear? */ + error_string = sformatf( + "Expected exchanger name to begin with a capital letter, but found:\n %s", + line_save); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + cxxExchComp temp_comp(this->phrq_io); + temp_exchange.Get_exchange_comps().push_back(temp_comp); + comp_ptr = &(temp_exchange.Get_exchange_comps().back()); + comp_ptr->Set_formula(token.c_str()); + prev_next_char = cptr; + std::string token1; + i = copy_token(token1, &cptr); + if (i == DIGIT) + { + /* + * Read exchange concentration + */ + + /* exchanger conc. is read directly .. */ + if (sscanf(token1.c_str(), SCANFORMAT, &conc) < 1) + { + error_string = sformatf( + "Expected concentration of exchanger, but found:\n %s", + line_save); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + prev_next_char = cptr; + int j = copy_token(token1, &cptr); + if (j == UPPER || j == LOWER) + { + comp_ptr->Set_rate_name(token1.c_str()); + if (copy_token(token1, &cptr) != DIGIT) + { + error_string = sformatf( + "Expected a coefficient to relate exchange to kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + LDBLE p; + (void)sscanf(token1.c_str(), SCANFORMAT, &p); + comp_ptr->Set_phase_proportion(p); + } + /* + * Read equilibrium phase name or kinetics rate name + */ + } + else if (i != EMPTY) + { + + /* exchanger conc. is related to mineral or kinetics */ + comp_ptr->Set_phase_name(token1.c_str()); + prev_next_char = cptr; + int j = copy_token(token1, &cptr); + if (j != DIGIT) + { + if (token1[0] == 'K' || token1[0] == 'k') + { + comp_ptr->Set_rate_name(comp_ptr->Get_phase_name().c_str()); + comp_ptr->Set_phase_name(""); + } + else if (token1[0] != 'E' && token1[0] != 'e') + { + error_string = sformatf( + "Character string expected to be 'equilibrium_phase' or 'kinetics'\n to relate exchange to mineral or kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + prev_next_char = cptr; + j = copy_token(token1, &cptr); + } + + + if (j != DIGIT) + { + error_string = sformatf( + "Expected a coefficient to relate exchanger to mineral or kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + LDBLE p; + (void)sscanf(token1.c_str(), SCANFORMAT, &p); + comp_ptr->Set_phase_proportion(p); + /* real conc must be defined in tidy_model */ + conc = 1.0; + } + else + { + error_msg + ("Expected concentration of exchanger, mineral name, or kinetic reaction name.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + /* + * Accumulate elements in elt_list + */ + count_elts = 0; + paren_count = 0; + std::string formula = token.c_str(); + cptr = formula.c_str(); + get_elts_in_species(&cptr, conc); + + /* + * save formula for adjusting number of exchange sites + */ + cptr = formula.c_str(); + std::string name; + LDBLE z; + int l; + get_token(&cptr, name, &z, &l); + comp_ptr->Set_formula_z(z); + /* + * Save elt_list + */ + comp_ptr->Set_totals(elt_list_NameDouble()); + comp_ptr->Set_charge_balance(0.0); + } + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + Rxn_exchange_map[n_user] = temp_exchange; + Rxn_new_exchange.insert(n_user); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange_master_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species data from data file or input file + */ + int j, l; + const char* cptr, *cptr1; + LDBLE l_z; + class element *elts_ptr; + class species *s_ptr; + char token[MAX_LENGTH]; + for (;;) + { + j = check_line("Exchange species equation", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } +/* + * Get element name with valence, allocate space, store + */ + cptr = line; +/* + * Get element name and save pointer to character string + */ + if (copy_token(token, &cptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + /* + if (token[0] == '[') { + cptr1 = token; + get_elt(&cptr, element, &l); + strcpy(token, element); + } + */ + replace("(+", "(", token); +/* + * Delete master if it exists + */ + master_delete(token); +/* + * Increase pointer array, if necessary, and malloc space + */ + size_t count_master = master.size(); + master.resize(count_master + 1); + master[count_master] = master_alloc(); +/* + * Set type to EX + */ + master[count_master]->type = EX; +/* + * Save element name + */ + master[count_master]->elt = element_store(token); +/* + * Save pointer to species data for master species + */ + if ((copy_token(token, &cptr, &l) != UPPER) && + token[0] != '[' && (strcmp_nocase_arg1(token, "e-") != 0)) + { + parse_error++; + error_msg("Reading master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + cptr1 = token; + std::string token1; + get_token(&cptr1, token1, &l_z, &l); + master[count_master]->s = s_store(token1.c_str(), l_z, FALSE); + } +/* + * MAKE LISTS OF PRIMARY AND SECONDARY MASTER SPECIES + */ + master[count_master]->primary = TRUE; + if (strcmp(master[count_master]->elt->name, "E") != 0) + { + elts_ptr = element_store(master[count_master]->elt->name); + elts_ptr->gfw = 0.0; + } + } + return (j); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads gas phase data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int i, j, l; + int n_user; + const char* cptr; + char token[MAX_LENGTH]; + cxxGasPhase temp_gas_phase(this->phrq_io); + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "pressure", /* 0 */ + "volume", /* 1 */ + "temp", /* 2 */ + "temperature", /* 3 */ + "fixed_pressure", /* 4 */ + "fixed_volume", /* 5 */ + "equilibrium", /* 6 */ + "equilibrate", /* 7 */ + "equil" /* 8 */ + }; + int count_opt_list = 9; + + cptr = line; + temp_gas_phase.read_number_description(cptr); + n_user = temp_gas_phase.Get_n_user(); + temp_gas_phase.Set_new_def(true); +/* + * Set use data to first read + */ + if (use.Get_gas_phase_in() == FALSE) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(n_user); + } +/* + * Read phases + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in GAS_PHASE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* pressure */ + (void)sscanf(next_char, SCANFORMAT, &dummy); + temp_gas_phase.Set_total_p(dummy); + break; + case 1: /* Volume */ + (void)sscanf(next_char, SCANFORMAT, &dummy); + temp_gas_phase.Set_volume(dummy); + break; + case 2: /* Temperature */ + case 3: + j = sscanf(next_char, SCANFORMAT, &dummy); + if (j == 1) + { + temp_gas_phase.Set_temperature(dummy + 273.15); + } + break; + case 4: /* fixed_pressure */ + temp_gas_phase.Set_type(cxxGasPhase::GP_PRESSURE); + break; + case 5: /* fixed_volume */ + temp_gas_phase.Set_type(cxxGasPhase::GP_VOLUME); + break; + case 6: /* equilibrate */ + case 7: /* equilibrium */ + case 8: /* equil */ +/* + * Read solution to equilibrate with + */ + for (;;) + { + i = copy_token(token, &next_char, &l); + if (i == DIGIT) + { + (void)sscanf(token, "%d", &l); + temp_gas_phase.Set_n_solution(l); + temp_gas_phase.Set_new_def(true); + temp_gas_phase.Set_solution_equilibria(true); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate gas phase.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case OPTION_DEFAULT: + { + cxxGasComp temp_comp; + /* + * Read name + */ + cptr = line; + copy_token(token, &cptr, &l); + temp_comp.Set_phase_name(token); + if ((j = copy_token(token, &cptr, &l)) == EMPTY) + { + temp_comp.Set_p_read(NAN); + temp_gas_phase.Get_gas_comps().push_back(temp_comp); + break; + } + /* + * Read initial partial pressure of gas + */ + + j = sscanf(token, SCANFORMAT, &dummy); + + if (j != 1) + { + error_msg("Expected partial pressure of gas in gas phase.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + temp_comp.Set_p_read(dummy); + temp_gas_phase.Get_gas_comps().push_back(temp_comp); + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // sort components + std::map gc; + for (size_t i = 0; i < temp_gas_phase.Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(temp_gas_phase.Get_gas_comps()[i]); + gc[gc_ptr->Get_phase_name()] = *gc_ptr; + } + std::vector vgc; + std::map::iterator it = gc.begin(); + for ( ; it != gc.end(); it++) + { + vgc.push_back(it->second); + } + temp_gas_phase.Set_gas_comps(vgc); + + + Rxn_gas_phase_map[n_user] = temp_gas_phase; + Rxn_new_gas_phase.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inverse(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads data for mass_balance calculations + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n, j; + int n_user, n_user_end; + const char* cptr; + char *description; + LDBLE range_max, inv_tol, water_uncertainty; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "solutions", /* 0 */ + "uncertainty", /* 1 */ + "uncertainties", /* 2 */ + "balances", /* 3 */ + "phase_data", /* 4 */ + "range", /* 5 */ + "minimal", /* 6 */ + "minimum", /* 7 */ + "balance", /* 8 */ + "bal", /* 9 */ + "sol", /* 10 */ + "phases", /* 11 */ + "ranges", /* 12 */ + "tolerance", /* 13 */ + "u_water", /* 14 */ + "uncertainty_water", /* 15 */ + "force", /* 16 */ + "force_solution", /* 17 */ + "force_solutions", /* 18 */ + "isotopes", /* 19 */ + "mineral_water", /* 20 */ + "phase", /* 21 */ + "multiple_precision", /* 22 */ + "mp_tolerance", /* 23 */ + "censor_mp", /* 24 */ + "lon_netpath", /* 25 */ + "pat_netpath" /* 26 */ + }; + int count_opt_list = 27; + + cptr = line; +/* + * Read solution number and description + */ + read_number_description(cptr, &n_user, &n_user_end, &description); +/* + * Malloc space for solution data + */ + if (inverse_search(n_user, &n) != NULL) + { + inverse_delete(n); + } + inverse_alloc(); + n = count_inverse - 1; +/* + * Initialize structure and use + */ + inverse[n].new_def = TRUE; + inverse[n].n_user = n_user; + inverse[n].range = FALSE; + inverse[n].range_max = 1000.; + inverse[n].tolerance = 1e-10; + inverse[n].minimal = FALSE; + inverse[n].description = description; + inverse[n].water_uncertainty = 0.0; + inverse[n].mineral_water = TRUE; + inverse[n].mp = FALSE; + inverse[n].mp_tolerance = 1e-12; + inverse[n].mp_censor = 1e-20; + inverse[n].netpath = NULL; + inverse[n].pat = NULL; +/* + * Read data for inverse modeling + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = opt; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in INVERSE_MODELING keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* solutions */ + case 10: /* solution */ + read_vector_ints(&next_char, inverse[n].solns, TRUE); + inverse[n].count_solns = (int)inverse[n].solns.size(); + opt_save = OPTION_ERROR; + break; + case 1: /* uncertainty */ + case 2: /* uncertainties */ + read_vector_doubles(&next_char, inverse[n].uncertainties); + opt_save = OPTION_ERROR; + break; + case 3: /* balances */ + case 8: /* balance */ + case 9: /* bal */ + read_inv_balances(&(inverse[n]), next_char); + break; + case 4: /* phase_data */ + case 11: /* phases */ + case 21: /* phase */ + read_inv_phases(&(inverse[n]), next_char); + break; + case 5: /* range */ + case 12: /* ranges */ + inverse[n].range = TRUE; + j = sscanf(next_char, SCANFORMAT, &range_max); + if (j == 1) + { + inverse[n].range_max = range_max; + } + opt_save = OPTION_ERROR; + break; + case 6: /* minimal */ + case 7: /* minimum */ + inverse[n].minimal = TRUE; + opt_save = OPTION_ERROR; + break; + case 13: /* tolerance */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].tolerance = inv_tol; + } + opt_save = OPTION_ERROR; + break; + case 14: /* u_water */ + case 15: /* uncertainty_water */ + j = sscanf(next_char, SCANFORMAT, &water_uncertainty); + if (j == 1) + { + inverse[n].water_uncertainty = water_uncertainty; + } + opt_save = OPTION_ERROR; + break; + case 16: /* force */ + case 17: /* force_solution */ + case 18: /* force_solutions */ + inverse[n].force_solns.clear(); + read_vector_t_f(&next_char, inverse[n].force_solns); + opt_save = OPTION_ERROR; + break; + case 19: /* isotope values */ + read_inv_isotopes(&(inverse[n]), next_char); + break; + case 20: /* mineral_water */ + inverse[n].mineral_water = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 22: /* multiple_precision */ + inverse[n].mp = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 23: /* mp_tolerance */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].mp_tolerance = fabs(inv_tol); + } + opt_save = OPTION_ERROR; + break; + case 24: /* censor_mp */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].mp_censor = fabs(inv_tol); + } + opt_save = OPTION_ERROR; + break; + case 25: /* lon_netpath */ + { + std::string temp_name(next_char); + string_trim(temp_name); + if (temp_name.size() > 0) + { + inverse[n].netpath = string_hsave(temp_name.c_str()); + } + else + { + inverse[n].netpath = string_hsave("netpath"); + } + opt_save = OPTION_ERROR; + } + break; + case 26: /* pat_netpath */ + { + std::string temp_name(next_char); + string_trim(temp_name); + if (temp_name.size() > 0) + { + inverse[n].pat = string_hsave(temp_name.c_str()); + } + else + { + inverse[n].pat = string_hsave("netpath"); + } + opt_save = OPTION_ERROR; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * Default: soln 1 -> soln 2 + */ + if (inverse[n].count_solns == 0) + { + inverse[n].solns.push_back(1); + inverse[n].solns.push_back(2); + inverse[n].count_solns = 2; + } +/* + * Sort isotopes + */ + if (inverse[n].isotopes.size() > 1) + { + qsort(&inverse[n].isotopes[0], + inverse[n].isotopes.size(), + sizeof(class inv_isotope), inverse_isotope_compare); + } + + if (inverse[n].i_u.size() > 1) + { + qsort(&inverse[n].i_u[0], + inverse[n].i_u.size(), + (size_t) sizeof(class inv_isotope), inverse_isotope_compare); + } + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_balances(class inverse *inverse_ptr, const char* cptr) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH]; +/* + * Read element name + */ + j = copy_token(token, &cptr, &l); + if (j == EMPTY) + { + return (OK); + } + else if (j == LOWER && strcmp_nocase_arg1(token, "ph") != 0) + { + error_msg("Expecting element name.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (strcmp_nocase_arg1(token, "ph") != 0) + { + size_t count_elts = inverse_ptr->elts.size(); + inverse_ptr->elts.resize(count_elts + 1); + replace("(+", "(", token); + inverse_ptr->elts[count_elts].name = string_hsave(token); +/* + * Read element uncertainties + */ + read_vector_doubles(&cptr, inverse_ptr->elts[count_elts].uncertainties); + } + else if (strcmp_nocase_arg1(token, "ph") == 0) + { + inverse_ptr->ph_uncertainties.clear(); + read_vector_doubles(&cptr, inverse_ptr->ph_uncertainties); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_isotopes(class inverse *inverse_ptr, const char* cptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l, l1, l2; + LDBLE isotope_number; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + const char* cptr1, *ptr2; + const char * redox_name, *element_name; +/* + * Read element name + */ + cptr1 = cptr; + j = copy_token(token, &cptr1, &l); +/* + * cptr1 is start of uncertainties + */ + if (j == EMPTY) + { + return (OK); + } + else if (j != DIGIT) + { + error_msg("Expecting isotope to begin with isotope number.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Read isotope name + */ + ptr2 = token; + get_num(&ptr2, &isotope_number); + if (ptr2[0] == '\0' || isupper((int) ptr2[0]) == FALSE) + { + error_msg("Expecting element name.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + + /* redox state name with parentheses */ + redox_name = string_hsave(ptr2); + + copy_token(token, &ptr2, &l1); + replace("(", " ", token); + ptr2 = token; + + /* element name, without parentheses */ + copy_token(token1, &ptr2, &l2); + element_name = string_hsave(token1); + +/* + * add element name to inv_ptr->isotopes + */ + for (i = 0; i < inverse_ptr->isotopes.size(); i++) + { + if (element_name == inverse_ptr->isotopes[i].elt_name) + break; + } + if (i == inverse_ptr->isotopes.size()) + { + size_t count_isotopes = inverse_ptr->isotopes.size(); + inverse_ptr->isotopes.resize(count_isotopes + 1); + inverse_ptr->isotopes[count_isotopes].isotope_number = isotope_number; + inverse_ptr->isotopes[count_isotopes].elt_name = element_name; + inverse_ptr->isotopes[count_isotopes].uncertainties.clear(); + } +/* + * add redox state name to inv_ptr->i_u + */ + size_t count_i_u = inverse_ptr->i_u.size(); + inverse_ptr->i_u.resize(count_i_u + 1); + inverse_ptr->i_u[count_i_u].elt_name = redox_name; + inverse_ptr->i_u[count_i_u].isotope_number = isotope_number; +/* + * Read isotope uncertainties + */ + read_vector_doubles(&cptr1, inverse_ptr->i_u[count_i_u].uncertainties); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_phases(class inverse *inverse_ptr, const char* cptr) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + const char* cptr1; + std::vector isotopes; +/* + * Read phase name + */ + j = copy_token(token, &cptr, &l); + if (j == EMPTY) + return (OK); + + size_t count_phases = inverse_ptr->phases.size(); + inverse_ptr->phases.resize(count_phases + 1); + inverse_ptr->phases[count_phases].name = string_hsave(token); +/* + * Read constraint, force, and isotopes + */ + inverse_ptr->phases[count_phases].constraint = EITHER; + inverse_ptr->phases[count_phases].force = FALSE; + for (;;) + { + cxxSolutionIsotope temp_isotope; + j = copy_token(token, &cptr, &l); + if (j == EMPTY) + break; + strcpy(token1, token); + str_tolower(token1); + if (token1[0] == 'p') + { + inverse_ptr->phases[count_phases].constraint = PRECIPITATE; + } + else if (token1[0] == 'd') + { + inverse_ptr->phases[count_phases].constraint = DISSOLVE; + } + else if (token[0] == 'f') + { + inverse_ptr->phases[count_phases].force = TRUE; + } + else if (j == DIGIT) + { +/* + * read isotope data + */ + cptr1 = token; + + /* isotope number */ + get_num(&cptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (cptr1[0] == '\0' || isupper((int) cptr1[0]) == FALSE) + { + error_string = sformatf( "Expecting element name: %s.", cptr1); + error_msg(error_string, CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + + /* element name */ + temp_isotope.Set_elt_name(cptr1); + + /* ratio */ + j = copy_token(token, &cptr, &l); + if (j != DIGIT) + { + error_msg("Expecting isotope ratio for phase.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + (void)sscanf(token, SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + + /* read and store isotope ratio uncertainty */ + prev_next_char = cptr; + if (copy_token(token, &cptr, &l) != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + continue; + } + (void)sscanf(token, SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + temp_isotope.Set_ratio_uncertainty_defined(true); + isotopes.push_back(temp_isotope); + } + else + { + error_string = sformatf( + "Unknown option for inverse modeling phase."); + warning_msg(error_string); + } + } + if (isotopes.size() > 0) + { + inverse_ptr->phases[count_phases].isotopes.resize(isotopes.size()); + for (size_t i = 0; i < isotopes.size(); i++) + { + class isotope *iso_ptr = &(inverse_ptr->phases[count_phases].isotopes[i]); + iso_ptr->isotope_number = isotopes[i].Get_isotope_number(); + iso_ptr->elt_name = string_hsave(isotopes[i].Get_elt_name().c_str()); + iso_ptr->isotope_name = string_hsave(isotopes[i].Get_isotope_name().c_str()); + iso_ptr->total = isotopes[i].Get_total(); + iso_ptr->ratio = isotopes[i].Get_ratio(); + if (isotopes[i].Get_ratio_uncertainty_defined()) + iso_ptr->ratio_uncertainty = isotopes[i].Get_ratio_uncertainty(); + else + iso_ptr->ratio_uncertainty = NAN; + iso_ptr->x_ratio_uncertainty = isotopes[i].Get_x_ratio_uncertainty(); + iso_ptr->coef = isotopes[i].Get_coef(); + iso_ptr->master = NULL; + iso_ptr->primary = NULL; + } + } + else + { + inverse_ptr->phases[count_phases].isotopes.clear(); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads kinetics data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read kinetics + */ + const char* cptr; + std::string token; + int n_user; + LDBLE step; + + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "tol", /* 0 */ + "m", /* 1 */ + "m0", /* 2 */ + "parms", /* 3 */ + "formula", /* 4 */ + "steps", /* 5 */ + "step_divide", /* 6 */ + "parameters", /* 7 */ + "runge-kutta", /* 8 */ + "runge_kutta", /* 9 */ + "rk", /* 10 */ + "bad_step_max", /* 11 */ + "cvode", /* 12 */ + "cvode_steps", /* 13 */ + "cvode_order", /* 14 */ + "time_steps" /* 15 */ + }; + int count_opt_list = 16; + + cxxKinetics temp_kinetics(this->phrq_io); + cptr = line; + temp_kinetics.read_number_description(cptr); + n_user = temp_kinetics.Get_n_user(); + cxxKineticsComp *kinetics_comp_ptr = NULL; + std::string stdunits; +/* + * Set use data to first read + */ + if (use.Get_kinetics_in() == FALSE) + { + use.Set_kinetics_in(true); + use.Set_n_kinetics_user(n_user); + } +/* + * Read kinetics data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: /* allocate space, read new name */ + if (kinetics_comp_ptr) + { + temp_kinetics.Get_kinetics_comps().push_back(*kinetics_comp_ptr); + delete kinetics_comp_ptr; + } + kinetics_comp_ptr = new cxxKineticsComp; + cptr = line; + copy_token(token, &cptr); + kinetics_comp_ptr->Set_rate_name(token.c_str()); + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in KINETICS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* tolerance */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + char* ptr; + kinetics_comp_ptr->Set_tol(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for tolerance, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 1: /* m */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + char* ptr; + kinetics_comp_ptr->Set_m(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for moles of reactant (m), but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 2: /* m0 */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + char* ptr; + kinetics_comp_ptr->Set_m0(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for initial moles of reactant (m0), but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 3: /* parms */ + case 7: /* parameters */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + int j; + while ((j = copy_token(token, &next_char)) != EMPTY) + { + /* + * Store a LDBLE parameter + */ + if (j == DIGIT) + { + char* ptr; + kinetics_comp_ptr->Get_d_params().push_back(strtod(token.c_str(), &ptr)); + } + else + { + /* + * Store a character parameter + */ + kinetics_comp_ptr->Get_c_params().push_back(token); + } + } + } + break; + case 4: /* formula */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + /* + * Store reactant name, default coefficient + */ + cptr = next_char; + bool have_name = false; + std::string name; + LDBLE coef = 1; + while (copy_token(token, &cptr) != EMPTY) + { + coef = 1; + if (isalpha((int) token[0]) || (token[0] == '(') + || (token[0] == '[')) + { + if (have_name) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + } + name = token; + have_name = true; + } + else + { + if (!have_name) + { + error_string = sformatf( "No phase or chemical formula has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + /* + * Store relative coefficient + */ + int j = sscanf(token.c_str(), SCANFORMAT, &coef); + + if (j == 1) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + have_name = false; + } + else + { + error_msg + ("Reading relative coefficient of reactant.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + } + if (have_name) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + } + } + break; + case 5: /* steps */ + case 15: /* time_steps */ + /* + * Read one or more kinetics time increments + */ + { + int j; + while ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j == DIGIT) + { + /* Read next step increment(s) */ + /* multiple, equal timesteps 15 aug. 2005 */ + if (Utilities::replace("*", " ", token)) + { + int k; + if (sscanf(token.c_str(), "%d" SCANFORMAT, &k, &step) == 2) + { + for (int i = 0; i < k; i++) + { + temp_kinetics.Get_steps().push_back(step); + } + } + else + { + input_error++; + error_msg + ("Format error in multiple, equal KINETICS timesteps.\nCorrect is (for example): 20 4*10 2*5 3\n", + CONTINUE); + } + } + else + { + char* ptr; + step = strtod(token.c_str(), &ptr); + temp_kinetics.Get_steps().push_back(step); + } + } + else + { + Utilities::str_tolower(token); + if (token.substr(0,1) == "i" ) + { + /* + * Read number of increments + */ + if (temp_kinetics.Get_steps().size() != 1) + { + error_msg + ("To define equal time increments, only one total time should be defined.", + CONTINUE); + input_error++; + break; + } + temp_kinetics.Set_equalIncrements(true); + do + { + int i = 1; + j = sscanf(token.c_str(), "%d", &i); + if (j == 1) + { + temp_kinetics.Set_count(abs(i)); + break; + } + else if (j == 1 && i < 0) + { + error_msg + ("Expecting positive number for number of equal " + "time increments for kinetics.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + while (copy_token(token, &next_char) != EMPTY); + } + else + { + stdunits = token; + } + } + } + } + break; + case 6: /* step_divide */ + if (copy_token(token, &next_char) == DIGIT) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_kinetics.Set_step_divide(dummy); + } + else + { + error_string = sformatf( + "Expecting numerical value for step_divide."); + error_msg(error_string, CONTINUE); + input_error++; + } + break; + case 8: /* runge-kutta */ + case 9: /* runge_kutta */ + case 10: /* rk */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + char* ptr; + temp_kinetics.Set_rk((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting order for Runge-Kutta method."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 11: /* bad_step_max */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + char* ptr; + temp_kinetics.Set_bad_step_max((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( "Expecting maximal bad steps number."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 12: /* cvode */ + temp_kinetics.Set_use_cvode(get_true_false(next_char, TRUE) == TRUE); + break; + case 13: /* cvode_steps */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + char* ptr; + temp_kinetics.Set_cvode_steps((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting maximum number of steps for one call to cvode."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 14: /* cvode_order */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + char* ptr; + temp_kinetics.Set_cvode_order((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting number of terms (order) used in cvode (1-5)."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // save last comp + if (kinetics_comp_ptr) + { + temp_kinetics.Get_kinetics_comps().push_back(*kinetics_comp_ptr); + delete kinetics_comp_ptr; + } +/* + * Default reactant + */ + for (size_t i = 0; i < temp_kinetics.Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(temp_kinetics.Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_namecoef().size() == 0) + { + kinetics_comp_ptr->Get_namecoef().add(kinetics_comp_ptr->Get_rate_name().c_str(), 1.0); + } + } +/* + * Default 1 sec + */ + if (temp_kinetics.Get_steps().size() == 0) + { + temp_kinetics.Get_steps().push_back(1.0); + } + else if (stdunits.size() > 0) + { + std::vector::iterator it; + for (it = temp_kinetics.Get_steps().begin(); it != temp_kinetics.Get_steps().end(); it++) + { + *it = Utilities::convert_time(*it, stdunits, "s"); + } + } +/* + * set defaults for moles + */ + for (size_t i = 0; i < temp_kinetics.Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(temp_kinetics.Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_m0() < 0) + { + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_m0(1); + } + else + { + kinetics_comp_ptr->Set_m0(kinetics_comp_ptr->Get_m()); + } + } + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_m(kinetics_comp_ptr->Get_m0()); + } + } + Rxn_kinetics_map[n_user] = temp_kinetics; + return (return_value); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +read_vector_doubles(const char** cptr, std::vector& v) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads a list of LDBLE numbers until end of line is reached or + * a LDBLE cannot be read from a token. + */ + double value; + std::istringstream iss(*cptr); + while (iss >> value) + { + v.push_back(value); + } + return true; +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +read_vector_ints(const char** cptr, std::vector& v, int positive) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads a list of int numbers until end of line is reached or + * an int cannot be read from a token. + */ + int value; + std::istringstream iss(*cptr); + while (iss >> value) + { + v.push_back(value); + if (value <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return false; + } + } + return true; +} + +/* ---------------------------------------------------------------------- */ +int * Phreeqc:: +read_list_ints_range(const char **cptr, int *count_ints, int positive, int *int_list) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads a list of int numbers until end of line is reached or + * an int cannot be read from a token. + * + * Arguments: + * cptr entry: points to line to read from + * exit: points to next non-int token or end of line + * + * count_ints entry: number of ints already in list + * + * positive entry: if TRUE, expects to read only positive integers + * + * Returns: + * pointer to a list of count_ints ints + */ + char token[MAX_LENGTH]; + int value, value1, value2; + int i, l; + const char* cptr_save; + + if (int_list == NULL) + { + int_list = (int *) PHRQ_malloc(sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + *count_ints = 0; + } + cptr_save = *cptr; + while (copy_token(token, cptr, &l) != EMPTY) + { + if (sscanf(token, "%d", &value) == 1) + { + /* Read an integer */ + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + int_list[(*count_ints) - 1] = value; + if (value <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + /* Read range of integers */ + if (replace("-", " ", token) == TRUE) + { + if (sscanf(token, "%d %d", &value1, &value2) != 2) + { + error_msg("Expected an integer range n-m.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (value2 < value1) + { + error_msg("Expected an integer range n-m, with n <= m.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (value2 <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + for (i = value1 + 1; i <= value2; i++) + { + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * + sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + int_list[(*count_ints) - 1] = i; + } + } + } + cptr_save = *cptr; + } + else + { + *cptr = cptr_save; + break; + } + } + return (int_list); +} + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +read_vector_t_f(const char** cptr, std::vector& v) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads a list of true and false until end of line is reached or + * until non- t or f is found + */ + std::string token; + while (copy_token(token, cptr) != EMPTY) + { + str_tolower(token); + if (token[0] == 't') + { + v.push_back(true); + } + else if (token[0] == 'f') + { + v.push_back(false); + } + else + { + error_msg("Expected TRUE or FALSE.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return false; + } + + } + return true; +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_log_k_only(const char* cptr_in, LDBLE * log_k) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read log k + */ + *log_k = 0.0; + std::string stds(cptr_in); + replace(stds, "=", " "); + //replace("=", " ", cptr); + if (sscanf(stds.c_str(), SCANFORMAT, log_k) < 1) + { + input_error++; + error_msg("Expecting log k.", CONTINUE); + return (ERROR); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_t_c_only(const char* cptr_in, LDBLE *t_c) +/* ---------------------------------------------------------------------- */ +{ + *t_c = 0.0; + std::string stds(cptr_in); + replace(stds, "=", " "); + //replace("=", " ", cptr); + if (sscanf(stds.c_str(), SCANFORMAT, t_c) < 1) + { + input_error++; + error_msg("Expecting numeric value for critical temperature T_c (K)", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_p_c_only(const char* cptr, LDBLE * p_c) +/* ---------------------------------------------------------------------- */ +{ + *p_c = 0.0; + std::string stds(cptr); + replace(stds, "=", " "); + if (sscanf(stds.c_str(), SCANFORMAT, p_c) < 1) + { + input_error++; + error_msg("Expecting numeric value for critical pressure P_c (atm)", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_omega_only(const char* cptr, LDBLE *omega) +/* ---------------------------------------------------------------------- */ +{ + *omega = 0.0; + std::string stds(cptr); + replace(stds, "=", " "); + if (sscanf(stds.c_str(), SCANFORMAT, omega) < 1) + { + input_error++; + error_msg("Expecting numeric value for acentric factor Omega", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_aq_species_vm_parms(const char* cptr, LDBLE * delta_v) +/* ---------------------------------------------------------------------- */ +{ + int j; + /* + * Read supcrt parms and Ionic strength terms + */ + for (j = 0; j < 11; j++) + { + delta_v[j] = 0.0; + } + delta_v[9] = 1.0; +/* Vmax, dmax... + delta_v[10] = 999.0; + delta_v[11] = 1.0; */ + j = sscanf(cptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT , + /* a1..a4 */ + &(delta_v[0]), &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + /* wref */ + &(delta_v[4]), + /* b_Av */ + &(delta_v[5]), + /* c1..c4 */ + &(delta_v[6]), &(delta_v[7]), &(delta_v[8]), &(delta_v[9]), + //vmP, exP + &(delta_v[10]), &(delta_v[11])); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric values for calculating the species molar volume.", + CONTINUE); + return (ERROR); + } + /* multiply with factors. a1 is in cal/mol/bar. a2 in cal/mol, a3, a4 in cal K/mol + 41.84004 converts cal/mol/bar to cm3/mol. */ + delta_v[0] *= 41.84004e-1; + delta_v[1] *= 41.84004e2; + delta_v[2] *= 41.84004; + delta_v[3] *= 41.84004e4; + /* wref in cal/mol/bar */ + delta_v[4] *= 1e5; + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_vm_only(const char* cptr, LDBLE * delta_v, DELTA_V_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH]; + /* + * Read analytical expression + */ + for (j = 0; j < 9; j++) + { + delta_v[j] = 0.0; + } + j = sscanf(cptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(delta_v[0]), &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + &(delta_v[4]), &(delta_v[5]), &(delta_v[6]), &(delta_v[7])); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric value for the phase's molar volume, vm.", + CONTINUE); + return (ERROR); + } + /* + * Read delta V units + */ + *units = cm3_per_mol; + do + { + j = copy_token(token, &cptr, &l); + } while (j == DIGIT); + + if (j == EMPTY) + { + return (OK); + } + + LDBLE factor = 1.0; + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "cm3") != NULL) + { + /* cm3/mol */ + ; + } + else if (strstr(token, "dm3") != NULL) + { + /* Convert dm3/mol to cm3/mol */ + factor = 1e3; + } + else if (strstr(token, "m3") != NULL) + { + /* Convert m3/mol to cm3/mol */ + factor = 1e6; + } + + for (int i = 0; i < 8; i++) + { + delta_v[i] *= factor; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_phase_vm(const char* cptr, LDBLE * delta_v, DELTA_V_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH]; + /* + * Read analytical expression + */ + for (j = 0; j < 1; j++) + { + delta_v[j] = 0.0; + } + j = sscanf(cptr, SCANFORMAT /*SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT*/, + &(delta_v[0])/*, &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + &(delta_v[4]), &(delta_v[5]), &(delta_v[6]), &(delta_v[7])*/); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric value for the phase's molar volume, vm.", + CONTINUE); + return (ERROR); + } + /* + * Read delta V units + */ + *units = cm3_per_mol; + do + { + j = copy_token(token, &cptr, &l); + } while (j == DIGIT); + + if (j == EMPTY) + { + return (OK); + } + + LDBLE factor = 1.0; + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "cm3") != NULL) + { + /* cm3/mol */ + ; + } + else if (strstr(token, "dm3") != NULL) + { + /* Convert dm3/mol to cm3/mol */ + factor = 1e3; + *units = dm3_per_mol; + } + else if (strstr(token, "m3") != NULL) + { + /* Convert m3/mol to cm3/mol */ + factor = 1e6; + *units = m3_per_mol; + } + + for (int i = 0; i < 1; i++) + { + delta_v[i] *= factor; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_delta_h_only(const char* cptr_in, LDBLE * delta_h, DELTA_H_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l, kilo, joul; + char token[MAX_LENGTH]; +/* + * Read delta H + */ + *delta_h = 0.0; + std::string stds(cptr_in); + replace(stds, "=", " "); + const char* cptr = stds.c_str(); + j = copy_token(token, &cptr, &l); + if (j == EMPTY) + { + input_error++; + error_msg("Expecting numeric value for delta H.", CONTINUE); + return (ERROR); + } + if (sscanf(token, SCANFORMAT, delta_h) < 1) + { + input_error++; + error_msg("Expecting numeric value for delta H.", CONTINUE); + return (ERROR); + } +/* + * Read delta H units + */ + j = copy_token(token, &cptr, &l); + *units = kjoules; + kilo = TRUE; + joul = TRUE; + if (j == EMPTY) + { + return (OK); + } + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "k") != token) + { + /* convert to kilo */ + kilo = FALSE; + *delta_h /= 1000.; + } + if (strstr(token, "c") != NULL) + { + /* convert to joules */ + *delta_h *= JOULES_PER_CALORIE; + joul = FALSE; + } + } + if (kilo == TRUE && joul == TRUE) + { + *units = kjoules; + } + else if (kilo == FALSE && joul == TRUE) + { + *units = joules; + } + else if (kilo == TRUE && joul == FALSE) + { + *units = kcal; + } + else if (kilo == FALSE && joul == FALSE) + { + *units = cal; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_analytical_expression_only(const char* cptr, LDBLE * log_k) +/* ---------------------------------------------------------------------- */ +{ + int j; + int num_terms = T_A6 - T_A1 + 1; +/* + * Read analytical expression + */ + for (j = 0; j < num_terms; j++) + { + log_k[j] = 0.0; + } + j = sscanf(cptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(log_k[0]), &(log_k[1]), &(log_k[2]), &(log_k[3]), + &(log_k[4]), &(log_k[5])); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric values for analytical expression.", + CONTINUE); + return (ERROR); + } + return (OK); +} + +/* VP: Density Start */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_millero_abcdef (const char* cptr, LDBLE * abcdef) +/* ---------------------------------------------------------------------- */ +{ + int j; +/* + * Read a, b, c, d, e, f, and kappa parameters for Millero density model. + */ + for (j = 0; j < 7; j++) + { + abcdef[j] = 0.0; + } + j = sscanf (cptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(abcdef[0]), &(abcdef[1]), &(abcdef[2]), &(abcdef[3]), &(abcdef[4]), &(abcdef[5]), &(abcdef[6])); + if (j < 1) + { + input_error++; + error_msg ("Expecting numeric values for analytical expression.", + CONTINUE); + return (ERROR); + } + return (OK); +} +/* VP: Density End */ + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_viscosity_parms(const char* cptr, LDBLE * Jones_Dole) +/* ---------------------------------------------------------------------- */ +{ + int j; +/* + * Read . + */ + for (j = 0; j <= 9; j++) + { + Jones_Dole[j] = 0.0; + } + j = sscanf (cptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(Jones_Dole[0]), &(Jones_Dole[1]), &(Jones_Dole[2]), &(Jones_Dole[3]), &(Jones_Dole[4]), &(Jones_Dole[5]), &(Jones_Dole[6]), &(Jones_Dole[7]), &(Jones_Dole[8]), &(Jones_Dole[9])); + if (j < 1) + { + input_error++; + error_msg ("Expecting numeric values for viscosity calculation.", + CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_incremental_reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Define flow only + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int j, l; + const char* cptr; + char token[MAX_LENGTH]; + + cptr = line; + /* read keyword */ + copy_token(token, &cptr, &l); + + /* read true or false */ + incremental_reactions = get_true_false(cptr, TRUE); +/* + * find next keyword + */ + while ((j = + check_line("Subroutine Read", FALSE, TRUE, TRUE, + FALSE)) != KEYWORD) + { + /* empty, eof, keyword, print */ + if (j == EOF) + return (EOF); + error_string = sformatf( "Unknown input: %s", line); + error_msg(error_string, CONTINUE); + input_error++; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_master_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species data from data file or input file + */ + int j, i, l; + const char* cptr, *cptr1; + LDBLE l_z; + class element *elts_ptr; + class species *s_ptr; + char token[MAX_LENGTH]; + + elts_ptr = NULL; + for (;;) + { + j = check_line("Master species", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } +/* + * Get element name with valence, allocate space, store + */ + cptr = line; +/* + * Get element name and save pointer to character string + */ + if (copy_token(token, &cptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + /* + if (token[0] == '[') { + cptr1 = token; + get_elt(&cptr, element, &l); + strcpy(token, element); + } + */ + replace("(+", "(", token); +/* + * Delete master if it exists + */ + master_delete(token); +/* + * Increase pointer array, if necessary, and malloc space + */ + size_t count_master = master.size(); + master.resize(count_master + 1); + master[count_master] = master_alloc(); +/* + * Set type to AQ + */ + master[count_master]->type = AQ; +/* + * Save element name + */ + master[count_master]->elt = element_store(token); + std::string ename = token; +/* + * Save pointer to species data for master species + */ + if ((copy_token(token, &cptr, &l) != UPPER) && + token[0] != '[' && (strcmp_nocase_arg1(token, "e-") != 0)) + { + parse_error++; + error_msg("Reading master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + cptr1 = token; + std::string token1; + get_token(&cptr1, token1, &l_z, &l); + master[count_master]->s = s_store(token1.c_str(), l_z, FALSE); + } + + std::string sname = token; + replace("("," ", ename); + std::istringstream iss(ename); + iss >> ename; + if (ename != "e" && ename != "E" && ename != "Alkalinity" && std::string::npos == sname.find(ename)) + { + input_error++; + std::ostringstream oss; + oss << "Master species, " << sname << " must contain the element, " << ename; + error_msg(oss.str().c_str(), CONTINUE); + continue; + } + +/* + * Read alkalinity for species + */ + copy_token(token, &cptr, &l); + i = sscanf(token, SCANFORMAT, &master[count_master]->alk); + if (i != 1) + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected alkalinity for master species, %s, in master species input.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected alkalinity for master species in master species input."); + } + error_msg(error_string, CONTINUE); + continue; + } +/* + * Read default gfw for species + */ + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, SCANFORMAT, &master[count_master]->gfw); + } + else if (i == UPPER) + { + master[count_master]->gfw_formula = string_hsave(token); + } + else + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected gram formula weight for master species, %s, in master species input.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected gram formula weight for master species in master species input."); + } + error_msg(error_string, CONTINUE); + continue; + } +/* + * MAKE LISTS OF PRIMARY AND SECONDARY MASTER SPECIES + */ + if (strchr(master[count_master]->elt->name, '(') == NULL) + { + master[count_master]->primary = TRUE; + /* Read gram formula weight for primary */ + if (strcmp(master[count_master]->elt->name, "E") != 0) + { + elts_ptr = master[count_master]->elt; + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, SCANFORMAT, &elts_ptr->gfw); + } + else + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected gram formula weight for element, %s.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected gram formula weight for element."); + } + + error_msg(error_string, CONTINUE); + continue; + } + } + } + else + { + master[count_master]->primary = FALSE; + } + } + gfw_map.clear(); + return (j); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_mix(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads mixing fractions + */ + int n_user; + int return_value; + int n_solution; + LDBLE fraction; + int j, i, l; + const char* cptr; + char token[MAX_LENGTH]; + cxxMix temp_mix; + + cptr = line; + temp_mix.read_number_description(cptr); + n_user = temp_mix.Get_n_user(); +/* + * Set use data to first read + */ + if (use.Get_mix_in() == FALSE) + { + use.Set_mix_in(true); + use.Set_n_mix_user(n_user); + } +/* + * Read mixture data + */ + for (;;) + { + return_value = check_line("Mixture data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + cptr = line; +/* + * Read n_user + */ + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, "%d ", &n_solution); + } + else + { + input_error++; + error_msg("Expected a solution number in mix input.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Read fraction for solution + */ + copy_token(token, &cptr, &l); + j = sscanf(token, SCANFORMAT, &fraction); + if (j != 1) + { + input_error++; + error_msg("Expected a mixing fraction.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + +/* + * Save data + */ + temp_mix.Add(n_solution ,fraction); + } + if (temp_mix.Get_mixComps().size() == 0) + { + input_error++; + error_msg + ("Must define at least one solution number and mixing fraction for MIX input.", + CONTINUE); + } + Rxn_mix_map[n_user] = temp_mix; + + // copy if needed + if (temp_mix.Get_n_user_end() > n_user) + { + int i; + for (i = n_user + 1; i <= temp_mix.Get_n_user_end(); i++) + { + Utilities::Rxn_copy(Rxn_mix_map, n_user, i); + } + } + + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_entity_mix(std::map &mix_map) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads mixing fractions + */ + int return_value; + int n_solution; + LDBLE fraction; + int j, i, l; + const char* cptr; + char token[MAX_LENGTH]; + cxxMix temp_mix; + +/* + * Read mix number + */ + cptr = line; + temp_mix.read_number_description(line); +/* + * Read mixture data + */ + for (;;) + { + return_value = check_line("Mix raw data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + cptr = line; +/* + * Read n_user + */ + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, "%d ", &n_solution); + } + else + { + input_error++; + error_msg("Expected a number in mix input.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Read fraction for entity + */ + copy_token(token, &cptr, &l); + j = sscanf(token, SCANFORMAT, &fraction); + if (j != 1) + { + input_error++; + error_msg("Expected a mixing fraction.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Save data + */ + temp_mix.Add(n_solution ,fraction); + } + if (temp_mix.Get_mixComps().size() == 0) + { + input_error++; + error_msg + ("Must define at least one number and mixing fraction for mix input.", + CONTINUE); + } + mix_map[temp_mix.Get_n_user()] = temp_mix; + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_number_description(const char* cptr, int *n_user, + int *n_user_end, char **description, int allow_negative) +/* ---------------------------------------------------------------------- */ +{ + int l, n; + char token[MAX_LENGTH]; + const char* cptr1; +/* + * Read user number, allow negative numbers Oct 3, 2011 + */ + copy_token(token, &cptr, &l); // keyword + cptr1 = cptr; + copy_token(token, &cptr, &l); + + if (!isdigit(token[0]) && token[0] != '-') + { + *n_user = 1; + *n_user_end = 1; + } + else + { + if (replace("-", " ", &token[1])) + { + n = sscanf(token, "%d%d", n_user, n_user_end); + if (n != 2) + { + if (n == 0) + { + *n_user_end = *n_user = 1; + } + else + { + *n_user_end = *n_user; + } + if (next_keyword >= 0) + { + error_string = sformatf( "Reading number range for %s.", Keywords::Keyword_name_search(next_keyword).c_str()); + } + else + { + error_string = sformatf( "Reading number range for keyword."); + } + error_msg(error_string, CONTINUE); + input_error++; + } + cptr1 = cptr; + } + else + { + n = sscanf(token, "%d", n_user); + if (n != 1) + { + if (next_keyword >= 0) + { + error_string = sformatf( "Reading number range for %s.", Keywords::Keyword_name_search(next_keyword).c_str()); + } + else + { + error_string = sformatf( "Reading number range for keyword."); + } + error_msg(error_string, CONTINUE); + input_error++; + } + *n_user_end = *n_user; + cptr1 = cptr; + }; + } + if (*n_user < 0 && allow_negative == FALSE) + { + error_string = sformatf( "Negative number in number range not allowed for keyword."); + error_msg(error_string, CONTINUE); + input_error++; + } +/* + * Read description + */ + for (; isspace((int) cptr1[0]); cptr1++); + *description = string_duplicate(cptr1); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_phases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for phases, parse equations + */ + int j, i, l; + int association; + const char* cptr; + char token[MAX_LENGTH]; + char token1[MAX_LENGTH]; + class phase *phase_ptr; + class rxn_token *token_ptr; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "log_k", /* 2 */ + "logk", /* 3 */ + "delta_h", /* 4 */ + "deltah", /* 5 */ + "analytical_expression", /* 6 */ + "a_e", /* 7 */ + "ae", /* 8 */ + "add_logk", /* 9 */ + "add_log_k", /* 10 */ + "add_constant", /* 11 */ + "t_c", /* 12 */ + "p_c", /* 13 */ + "omega", /* 14 */ + "vm" /* 15, molar volume, must replace delta_v */ + }; + int count_opt_list = 16; + association = FALSE; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + phase_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PHASES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (phase_ptr == NULL) + break; + phase_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (phase_ptr == NULL) + break; + phase_ptr->check_equation = TRUE; + break; + case 2: /* log_k */ + case 3: /* logk */ + if (phase_ptr == NULL) + break; + read_log_k_only(next_char, &phase_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 4: /* delta_h */ + case 5: /* deltah */ + if (phase_ptr == NULL) + break; + read_delta_h_only(next_char, &phase_ptr->logk[1], + &phase_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 6: /* analytical_expression */ + case 7: /* a_e */ + case 8: /* ae */ + if (phase_ptr == NULL) + break; + read_analytical_expression_only(next_char, &(phase_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 9: /* add_logk */ + case 10: /* add_log_k */ + { + if (phase_ptr == NULL) + break; + size_t count_add_logk = phase_ptr->add_logk.size(); + phase_ptr->add_logk.resize(count_add_logk + 1); + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + phase_ptr->add_logk[count_add_logk].name = + string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &phase_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + phase_ptr->add_logk[count_add_logk].coef = 1; + } + opt_save = OPTION_DEFAULT; + } + break; + case 11: /* add_constant */ + { + if (phase_ptr == NULL) + break; + size_t count_add_logk = phase_ptr->add_logk.size(); + phase_ptr->add_logk.resize(count_add_logk + 1); + i = sscanf(next_char, SCANFORMAT, + &phase_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + phase_ptr->add_logk[count_add_logk].name = + string_hsave("XconstantX"); + opt_save = OPTION_DEFAULT; + } + break; + case 12: /* T_c */ + if (phase_ptr == NULL) + break; + read_t_c_only(next_char, &phase_ptr->t_c); + opt_save = OPTION_DEFAULT; + break; + case 13: /* P_c */ + if (phase_ptr == NULL) + break; + read_p_c_only(next_char, &phase_ptr->p_c); + opt_save = OPTION_DEFAULT; + break; + case 14: /* Omega */ + if (phase_ptr == NULL) + break; + read_omega_only(next_char, &phase_ptr->omega); + opt_save = OPTION_DEFAULT; + break; + case 15: /* vm, molar volume */ + if (phase_ptr == NULL) + break; + read_phase_vm(next_char, &(phase_ptr->logk[vm0]), + &phase_ptr->original_deltav_units); + phase_ptr->delta_v[1] = phase_ptr->logk[vm0]; + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: + { + /* + * Get element name and save pointer to character string + */ + phase_ptr = NULL; + cptr = line; + copy_token(token, &cptr, &l); + /* + * Get and parse equation + */ + j = check_line("Phase equation", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + return_value = j; + break; + } + if (j == OPTION) + { + parse_error++; + error_string = sformatf("Expecting equation for phase %s.", token); + error_msg(error_string, CONTINUE); + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + std::vector new_elt_list; + if (parse_eq(line, new_elt_list, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + phase_ptr = phase_store(token); + /* + * Get pointer to each species in the reaction, store new species if necessary + */ + strcpy(token1, trxn.token[0].name); + replace("(g)", "", token1); + replace("(s)", "", token1); + replace("(G)", "", token1); + replace("(S)", "", token1); + phase_ptr->formula = string_hsave(token1); + for (i = 1; i < count_trxn; i++) + { + if ((strstr(trxn.token[i].name, "(s)") == NULL) && + (strstr(trxn.token[i].name, "(g)") == NULL) && + (strstr(trxn.token[i].name, "(S)") == NULL) && + (strstr(trxn.token[i].name, "(G)") == NULL)) + { + strcpy(token1, trxn.token[i].name); + replace("(aq)", "", token1); + replace("(AQ)", "", token1); + replace("H2O(l)", "H2O", token1); + replace("(H2O(L)", "H2O", token1); + trxn.token[i].s = s_store(token1, trxn.token[i].z, FALSE); + } + else + { + trxn.token[i].s = NULL; + } + } + /* + * Save element list + */ + phase_ptr->next_elt = new_elt_list; + /* + * Copy reaction to reaction for phase, first token (token[0]) is not used + * except to check that coef of phase formula = 1.0 + */ + trxn_copy(phase_ptr->rxn); + token_ptr = &phase_ptr->rxn.token[0]; + token_ptr[0].name = trxn.token[1].name; + token_ptr[i].s = NULL; + token_ptr[i].name = NULL; + /* + * Set type for phase + */ + phase_ptr->type = SOLID; + opt_save = OPTION_DEFAULT; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads pp_assemblage data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int j; + int return_value; + int n_user; + const char* cptr; + std::string token; + int opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "force_equality" /* 0 */ + }; + int count_opt_list = 1; + /* + * Find pp_assemblage or realloc space for pp_assemblage + */ + cxxPPassemblage temp_pp_assemblage; + cptr = line; + temp_pp_assemblage.read_number_description(cptr); + n_user = temp_pp_assemblage.Get_n_user(); + cxxPPassemblageComp *comp = NULL; + std::map comps; + temp_pp_assemblage.Set_new_def(true); + /* + * Set use data to first read + */ + if (use.Get_pp_assemblage_in() == FALSE) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(n_user); + } + /* + * Read equilibrium phase data + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EQUILIBRIUM_PHASES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* force_equality */ + if (comp == NULL) + { + error_msg + ("Force_equality defined before equilibrium phase has been defined.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + comp->Set_force_equality(get_true_false(next_char, TRUE) == TRUE); + } + break; + case OPTION_DEFAULT: + /* + * Make space, set default + */ + if (comp) + { + comps[comp->Get_name()] = *comp; + } + delete comp; + comp = new cxxPPassemblageComp; + /* + * Read name + */ + cptr = line; + copy_token(token, &cptr); + comp->Set_name(token.c_str()); + + if ((j = copy_token(token, &cptr)) == EMPTY) + continue; + /* + * Read saturation index + */ + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp->Set_si(dummy); + comp->Set_si_org(dummy); + if (j != 1) + { + error_msg("Expected saturation index.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + continue; + } + /* + * Adding a reaction to the phase boundary + */ + if ((j = copy_token(token, &cptr)) == EMPTY) + continue; + if (j == UPPER || j == LOWER) + { + comp->Set_add_formula(token.c_str()); + j = copy_token(token, &cptr); + } + /* + * Read amount + */ + if (j == EMPTY) + continue; + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + if (dummy < 0) + { + error_string = sformatf( "Moles of mineral < 0, reset to 0."); + dummy = 0; + warning_msg(error_string); + } + comp->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected amount of mineral.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + continue; + } + if ((j = copy_token(token, &cptr)) == EMPTY) + continue; + Utilities::str_tolower(token); + if (strstr(token.c_str(), "d") == token.c_str()) + { + comp->Set_dissolve_only(true); + comp->Set_precipitate_only(false); + } else if (strstr(token.c_str(), "p") == token.c_str()) + { + comp->Set_precipitate_only(true); + comp->Set_dissolve_only(false); + } + else + { + error_msg + ("Unexpected data at end of equilibrium-phase definition.", + CONTINUE); + input_error++; + continue; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + if (comp) + { + comps[comp->Get_name()] = *comp; + delete comp; + comp = NULL; + } + temp_pp_assemblage.Set_pp_assemblage_comps(comps); + Rxn_pp_assemblage_map[n_user] = temp_pp_assemblage; + Rxn_new_pp_assemblage.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads reaction data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read reaction + */ + int l; + const char* cptr; + char token[MAX_LENGTH]; + int return_value; + int n_user; +/* + * Defaults + */ + cxxReaction temp_reaction; + cptr = line; + temp_reaction.read_number_description(cptr); + n_user = temp_reaction.Get_n_user(); +/* + * Set use data to first read + */ + if (use.Get_reaction_in() == FALSE) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(n_user); + } +/* + * Read reaction data + */ + for (;;) + { +/* + * Read line + */ + return_value = check_line("Reaction data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + cptr = line; + copy_token(token, &cptr, &l); + if (isalpha((int) token[0]) || (token[0] == '(') || (token[0] == '[')) + { +/* + * Read reactant information + */ + read_reaction_reactants(&temp_reaction); + } + else + { +/* + * Read steps information + */ + read_reaction_steps(&temp_reaction); + } + } +/* + * Default 1 mol of reaction + */ + if (temp_reaction.Get_steps().size() == 0) + { + std::vector v; + v.push_back(1.0); + temp_reaction.Set_steps(v); + } + if (temp_reaction.Get_equalIncrements()) + { + if (temp_reaction.Get_countSteps() == 0) + { + temp_reaction.Set_countSteps(1); + } + } + Rxn_reaction_map[n_user] = temp_reaction; + // copy if needed + Utilities::Rxn_copies(Rxn_reaction_map, n_user, temp_reaction.Get_n_user_end()); + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_reactants(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read reactants, may be a chemical formula or a pure_phase name + * followed by relative reaction coefficient, default 1.0. + * + */ + std::string token, last_token; + LDBLE coef; + const char* cptr; +/* + * Read one or more reactants + */ + cptr = line; + while (copy_token(token, &cptr) != EMPTY) + { +/* + * Store reactant name, default coefficient + */ + if (isalpha((int) token[0]) || (token[0] == '(') || (token[0] == '[')) + { + + reaction_ptr->Get_reactantList()[token] = 1.0; + last_token = token; +/* + * Store relative coefficient + */ + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &coef); + if (j == 1 && last_token.size() > 0) + { + reaction_ptr->Get_reactantList()[last_token] = coef; + } + else + { + error_msg("Reading relative coefficient of reactant.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_steps(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read amount(s) of irrev reactions in one of three forms: + * + * 6 millimoles in 6 steps or + * + * 1 2 3 4 5 6 millimoles or + * + * 6*1 millimoles + * INCREMENTAL_REACTIONS + */ + + const char* cptr; + std::string token, token1; + + cptr = line; +/* + * Read one or more reaction increments + */ + for (;;) + { + if (copy_token(token, &cptr) == EMPTY) + { + return (OK); + } +/* + * Read next step increment + */ +/* begin modif 29 july 2005... */ + if (replace("*", " ", token)) + { + int n; + LDBLE value; + if (sscanf(token.c_str(), "%d" SCANFORMAT, &n, &value) == 2) + { + for (int i = 0; i < n; i++) + { + reaction_ptr->Get_steps().push_back(value); + } + } + else + { + input_error++; + error_msg + ("Format error in multiple, equal REACTION steps.\nCorrect is (for example): 0.2 4*0.1 2*0.5 0.3\n", + CONTINUE); + } + } + else + { + LDBLE step; + int j = sscanf(token.c_str(), SCANFORMAT, &step); + if (j == 1) + { + reaction_ptr->Get_steps().push_back(step); + } + else + { + break; + } + } +/* ...end modif 29 july 2005 */ + } +/* + * Read units + */ + token1 = token; + token1.append("/l"); + std::string t1 = token1; + if (check_units(t1, false, false, NULL, false) == OK) + { + replace("/l", "", t1); + if (strstr(t1.c_str(), "Mol") == NULL) + { + error_string = sformatf( "Units of steps not in moles, %s.", token.c_str()); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + else + { + reaction_ptr->Set_units(t1.c_str()); + } + if (copy_token(token, &cptr) == EMPTY) + { + return (OK); + } + } +/* + * Read number of equal increments, store as negative integer + */ + if (reaction_ptr->Get_reaction_steps() != 1) + { + error_msg + ("To define equal increments, only one reaction increment should be defined.", + CONTINUE); + input_error++; + return (ERROR); + } + do + { + int i; + int j = sscanf(token.c_str(), "%d", &i); + if (j == 1 && i > 0) + { + reaction_ptr->Set_countSteps(i); + reaction_ptr->Set_equalIncrements(true); + return (OK); + } + else if (j == 1 && i <= 0) + { + break; + } + } + while (copy_token(token, &cptr) != EMPTY); + + error_msg("Expecting positive number for number of equal " + "increments to add.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_save(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, mix, irreversible reaction, and pure phases to use + * in reaction calculation + */ + int i, l, n, n_user, n_user_end; + const char* cptr; + char token[MAX_LENGTH]; +/* + * Read "save" + */ + cptr = line; + copy_token(token, &cptr, &l); +/* + * Read keyword + */ + copy_token(token, &cptr, &l); + check_key(token); +/* + * Read number + */ + for (;;) + { + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + replace("-", " ", token); + n = sscanf(token, "%d%d", &n_user, &n_user_end); + if (n == 1) + { + n_user_end = n_user; + } + if (n_user < 0) + { + error_msg("Number must be a positive integer.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + break; + } + else if (i == EMPTY) + { + error_string = sformatf( "No number given, 1 assumed."); + warning_msg(error_string); + n_user = 1; + n_user_end = 1; + break; + } + } + + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + save.solution = TRUE; + save.n_solution_user = n_user; + save.n_solution_user_end = n_user_end; + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = n_user; + save.n_pp_assemblage_user_end = n_user_end; + break; + case Keywords::KEY_EXCHANGE: /* exchange */ + save.exchange = TRUE; + save.n_exchange_user = n_user; + save.n_exchange_user_end = n_user_end; + break; + case Keywords::KEY_SURFACE: /* surface */ + save.surface = TRUE; + save.n_surface_user = n_user; + save.n_surface_user_end = n_user_end; + break; + case Keywords::KEY_GAS_PHASE: /* gas_phase */ + save.gas_phase = TRUE; + save.n_gas_phase_user = n_user; + save.n_gas_phase_user_end = n_user_end; + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = n_user; + save.n_ss_assemblage_user_end = n_user_end; + break; + default: + input_error++; + error_msg + ("Expecting keyword solution, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions.", + CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of save", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } + + check_line("End of save", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_selected_output(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for to output to flat file + */ + int value; + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "file", /* 0 */ + "totals", /* 1 */ + "molalities", /* 2 */ + "activities", /* 3 */ + "pure_phases", /* 4 */ + "si", /* 5 */ + "saturation_indices", /* 6 */ + "gases", /* 7 */ + "equilibrium_phases", /* 8 */ + "equilibria", /* 9 */ + "equilibrium", /* 10 */ + "pure", /* 11 */ + "inverse", /* 12 */ + "kinetic_reactants", /* 13 */ + "kinetics", /* 14 */ + "solid_solutions", /* 15 */ + "inverse_modeling", /* 16 */ + "reset", /* 17 */ + "simulation", /* 18 */ + "sim", /* 19 */ + "state", /* 20 */ + "solution", /* 21 */ + "soln", /* 22 */ + "distance", /* 23 */ + "dist", /* 24 */ + "time", /* 25 */ + "step", /* 26 */ + "reaction", /* 27 */ + "rxn", /* 28 */ + "temperature", /* 29 */ + "temp", /* 30 */ + "ph", /* 31 */ + "pe", /* 32 */ + "alkalinity", /* 33 */ + "alk", /* 34 */ + "ionic_strength", /* 35 */ + "mu", /* 36 */ + "water", /* 37 */ + "high_precision", /* 38 */ + "user_punch", /* 39 */ + "mol", /* 40 */ + "kin", /* 41 */ + "charge_balance", /* 42 */ + "percent_error", /* 43 */ + "selected_out", /* 44 */ + "selected_output", /* 45 */ + "isotopes", /* 46 */ + "calculate_values", /* 47 */ + "equilibrium_phase", /* 48 */ + "active", /* 49 */ + "new_line" /* 50 */ + }; + int count_opt_list = 51; + + int i, l; + char token[MAX_LENGTH]; + std::string file_name; + const char* cptr; + int n_user; + + SelectedOutput temp_selected_output; + cptr = line; + temp_selected_output.read_number_description(cptr); + n_user = temp_selected_output.Get_n_user(); + temp_selected_output.Set_new_def(false); + temp_selected_output.Set_file_name(n_user); + // find if it exists + std::map< int, SelectedOutput >::iterator so = SelectedOutput_map.find(n_user); + if (n_user == 1 && so != SelectedOutput_map.end()) + { + // n_user = 1, old definition, keep old definition + SelectedOutput & so_ref = so->second; + temp_selected_output.Set_active ( so_ref.Get_active() ); + temp_selected_output.Set_new_line ( so_ref.Get_new_line() ); + temp_selected_output.Set_inverse ( so_ref.Get_inverse() ); + temp_selected_output.Set_sim ( so_ref.Get_sim() ); + temp_selected_output.Set_state ( so_ref.Get_state() ); + temp_selected_output.Set_soln ( so_ref.Get_soln() ); + temp_selected_output.Set_dist ( so_ref.Get_dist() ); + temp_selected_output.Set_time ( so_ref.Get_time() ); + temp_selected_output.Set_step ( so_ref.Get_step() ); + temp_selected_output.Set_rxn ( so_ref.Get_rxn() ); + temp_selected_output.Set_temp ( so_ref.Get_temp() ); + temp_selected_output.Set_ph ( so_ref.Get_ph() ); + temp_selected_output.Set_pe ( so_ref.Get_pe() ); + temp_selected_output.Set_alk ( so_ref.Get_alk() ); + temp_selected_output.Set_mu ( so_ref.Get_mu() ); + temp_selected_output.Set_water ( so_ref.Get_water() ); + temp_selected_output.Set_high_precision ( so_ref.Get_high_precision() ); + temp_selected_output.Set_user_punch ( so_ref.Get_user_punch() ); + temp_selected_output.Set_charge_balance ( so_ref.Get_charge_balance() ); + temp_selected_output.Set_percent_error ( so_ref.Get_percent_error() ); + temp_selected_output.Set_have_punch_name ( so_ref.Get_have_punch_name() ); + temp_selected_output.Set_file_name ( so_ref.Get_file_name() ); +#if PHREEQCI_GUI + assert(false); +#endif + } + else if (n_user == 1 && so == SelectedOutput_map.end()) + { + // n_user = 1, new definition, do nothing use; constructor default + temp_selected_output.Set_new_def(true); + } + else + { + // n_user != 1 then reset false + + temp_selected_output.Reset(false); + if (so == SelectedOutput_map.end()) + { + temp_selected_output.Set_new_def(true); + } + } + CParser parser(this->phrq_io); + +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = opt; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SELECTED_OUTPUT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* file name */ + { + temp_selected_output.Set_new_def(true); + std::string temp_name(next_char); + string_trim(temp_name); + if (temp_name.size() > 0) + { + file_name = temp_name; + temp_selected_output.Set_file_name(file_name); + temp_selected_output.Set_have_punch_name(true); + } + opt_save = OPTION_ERROR; + } + break; + case 1: /* totals */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_totals().push_back(t_pair); + } + } + break; + case 2: /* molalities */ + case 40: /* mol */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_molalities().push_back(t_pair); + } + } + break; + case 3: /* activities */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_activities().push_back(t_pair); + } + } + break; + case 4: /* pure_phases */ + case 8: /* equilibrium_phases */ + case 9: /* equilibria */ + case 10: /* equilibrium */ + case 11: /* pure */ + case 48: /* equilibrium_phase */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_pure_phases().push_back(t_pair); + } + break; + case 5: /* si */ + case 6: /* saturation_index */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_si().push_back(t_pair); + } + break; + case 7: /* gases */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_gases().push_back(t_pair); + } + break; + case 12: /* inverse */ + case 16: /* inverse_modeling */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_inverse(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 13: /* kinetic_reactants */ + case 14: /* kinetics */ + case 41: /* kin */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_kinetics().push_back(t_pair); + } + break; + case 15: /* solid_solutions */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_s_s().push_back(t_pair); + } + break; + case 46: /* isotopes */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_isotopes().push_back(t_pair); + } + } + break; + case 47: /* calculate_values */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_calculate_values().push_back(t_pair); + } + break; + case 17: /* reset */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + /* matches print order */ + temp_selected_output.Reset(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 18: /* simulation */ + case 19: /* sim */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_sim(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 20: /* state */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_state(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 21: /* solution */ + case 22: /* soln */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_soln(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 23: /* distance */ + case 24: /* dist */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_dist(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 25: /* time */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_time(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 26: /* step */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_step(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 27: /* reaction */ + case 28: /* rxn */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_rxn(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 29: /* temperature */ + case 30: /* temp */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_temp(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 31: /* ph */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_ph(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 32: /* pe */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_pe(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 33: /* alkalinity */ + case 34: /* alk */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_alk(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 35: /* ionic strength */ + case 36: /* mu */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_mu(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 37: /* water */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_water(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 38: /* high_precision */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_high_precision(value!=FALSE); + //if (n_user == 1) + //{ + // high_precision = (value != FALSE); + //} + if (value == TRUE) + { + convergence_tolerance = 1e-12; + } + opt_save = OPTION_ERROR; + break; + case 39: /* user_punch */ + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_user_punch(value!=FALSE); + if (so != SelectedOutput_map.end()) + { + so->second.Set_user_punch(value!=FALSE); + } opt_save = OPTION_ERROR; + break; + case 42: /* charge_balance */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_charge_balance(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 43: /* percent_error */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_percent_error(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 44: /* selected_out */ + case 45: /* selected_output */ + //warning_msg("Use PRINT; -selected_output, not SELECTED_OUTPUT; -selected_output"); + //value = get_true_false(next_char, TRUE); + //temp_selected_output.Set_active(value!=FALSE); + //opt_save = OPTION_ERROR; + //break; + case 49: /* active */ + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_active(value!=FALSE); + if (so != SelectedOutput_map.end()) + { + so->second.Set_active(value!=FALSE); + } + opt_save = OPTION_ERROR; + break; + case 50: /* new_line */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_new_line(value != FALSE); + opt_save = OPTION_ERROR; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + if (temp_selected_output.Get_new_def() || so == SelectedOutput_map.end()) + { + // delete if exists + if (so != SelectedOutput_map.end()) + { + SelectedOutput_map.erase(so); + } + + // store new selected output + SelectedOutput_map[n_user] = temp_selected_output; + + if (punch_open(SelectedOutput_map[n_user].Get_file_name().c_str(), n_user)) + { + if (phrq_io) + { + SelectedOutput_map[n_user].Set_punch_ostream(phrq_io->Get_punch_ostream()); + phrq_io->Set_punch_ostream(NULL); + } + } + else + { + error_string = sformatf( "Can`t open file, %s.", SelectedOutput_map[n_user].Get_file_name().c_str()); + input_error++; + error_msg(error_string, CONTINUE); + } + } + + //if (!have_punch_name) + //{ + // punch_close(); + // if (!punch_open("selected.out")) + // { + // error_string = sformatf( "Can`t open file, %s.", "selected.out"); + // input_error++; + // error_msg(error_string, CONTINUE); + // } + //} + + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user; + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "press", /* 11 */ + "pressure", /* 12 */ + "potential" /* 13 */ + }; + int count_opt_list = 14; + + cxxSolution temp_solution; + const char* cptr = line; + temp_solution.read_number_description(cptr); + n_user = temp_solution.Get_n_user(); + temp_solution.Set_new_def(true); + temp_solution.Create_initial_data(); + cxxISolution *isoln_ptr = temp_solution.Get_initial_data(); + CParser parser(this->phrq_io); + + if (!use.Get_solution_in()) + { + use.Set_solution_in(true); + use.Set_n_solution_user(n_user); + } + +/* + * Read concentration data + */ + std::string token; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + cptr = next_char; + if (copy_token(token, &cptr) == CParser::TT_DIGIT) + { + opt = 9; + } + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* temperature */ + case 1: + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_tc(dummy); + } + break; + case 2: /* density */ + case 3: + { + copy_token(token, &next_char); + if (sscanf(token.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expecting numeric value for density.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + temp_solution.Set_density(dummy); + } + int j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + isoln_ptr->Set_calc_density(true); + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == CParser::TT_EMPTY) + break; + { + if (check_units(token, false, false, "mMol/kgw", false) == CParser::PARSER_OK) + { + isoln_ptr->Set_units(token); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == CParser::TT_EMPTY) + break; + if (parser.parse_couple(token) == CParser::PARSER_OK) + { + const char * str = string_hsave(token.c_str()); + //isoln_ptr->Set_default_pe(token); + isoln_ptr->Set_default_pe(str); + CReaction temp_chem_reaction; + isoln_ptr->Get_pe_reactions()[token] = temp_chem_reaction; + } + else + { + input_error++; + } + break; + case 6: /* ph */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + + temp_solution.Set_ph(temp_comp.Get_input_conc()); + + if (temp_comp.Get_equation_name().size() == 0) + { + break; + + } + temp_comp.Set_description("H(1)"); + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 7: /* pe */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + temp_solution.Set_pe(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + } + temp_comp.Set_description("E"); + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 9: /* isotope */ + { + cxxSolutionIsotope temp_isotope; + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "In read_solution\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + temp_isotope.Set_isotope_name(token.c_str()); + /* read and save element name */ + { + std::string temp_iso_name = token.c_str(); + const char* cptr1 = temp_iso_name.c_str(); + get_num(&cptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (cptr1[0] == '\0' || isupper((int) cptr1[0]) == FALSE) + { + error_msg("Expecting element name.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + return (CParser::PARSER_ERROR); + } + temp_isotope.Set_elt_name(cptr1); + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + temp_isotope.Set_ratio_uncertainty(NAN); + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != CParser::TT_EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + temp_isotope.Set_ratio_uncertainty_defined(true); + } + temp_solution.Get_isotopes()[temp_isotope.Get_isotope_name()] = temp_isotope; + } + break; + case 10: /* water */ + { + int j = copy_token(token, &next_char); + if (j == EMPTY) + { + temp_solution.Set_mass_water(1.0); + } + else if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_mass_water(dummy); + } + } + break; + case 11: /* pressure */ + case 12: + { + if (sscanf(next_char, SCANFORMAT, &dummy) != 1) + { + temp_solution.Set_patm(1); + } + else + { + temp_solution.Set_patm(dummy); + } + } + break; + case 13: /* potential, Volt */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) != 1) + { + temp_solution.Set_potV(0); + } + else + { + temp_solution.Set_potV(dummy); + } + } + break; + case OPTION_DEFAULT: +/* + * Read concentration + */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + if (temp_comp.Get_pe_reaction().size() > 0) + { + CReaction temp_chem_reaction; + isoln_ptr->Get_pe_reactions()[temp_comp.Get_pe_reaction()] = temp_chem_reaction; + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * fix up default units and default pe + */ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = isoln_ptr->Get_comps().begin(); it != isoln_ptr->Get_comps().end(); it++) + { + token = it->first; + Utilities::str_tolower(token); + if (it->second.Get_units().size() == 0) + { + it->second.Set_units(isoln_ptr->Get_units().c_str()); + } + else + { + bool alk = false; + if (strstr(token.c_str(), "alk") == token.c_str()) + alk = true; + std::string token1 = it->second.Get_units(); + if (check_units(token1, alk, true, isoln_ptr->Get_units().c_str(), true) == CParser::PARSER_ERROR) + { + input_error++; + } + else + { + it->second.Set_units(token1.c_str()); + } + } + if (it->second.Get_pe_reaction().size() == 0) + { + it->second.Set_pe_reaction(isoln_ptr->Get_default_pe()); + } + } + Rxn_solution_map[n_user] = temp_solution; + Rxn_new_solution.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for aqueous species, parse equations + */ + int i; + int association; + class species *s_ptr; + const class elt_list *next_elt; + const char* cptr; + char token[MAX_LENGTH]; + //bool vm_read = false; + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "gamma", /* 2 */ + "mb", /* 3 */ + "mass_balance", /* 4 */ + "log_k", /* 5 */ + "logk", /* 6 */ + "delta_h", /* 7 */ + "deltah", /* 8 */ + "analytical_expression", /* 9 */ + "a_e", /* 10 */ + "ae", /* 11 */ + "mole_balance", /* 12 */ + "llnl_gamma", /* 13 */ + "co2_llnl_gamma", /* 14 */ + "activity_water", /* 15 */ + "add_logk", /* 16 */ + "add_log_k", /* 17 */ + "add_constant", /* 18 */ + "dw", /* 19 */ + "erm_ddl", /* 20 */ +/* VP: Density Start */ + "millero", /* 21 */ +/* VP: Density End */ + "vm", /* 22, parms for molar volume, a1..a4 and w_ref, I terms */ + "viscosity" /* 23, b and d parms for viscosity, (b1 + b2 * exp(-b3 * tc)) * c + (d1 * exp(-d2 * tc)) * c ^ d3 */ + }; + int count_opt_list = 24; + association = TRUE; + s_ptr = NULL; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + //vm_read = false; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* gamma data */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 2; /* Wateq D-H */ + i = sscanf(next_char, SCANFORMAT SCANFORMAT, &s_ptr->dha, + &s_ptr->dhb); + if (i < 2) + { + error_string = sformatf( "Expecting 2 activity-" + "coefficient parameters, a and b."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 3: /* mb */ + case 4: /* mass_balance */ + case 12: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + cptr = token; + s_ptr->next_secondary.clear(); + get_secondary_in_species(&cptr, 1.0); + s_ptr->next_secondary = elt_list_vsave(); +/* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 5: /* log_k */ + case 6: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 7: /* delta_h */ + case 8: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 9: /* analytical_expression */ + case 10: /* a_e */ + case 11: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 13: /* llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 7; /* llnl D-H */ + i = sscanf(next_char, SCANFORMAT, &s_ptr->dha); + if (i < 1) + { + error_string = sformatf( + "Expecting activity-coefficient parameter, a."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 14: /* co2_llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 8; /* llnl CO2 D-H */ + opt_save = OPTION_DEFAULT; + break; + case 15: /* activity water */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 9; /* activity_water/55.5 for HDO, D2O, H2[O18], etc */ + opt_save = OPTION_DEFAULT; + break; + case 16: /* add_logk */ + case 17: /* add_log_k */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[count_add_logk].coef = 1; + } + opt_save = OPTION_DEFAULT; + } + break; + case 18: /* add_constant */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + opt_save = OPTION_DEFAULT; + } + break; + case 19: /* tracer diffusion coefficient */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->dw_t = 0; s_ptr->dw_a = 0; s_ptr->dw_a2 = 0; s_ptr->dw_a_visc = 0; + i = sscanf(next_char, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, &s_ptr->dw, &s_ptr->dw_t, + &s_ptr->dw_a, &s_ptr->dw_a2, &s_ptr->dw_a_visc); + s_ptr->dw_corr = s_ptr->dw; + opt_save = OPTION_DEFAULT; + break; + case 20: /* enrichment factor in the DDL */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + i = sscanf(next_char, SCANFORMAT, &s_ptr->erm_ddl); + if (s_ptr->erm_ddl < 0) + { + error_string = sformatf( "Expecting enrichment factor > 0, " + "resetting to erm_ddl = 1."); + warning_msg(error_string); + s_ptr->erm_ddl = 1.0; + } + opt_save = OPTION_DEFAULT; + break; +/* VP: Density Start */ + case 21: /* Millero a, b, c, d, e and f coefficients */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + print_density = read_millero_abcdef (next_char, &(s_ptr->millero[0])); + //if (!vm_read) + //{ + ///* copy millero parms into logk, for calculating pressure dependency... */ + // for (i = 0; i < 7; i++) + // s_ptr->logk[vm0 + i] = s_ptr->millero[i]; + // s_ptr->logk[vm0 + i] = 0; + //} + opt_save = OPTION_DEFAULT; + break; +/* VP: Density End */ + case 22: /* vm, supcrt parms + Ionic strength terms */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_aq_species_vm_parms(next_char, &s_ptr->logk[vma1]); + //vm_read = true; + print_density = OK; + opt_save = OPTION_DEFAULT; + break; + case 23: /* viscosity parms for the Jones-Dole eqn */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_viscosity_parms(next_char, &s_ptr->Jones_Dole[0]); + print_viscosity = OK; + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: + { + /* + * Get space for species information and parse equation + */ + s_ptr = NULL; + std::vector new_elt_list; + if (parse_eq(line, new_elt_list, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + /* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = + s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = + s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } + /* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = new_elt_list; + trxn.token[0].s->next_secondary.clear(); + next_elt = &trxn.token[0].s->next_elt[0]; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } + /* + * Copy reaction to reaction for species + */ + trxn_copy(trxn.token[0].s->rxn); + s_ptr = trxn.token[0].s; + /* + * Default gamma data + */ + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + if (equal(s_ptr->z, 0.0, TOL) == TRUE) + { + s_ptr->gflag = 0; /* Uncharged */ + s_ptr->dhb = 0.1; + } + else + { + s_ptr->gflag = 1; /* Davies */ + } + /* + * Set type for species + */ + if (strcmp(trxn.token[0].s->name, "H+") == 0) + { + s_hplus = trxn.token[0].s; + s_hplus->type = HPLUS; + } + else if (strcmp(trxn.token[0].s->name, "H3O+") == 0) + { + s_h3oplus = trxn.token[0].s; + s_h3oplus->type = HPLUS; + } + else if (strcmp(trxn.token[0].s->name, "e-") == 0) + { + s_eminus = trxn.token[0].s; + s_eminus->type = EMINUS; + s_eminus->gflag = 3; /* Always 1 */ + } + else if (strcmp(trxn.token[0].s->name, "H2O") == 0) + { + s_h2o = trxn.token[0].s; + s_h2o->type = H2O; + s_h2o->gflag = 3; /* Always 1 */ + } + else if (strstr(trxn.token[0].s->name, "(s)") != NULL) + { + trxn.token[0].s->type = SOLID; + } + else if (strcmp(trxn.token[0].s->name, "H2") == 0) + { + trxn.token[0].s->type = AQ; + s_h2 = trxn.token[0].s; + } + else if (strcmp(trxn.token[0].s->name, "O2") == 0) + { + trxn.token[0].s->type = AQ; + s_o2 = trxn.token[0].s; + } + else + { + trxn.token[0].s->type = AQ; + } + opt_save = OPTION_DEFAULT; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_use(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, mix, irreversible reaction, and pure phases to use + * in reaction calculation + */ + int i, l, n_user, return_value; + const char* cptr; + char token[MAX_LENGTH], token1[MAX_LENGTH];; +/* + * Read "use" + */ + cptr = line; + copy_token(token, &cptr, &l); +/* + * Read keyword + */ + copy_token(token, &cptr, &l); + check_key(token); + if (next_keyword != Keywords::KEY_SOLUTION && + next_keyword != Keywords::KEY_MIX && + next_keyword != Keywords::KEY_KINETICS && + next_keyword != Keywords::KEY_REACTION && + next_keyword != Keywords::KEY_REACTION_TEMPERATURE && + next_keyword != Keywords::KEY_REACTION_PRESSURE && + next_keyword != Keywords::KEY_EQUILIBRIUM_PHASES && + next_keyword != Keywords::KEY_EXCHANGE && + next_keyword != Keywords::KEY_SURFACE && + next_keyword != Keywords::KEY_GAS_PHASE && + next_keyword != Keywords::KEY_SOLID_SOLUTIONS) + { + input_error++; + error_msg("Unknown item in USE keyword", CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } +/* + * Read number + */ + strcpy(token1, token); + for (;;) + { + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, "%d", &n_user); + if (n_user < 0) + { + error_msg("Number must be a positive integer.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + if (strstr(token, "-") != NULL) + { + error_string = sformatf( + "USE does not accept a range of numbers, %s.", token); + warning_msg(error_string); + error_string = sformatf( + "Only %s %d will be used in the batch-reaction calculation.", + token1, n_user); + warning_msg(error_string); + error_string = sformatf( + "NOTE--USE is not needed for ADVECTION and TRANSPORT calculations."); + warning_msg(error_string); + + } + break; + } + else if (i == EMPTY) + { + error_string = sformatf( "No number given, 1 assumed."); + warning_msg(error_string); + n_user = 1; + break; + } + else if (token[0] == 'N' || token[0] == 'n') + { + n_user = -2; + break; + } + } + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + use.Set_n_solution_user(n_user); + if (n_user >= 0) + { + use.Set_solution_in(true); + } + else + { + use.Set_solution_in(false); + } + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + use.Set_n_pp_assemblage_user(n_user); + if (n_user >= 0) + { + use.Set_pp_assemblage_in(true); + } + else + { + use.Set_pp_assemblage_in(false); + } + break; + case Keywords::KEY_REACTION: /* Reaction */ + use.Set_n_reaction_user(n_user); + if (n_user >= 0) + { + use.Set_reaction_in(true); + } + else + { + use.Set_reaction_in(false); + } + break; + case Keywords::KEY_MIX: /* Mix */ + use.Set_n_mix_user(n_user); + if (n_user >= 0) + { + use.Set_mix_in(true); + } + else + { + use.Set_mix_in(false); + } + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + use.Set_n_exchange_user(n_user); + if (n_user >= 0) + { + use.Set_exchange_in(true); + } + else + { + use.Set_exchange_in(false); + } + break; + case Keywords::KEY_SURFACE: /* Surface */ + use.Set_n_surface_user(n_user); + if (n_user >= 0) + { + use.Set_surface_in(true); + } + else + { + use.Set_surface_in(false); + } + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + use.Set_n_temperature_user(n_user); + if (n_user >= 0) + { + use.Set_temperature_in(true); + } + else + { + use.Set_temperature_in(false); + } + break; + case Keywords::KEY_REACTION_PRESSURE: /* pressure */ + use.Set_n_pressure_user(n_user); + if (n_user >= 0) + { + use.Set_pressure_in(true); + } + else + { + use.Set_pressure_in(false); + } + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + use.Set_n_gas_phase_user(n_user); + if (n_user >= 0) + { + use.Set_gas_phase_in(true); + } + else + { + use.Set_gas_phase_in(false); + } + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + use.Set_n_kinetics_user(n_user); + if (n_user >= 0) + { + use.Set_kinetics_in(true); + } + else + { + use.Set_kinetics_in(false); + } + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + use.Set_n_ss_assemblage_user(n_user); + if (n_user >= 0) + { + use.Set_ss_assemblage_in(true); + } + else + { + use.Set_ss_assemblage_in(false); + } + break; + default: + input_error++; + error_msg(line_save, CONTINUE); + error_msg("Error in switch for USE.", STOP); + break; + } + return_value = check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface_species(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Read data for surface species, parse equations + */ + int i, j; + int association; + char token[MAX_LENGTH]; + const char* cptr; + LDBLE offset; + + class species *s_ptr; + const class elt_list *next_elt; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "mb", /* 2 */ + "mass_balance", /* 3 */ + "log_k", /* 4 */ + "logk", /* 5 */ + "delta_h", /* 6 */ + "deltah", /* 7 */ + "analytical_expression", /* 8 */ + "a_e", /* 9 */ + "ae", /* 10 */ + "mole_balance", /* 11 */ + "offset", /* 12 */ + "add_logk", /* 13 */ + "add_log_k", /* 14 */ + "add_constant", /* 15 */ + "cd_music", /* 16 */ + "music", /* 17 */ + "vm" /* 18 */ + }; + int count_opt_list = 19; + association = TRUE; + /* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + s_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* mb */ + case 3: /* mass_balance */ + case 11: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + cptr = token; + s_ptr->next_secondary.clear(); + get_secondary_in_species(&cptr, 1.0); + s_ptr->next_secondary = elt_list_vsave(); + /* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 4: /* log_k */ + case 5: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 6: /* delta_h */ + case 7: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 8: /* analytical_expression */ + case 9: /* a_e */ + case 10: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 12: /* offset */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (sscanf(next_char, SCANFORMAT, &offset) != 1) + { + error_msg("No offset for log K given", STOP); + } + s_ptr->logk[0] += offset; + opt_save = OPTION_DEFAULT; + break; + case 13: /* add_logk */ + case 14: /* add_log_k */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[count_add_logk].coef = 1; + } + opt_save = OPTION_DEFAULT; + } + break; + case 15: /* add_constant */ + { + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = s_ptr->add_logk.size(); + s_ptr->add_logk.resize(count_add_logk + 1); + i = sscanf(next_char, SCANFORMAT, &s_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[count_add_logk].name = + string_hsave("XconstantX"); + opt_save = OPTION_DEFAULT; + } + break; + case 16: /* cd_music */ + case 17: /* music */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + for (j = 0; j < 5; j++) + { + if (copy_token(token, &next_char, &i) == EMPTY) + break; + if (sscanf(token, SCANFORMAT, &s_ptr->cd_music[j]) != 1) + break; + } + s_ptr->dz[0] = s_ptr->cd_music[0] + s_ptr->cd_music[3] * s_ptr->cd_music[4]; + s_ptr->dz[1] = s_ptr->cd_music[1] + (1 - s_ptr->cd_music[3]) * s_ptr->cd_music[4]; + s_ptr->dz[2] = s_ptr->cd_music[2]; + for (j = 0; j < 3; j++) + { + s_ptr->rxn.dz[j] = s_ptr->dz[j]; + } + opt_save = OPTION_DEFAULT; + break; + case 18: /* vm, molar volume */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &s_ptr->logk[vm0], + &s_ptr->original_deltav_units); + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: + { + /* + * Get surface species information and parse equation + */ + s_ptr = NULL; + std::vector new_elt_list; + if (parse_eq(line, new_elt_list, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + /* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = + s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } + /* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = new_elt_list; + next_elt = &trxn.token[0].s->next_elt[0]; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } + /* + * Copy reaction to reaction for species + */ + trxn_copy(trxn.token[0].s->rxn); + /* + * Set type for species + */ + trxn.token[0].s->type = SURF; + s_ptr = trxn.token[0].s; + /* + * Read gamma data + */ + s_ptr->gflag = 6; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + opt_save = OPTION_DEFAULT; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads surface data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user; + LDBLE conc; + const char* cptr, *cptr1; + std::string token, token1, name; + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "equilibrate", /* 0 */ + "equil", /* 1 */ + "diff", /* 2 */ + "diffuse_layer", /* 3 */ + "no_edl", /* 4 */ + "no_electrostatic", /* 5 */ + "only_counter_ions", /* 6 */ + "donnan", /* 7 */ + "cd_music", /* 8 */ + "capacitances", /* 9 */ + "sites", /* 10 */ + "sites_units", /* 11 */ + "constant_capacitance", /* 12 */ + "ccm", /* 13 */ + "equilibrium", /* 14 */ + "site_units", /* 15 */ + "ddl" /* 16 */ + }; + int count_opt_list = 17; + /* + * kin_surf is for Surfaces, related to kinetically reacting minerals + * they are defined if "sites" is followed by mineral name: + * Surf_wOH Manganite [equilibrium_phases or kinetics] 0.25 4000 + * ^Name mineral ^switch ^prop.factor ^m2/mol + */ + cxxSurface temp_surface; + cptr = line; + temp_surface.read_number_description(cptr); + n_user = temp_surface.Get_n_user(); + cxxSurfaceComp *comp_ptr = NULL; + cxxSurfaceCharge *charge_ptr = NULL; + temp_surface.Set_new_def(true); + + if (use.Get_surface_in() == FALSE) + { + use.Set_surface_in(true); + use.Set_n_surface_user(n_user); + } + /* + * Read surface data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* equilibrate */ + case 1: /* equil */ + case 14: /* equilibrium */ + for (;;) + { + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + int j; + (void)sscanf(token.c_str(), "%d", &j); + temp_surface.Set_solution_equilibria(true); + temp_surface.Set_n_solution(j); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate surface.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case 2: /* diffuse_layer */ + case 3: + { + temp_surface.Set_thickness(1e-8); + temp_surface.Set_dl_type(cxxSurface::BORKOVEK_DL); + int j = sscanf(next_char, SCANFORMAT, &dummy); + if (j == 1) + { + temp_surface.Set_thickness(dummy); + } + } + break; + case 4: /* no electrostatic */ + case 5: + temp_surface.Set_type(cxxSurface::NO_EDL); + break; + case 6: + temp_surface.Set_only_counter_ions(get_true_false(next_char, TRUE) == TRUE); + break; + case 7: /* donnan for DL conc's */ + { + temp_surface.Set_dl_type(cxxSurface::DONNAN_DL); + LDBLE thickness = 0.0; + for (;;) + { + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_thickness(dummy); + thickness = 1; + continue; + } + else if (i != EMPTY) + { + if (token[0] == 'D' || token[0] == 'd') + { + if (thickness != 0) + { + error_msg + ("You must enter EITHER thickness OR Debye lengths (1/k),\n and relative DDL viscosity, DDL limit.\nCorrect is (for example): -donnan 1e-8 viscosity 0.5\n or (default values): -donnan debye_lengths 1 viscosity 1 limit 0.8", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + (void)sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_debye_lengths(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number of Debye lengths (1/k).", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else if (token[0] == 'V' || token[0] == 'v') + { + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + (void)sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_DDL_viscosity(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number for relative DDL viscosity.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else if (token[0] == 'L' || token[0] == 'l') + { + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + (void)sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_DDL_limit(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number for maximum of DDL water as fraction of bulk water.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else + { + error_msg + ("Expected diffuse layer thickness (m) or Debye lengths (1/k) for \n\tcalculating the thickness, and relative DDL viscosity and DDL limit.\nCorrect is (for example): -donnan 1e-8 visc 0.5\n or (default values): -donnan debye_lengths 1 visc 1 lim 0.8", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else + break; + } + } + break; + case 8: /* cd_music */ + temp_surface.Set_type(cxxSurface::CD_MUSIC); + break; + case 9: /* capacitances */ + /* + * Read capacitance for CD_MUSIC + */ + if (charge_ptr == NULL) + { + error_msg + ("Surface component has not been defined for capacitances.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + copy_token(token1, &next_char); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_capacitance0(dummy); + } + copy_token(token1, &next_char); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_capacitance1(dummy); + } + break; + case 10: /* sites */ + case 11: /* sites_units */ + case 15: /* site_units */ + { + int j = copy_token(token1, &next_char); + if (j != EMPTY && (token1[0] == 'A' || token1[0] == 'a')) + { + temp_surface.Set_sites_units(cxxSurface::SITES_ABSOLUTE); + } + else if (j != EMPTY && (token1[0] == 'D' || token1[0] == 'd')) + { + temp_surface.Set_sites_units(cxxSurface::SITES_DENSITY); + } + else + { + error_msg + ("Character string expected to be 'Absolute' or 'Density' to define the units of the first item in the definition of a surface component.\n", + CONTINUE); + input_error++; + break; + } + } + break; + // CCM not implemented yet + case 12: /* constant_capacitance */ + case 13: /* ccm */ + temp_surface.Set_type(cxxSurface::CCM); + copy_token(token1, &next_char); + if (charge_ptr == NULL) + { + error_msg("A surface must be defined before the capacitance is defined.\n", + CONTINUE); + input_error++; + break; + } + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expected capacitance for constant_capacitance model.\n", + CONTINUE); + input_error++; + break; + } + else + { + charge_ptr->Set_capacitance0(dummy); + } + + /* constant capacitance model not implemented yet */ + //error_msg("Constant capacitance model not implemented.", CONTINUE); + //input_error++; + break; + case 16: /* ddl */ + temp_surface.Set_type(cxxSurface::DDL); + break; + case OPTION_DEFAULT: + /* + * Read surface component + */ + { + cptr = line; + int i = copy_token(token, &cptr); + if (i != UPPER && token[0] != '[') + { + error_msg + ("Expected surface name to begin with a capital letter.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + + cxxSurfaceComp temp_comp(this->phrq_io); + + temp_surface.Get_surface_comps().push_back(temp_comp); + comp_ptr = &(temp_surface.Get_surface_comps().back()); + comp_ptr->Set_formula(token.c_str()); + + i = copy_token(token1, &cptr); + if (i == DIGIT) + { + /* + * Read surface concentration + */ + /* surface concentration is read directly */ + if (sscanf(token1.c_str(), SCANFORMAT, &conc) < 1) + { + error_msg("Expected number of surface sites in moles.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + comp_ptr->Set_moles(conc); + /* + * Read equilibrium phase name or kinetics rate name + */ + } + else if (i != EMPTY) + { + + /* surface conc. is related to mineral or kinetics */ + comp_ptr->Set_phase_name(token1.c_str()); + int j = copy_token(token1, &cptr); + + /* read optional 'equilibrium_phases' or 'kinetics' */ + if (j != DIGIT) + { + if (token1[0] == 'K' || token1[0] == 'k') + { + comp_ptr->Set_rate_name(comp_ptr->Get_phase_name().c_str()); + comp_ptr->Set_phase_name(NULL); + } + else if (token1[0] == 'E' || token1[0] == 'e') + { + comp_ptr->Set_rate_name(NULL); + } + else + { + error_msg + ("Character string expected to be 'equilibrium_phase' or 'kinetics' to relate surface to mineral or kinetic reaction.\n", + CONTINUE); + input_error++; + break; + } + j = copy_token(token1, &cptr); + } + + /* read proportion */ + if (j != DIGIT) + { + error_msg + ("Expected a coefficient to relate surface to mineral or kinetic reaction.\n", + CONTINUE); + input_error++; + break; + } + (void)sscanf(token1.c_str(), SCANFORMAT, &dummy); + comp_ptr->Set_phase_proportion(dummy); + /* real conc must be defined in tidy_model */ + conc = 1.0; + } + else + { + error_msg + ("Expected concentration of surface, mineral name, or kinetic reaction name.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + /* + * Accumulate elements in elt_list + */ + count_elts = 0; + paren_count = 0; + std::string formula = token.c_str(); + cptr1 = formula.c_str(); + get_elts_in_species(&cptr1, conc); + /* + * save formula for adjusting number of exchange sites + */ + cptr1 = formula.c_str(); + int l; + std::string name; + get_token(&cptr1, name, &dummy, &l); + comp_ptr->Set_formula_z(dummy); + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + /* + * Search for charge structure + */ + cptr1 = formula.c_str(); + get_elt(&cptr1, name, &l); + { + std::string::size_type pos = name.find('_'); + if (pos != std::string::npos) + { + name = name.substr(0, pos); + } + } + charge_ptr = temp_surface.Find_charge(name); + if (charge_ptr == NULL) + { + cxxSurfaceCharge temp_charge(this->phrq_io); + temp_charge.Set_name(name.c_str()); + if (comp_ptr->Get_phase_name().size() == 0 + && comp_ptr->Get_rate_name().size() == 0) + { + temp_charge.Set_specific_area(600.0); + temp_charge.Set_grams(0.0); + } + else + { + temp_charge.Set_specific_area(0.0); + temp_charge.Set_grams(1.0); + } + temp_surface.Get_surface_charges().push_back(temp_charge); + charge_ptr = temp_surface.Find_charge(name); + } + comp_ptr->Set_charge_name(name.c_str()); + /* + * Read surface area (m2/g) + */ + copy_token(token1, &cptr); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_specific_area(dummy); + } + else + { + break; + } + /* + * Read grams of solid (g) + */ + copy_token(token1, &cptr); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_grams(dummy); + } + /* read Dw */ + copy_token(token1, &cptr); + Utilities::str_tolower(token1); + if (strcmp(token1.c_str(), "dw") == 0) + { + int j = copy_token(token1, &cptr); + if (j != DIGIT) + { + error_msg + ("Expected surface diffusion coefficient (m2/s).\n", + CONTINUE); + input_error++; + break; + } + else + { + (void)sscanf(token1.c_str(), SCANFORMAT, &dummy); + comp_ptr->Set_Dw(dummy); + if (dummy > 0) + { + temp_surface.Set_transport(true); + if (temp_surface.Get_related_rate() + || temp_surface.Get_related_phases()) + { + error_msg + ("Can`t transport surfaces related to phases or rates (yet).", + CONTINUE); + input_error++; + } + } + break; + } + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // Sort comps and charge + temp_surface.Sort_comps(); + + /* + * copy Dw > 0 to all comps with the same charge structure, check edl & dif for surface transport + */ + if (temp_surface.Get_transport()) + { + if (temp_surface.Get_type() <= cxxSurface::NO_EDL) + { + input_error++; + error_msg + ("Must use default Dzombak and Morel or -cd_music for surface transport.", + CONTINUE); + } + if (temp_surface.Get_dl_type() <= cxxSurface::NO_DL) + { + input_error++; + error_msg + ("Must use -donnan or -diffuse_layer for surface transport.", + CONTINUE); + } + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + comp_ptr = &(temp_surface.Get_surface_comps()[i]); + if (comp_ptr->Get_Dw() > 0) + { + std::string name = comp_ptr->Get_charge_name(); + for (size_t j = 0; j < temp_surface.Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr1 = &(temp_surface.Get_surface_comps()[j]); + std::string name1 = comp_ptr1->Get_charge_name(); + if (name == name1) + { + comp_ptr1->Set_Dw(comp_ptr->Get_Dw()); + } + } + } + } + } + /* + * Make sure surface area is defined + */ + if (temp_surface.Get_type() == cxxSurface::DDL || temp_surface.Get_type() == cxxSurface::CCM || temp_surface.Get_type() == cxxSurface::CD_MUSIC) + { + for (size_t i = 0; i < temp_surface.Get_surface_charges().size(); i++) + { + charge_ptr = &(temp_surface.Get_surface_charges()[i]); + if (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() <= 0) + { + error_string = sformatf( "Surface area not defined for %s.\n", + charge_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } + /* + * Logical checks + */ + if (temp_surface.Get_type() == cxxSurface::UNKNOWN_DL) + { + error_string = sformatf( "Unknown surface type.\n"); + error_msg(error_string, CONTINUE); + input_error++; + } + else if (temp_surface.Get_type() == cxxSurface::NO_EDL) + { + if (temp_surface.Get_dl_type() != cxxSurface::NO_DL) + { + error_string = sformatf( + "No electrostatic term calculations do not allow calculation of the diffuse layer composition.\n"); + warning_msg(error_string); + temp_surface.Set_dl_type(cxxSurface::NO_DL); + } + } + else if (temp_surface.Get_type() == cxxSurface::DDL || temp_surface.Get_type() == cxxSurface::CCM) + { + /* all values of dl_type are valid */ + } + else if (temp_surface.Get_type() == cxxSurface::CD_MUSIC) + { + if (temp_surface.Get_dl_type() == cxxSurface::BORKOVEK_DL) + { + error_string = sformatf( + "Borkovec and Westall diffuse layer calculation is not allowed with a CD_MUSIC surface.\n\tUsing Donnan diffuse layer calculation."); + warning_msg(error_string); + temp_surface.Set_dl_type(cxxSurface::DONNAN_DL); + } + if (temp_surface.Get_debye_lengths() > 0) + { + error_msg + ("Variable DDL thickness is not permitted in CD_MUSIC. Fix DDL thickness\n\tfor example (default value): -donnan 1e-8", + CONTINUE); + input_error++; + } + } + temp_surface.Sort_comps(); + Rxn_surface_map[n_user] = temp_surface; + Rxn_new_surface.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface_master_species(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads master species data from data file or input file + */ + int l, return_value; + const char* cptr, *cptr1; + LDBLE l_z; + class species *s_ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + int opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "capacitance", /* 0 */ + "cd_music_capacitance" /* 1 */ + }; + int count_opt_list = 0; + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE_MASTER_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: + /* + * Get "element" name with valence, allocate space, store + */ + cptr = line; + /* + * Get element name and save pointer to character string + */ + if (copy_token(token, &cptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + replace("(+", "(", token); + /* + * Delete master if it exists + */ + master_delete(token); + /* + * Save values in master and species structure for surface sites + */ + size_t count_master = master.size(); + master.resize(count_master + 1); + master[count_master] = master_alloc(); + master[count_master]->type = SURF; + master[count_master]->elt = element_store(token); + if (copy_token(token, &cptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading surface master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + cptr1 = token; + std::string token1; + get_token(&cptr1, token1, &l_z, &l); + master[count_master]->s = s_store(token1.c_str(), l_z, FALSE); + } + master[count_master]->primary = TRUE; + strcpy(token, master[count_master]->elt->name); + count_master++; + /* + * Save values in master and species structure for surface psi + */ + strcpy(token1, token); + replace("_", " ", token1); + cptr1 = token1; + copy_token(token, &cptr1, &l); + strcat(token, "_psi"); + add_psi_master_species(token); + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_psi_master_species(char *token) +/* ---------------------------------------------------------------------- */ +{ + class species *s_ptr; + class master *master_ptr; + const char* cptr; + char token1[MAX_LENGTH]; + int i, n, plane; + + strcpy(token1, token); + for (plane = SURF_PSI; plane <= SURF_PSI2; plane++) + { + strcpy(token, token1); + switch (plane) + { + case SURF_PSI: + break; + case SURF_PSI1: + strcat(token, "b"); + break; + case SURF_PSI2: + strcat(token, "d"); + break; + } + master_ptr = master_search(token, &n); + if (master_ptr == NULL) + { + size_t count_master = master.size(); + master.resize(count_master + 1); + master[count_master] = master_alloc(); + master[count_master]->type = plane; + master[count_master]->elt = element_store(token); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + master[count_master]->s = s_store(token, 0.0, FALSE); + } + count_elts = 0; + paren_count = 0; + cptr = token; + get_elts_in_species(&cptr, 1.0); + master[count_master]->s->next_elt = elt_list_vsave(); + master[count_master]->s->type = plane; + master[count_master]->primary = TRUE; + + master[count_master]->s->rxn.token.resize(3); + /* + * Define reaction for psi + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + master[count_master]->s->rxn.logk[i] = 0.0; + } + master[count_master]->s->rxn.token[0].s = + master[count_master]->s; + master[count_master]->s->rxn.token[0].coef = -1.0; + master[count_master]->s->rxn.token[1].s = + master[count_master]->s; + master[count_master]->s->rxn.token[1].coef = 1.0; + master[count_master]->s->rxn.token[2].s = NULL; + count_master++; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_title(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads title for simulation + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + const char* cptr, *cptr1; + int l; + int return_value; + char token[MAX_LENGTH]; +/* + * Read anything after keyword + */ + cptr = line; + copy_token(token, &cptr, &l); + cptr1 = cptr; + title_x.clear(); + if (copy_token(token, &cptr, &l) != EMPTY) + { + title_x = cptr1; + } + +/* + * Read additonal lines + */ + for (;;) + { + return_value = check_line("title", TRUE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + break; +/* + * append line to title_x + */ + if (title_x.size() > 0) + { + title_x.append("\n"); + } + title_x.append(line); + } + last_title_x = title_x; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_advection(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + std::vector punch_temp, print_temp; + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "cells", /* 0 */ + "shifts", /* 1 */ + "print", /* 2 */ + "selected_output", /* 3 */ + "punch", /* 4 */ + "print_cells", /* 5 */ + "selected_cells", /* 6 */ + "time_step", /* 7 */ + "timest", /* 8 */ + "output", /* 9 */ + "output_frequency", /* 10 */ + "selected_output_frequency", /* 11 */ + "punch_frequency", /* 12 */ + "print_frequency", /* 13 */ + "punch_cells", /* 14 */ + "initial_time", /* 15 */ + "warning", /* 16 */ + "warnings" /* 17 */ + }; + int count_opt_list = 18; +/* + * Set use data + */ + use.Set_advect_in(true); + count_ad_cells = 0; + count_ad_shifts = 0; + print_ad_modulus = 1; + punch_ad_modulus = 1; +/* + * Read lines + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ADVECTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* cells */ + (void)sscanf(next_char, "%d", &count_ad_cells); + opt_save = OPTION_DEFAULT; + break; + case 1: /* shifts */ + (void)sscanf(next_char, "%d", &count_ad_shifts); + opt_save = OPTION_DEFAULT; + break; + case 2: /* print */ + case 5: /* print_cells */ + { + std::istringstream iss(next_char); + int idummy; + while (iss >> idummy) + { + print_temp.push_back(idummy); + } + opt_save = 2; + } + break; + case 3: /* selected_output */ + case 11: /* selected_output_frequency */ + case 12: /* punch_frequency */ + (void)sscanf(next_char, "%d", &punch_ad_modulus); + opt_save = OPTION_DEFAULT; + if (punch_ad_modulus <= 0) + { + error_string = sformatf( + "Punch frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + punch_ad_modulus = 1000; + } + break; + case 4: /* punch */ + case 14: /* punch_cells */ + case 6: /* selected_cells */ + { + std::istringstream iss(next_char); + int idummy; + while (iss >> idummy) + { + punch_temp.push_back(idummy); + } + opt_save = 4; + break; + } + case 7: /* time_step */ + case 8: /* timest */ + (void)sscanf(next_char, SCANFORMAT, &advection_kin_time); + { + std::string token; + int j = copy_token(token, &next_char); + j = copy_token(token, &next_char); + if (j == UPPER || j == LOWER) + { + advection_kin_time = Utilities::convert_time(advection_kin_time, token, "s"); + } + } + advection_kin_time_defined = TRUE; + opt_save = OPTION_DEFAULT; + break; + case 9: /* output */ + case 10: /* output_frequency */ + case 13: /* print_frequency */ + (void)sscanf(next_char, "%d", &print_ad_modulus); + opt_save = OPTION_DEFAULT; + if (print_ad_modulus <= 0) + { + error_string = sformatf( + "Print frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + print_ad_modulus = 1000; + } + break; + case 15: /* initial_time */ + char token[MAX_LENGTH]; + int j; + if (copy_token(token, &next_char, &j) == DIGIT) + (void)sscanf(token, SCANFORMAT, &initial_total_time); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + initial_total_time = Utilities::convert_time(initial_total_time, stdtoken, "s"); + } + } + opt_save = OPTION_DEFAULT; + break; + case 16: /* warning */ + case 17: /* warnings */ + advection_warnings = get_true_false(next_char, TRUE); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * Fill in data for punch + */ + advection_punch.resize(count_ad_cells + 1); + if (punch_temp.size() != 0) + { + for (size_t i = 0; i < count_ad_cells; i++) + advection_punch[i] = FALSE; + for (size_t i = 0; i < punch_temp.size(); i++) + { + if (punch_temp[i] > count_ad_cells || punch_temp[i] < 1) + { + error_string = sformatf( + "Cell number for punch is out of range, %d. Request ignored.", + punch_temp[i]); + warning_msg(error_string); + } + else + { + advection_punch[punch_temp[i] - 1] = TRUE; + } + } + } + else + { + for (size_t i = 0; i < count_ad_cells; i++) + advection_punch[i] = TRUE; + } + punch_temp.clear(); +/* + * Fill in data for print + */ + advection_print.resize(count_ad_cells + 1); + if (print_temp.size() != 0) + { + for (size_t i = 0; i < count_ad_cells; i++) + advection_print[i] = FALSE; + for (size_t i = 0; i < print_temp.size(); i++) + { + if (print_temp[i] > count_ad_cells || print_temp[i] < 1) + { + error_string = sformatf( + "Cell number for print is out of range, %d. Request ignored.", + print_temp[i]); + warning_msg(error_string); + } + else + { + advection_print[print_temp[i] - 1] = TRUE; + } + } + } + else + { + for (size_t i = 0; i < count_ad_cells; i++) + advection_print[i] = TRUE; + } + print_temp.clear(); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_debug(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads knobs and debugging info + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "iterations", /* 0 */ + "tolerance", /* 1 */ + "step_size", /* 2 */ + "pe_step_size", /* 3 */ + "scale_pure_phases", /* 4 */ + "diagonal_scale", /* 5 */ + "debug_model", /* 6 */ + "debug_prep", /* 7 */ + "debug_set", /* 8 */ + "debug_inverse", /* 9 */ + "logfile", /* 10 */ + "log_file", /* 11 */ + "debug_diffuse_layer", /* 12 */ + "delay_mass_water", /* 13 */ + "convergence_tolerance", /* 14 */ + "numerical_derivatives", /* 15 */ + "tries", /* 16 */ + "try", /* 17 */ + "numerical_fixed_volume", /* 18 */ + "force_numerical_fixed_volume", /* 19 */ + "equi_delay", /* 20 */ + "minimum_total", /* 21 */ + "min_total", /* 22 */ + "debug_mass_action", /* 23 */ + "debug_mass_balance" /* 24 */ + }; + int count_opt_list = 25; +/* + * Read parameters: + * ineq_tol; + * step_size; + * pe_step_size; + * pp_scale; + * diagonal_scale; + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in KNOBS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* iterations */ + (void)sscanf(next_char, "%d", &itmax); + break; + case 1: /* tolerance */ + (void)sscanf(next_char, SCANFORMAT, &ineq_tol); + break; + case 2: /* step_size */ + (void)sscanf(next_char, SCANFORMAT, &step_size); + break; + case 3: /* pe_step_size */ + (void)sscanf(next_char, SCANFORMAT, &pe_step_size); + break; + case 4: /* pp_scale */ + (void)sscanf(next_char, SCANFORMAT, &pp_scale); + break; + case 5: /* diagonal_scale */ + diagonal_scale = get_true_false(next_char, TRUE); + break; + case 6: /* debug_model */ + debug_model = get_true_false(next_char, TRUE); + break; + case 7: /* debug_prep */ + debug_prep = get_true_false(next_char, TRUE); + break; + case 8: /* debug_set */ + debug_set = get_true_false(next_char, TRUE); + break; + case 9: /* debug_inverse */ + debug_inverse = get_true_false(next_char, TRUE); + break; + case 10: /* logfile */ + case 11: /* log_file */ + pr.logfile = get_true_false(next_char, TRUE); + if (phast == TRUE) + { + pr.logfile = FALSE; + warning_msg("PHREEQC log file is disabled in PHAST"); + } + phrq_io->Set_log_on(pr.logfile == TRUE); + break; + case 12: /* debug_diffuse_layer */ + debug_diffuse_layer = get_true_false(next_char, TRUE); + break; + case 13: /* delay_mass_water */ + delay_mass_water = get_true_false(next_char, TRUE); + break; + case 14: /* convergence_tolerance */ + { + LDBLE ct; + (void)sscanf(next_char, SCANFORMAT, &ct); + convergence_tolerance = ct; + } + break; + case 15: /* numerical_derivatives */ + numerical_deriv = get_true_false(next_char, TRUE); + break; + case 16: /* tries */ + case 17: /* try */ + (void)sscanf(next_char, "%d", &max_tries); + break; + case 18: /* debug_inverse */ + numerical_fixed_volume = (get_true_false(next_char, TRUE) == TRUE); + break; + case 19: /* debug_inverse */ + force_numerical_fixed_volume = (get_true_false(next_char, TRUE) == TRUE); + break; + case 20: /* equi_delay */ + (void)sscanf(next_char, "%d", &equi_delay); + break; + case 21: /* minimum_total */ + case 22: /* min_total */ + (void)sscanf(next_char, SCANFORMAT, &MIN_TOTAL); + MIN_TOTAL_SS = MIN_TOTAL/100; + MIN_RELATED_SURFACE = MIN_TOTAL*100; + break; + case 23: /* debug_mass_action */ + debug_mass_action = get_true_false(next_char, TRUE); + break; + case 24: /* debug_mass_balance */ + debug_mass_balance = get_true_false(next_char, TRUE); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads printing info + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt, l; + const char* next_char; + char token[MAX_LENGTH]; + LDBLE num; + const char *opt_list[] = { + "reset", /* 0 */ + "gas_phase", /* 1 */ + "pure_phase", /* 2 */ + "surface", /* 3 */ + "exchange", /* 4 */ + "totals", /* 5 */ + "eh", /* 6 */ + "species", /* 7 */ + "saturation_indices", /* 8 */ + "si", /* 9 same a 8 */ + "reaction", /* 10 irrev, not used, pr.use */ + "mix", /* 11 */ + "use", /* 12 */ + "selected_output", /* 13 */ + "equilibrium_phases", /* 14 same as 2 */ + "equilibria", /* 15 same as 2 */ + "equilibrium", /* 16 same as 2 */ + "pure", /* 17 same as 2 */ + "other", /* 18 same as 12 */ + "status", /* 19 */ + "inverse", /* 20 */ + "kinetics", /* 21 */ + "dump", /* 22 */ + "user_print", /* 23 */ + "user_pr", /* 24 */ + "solid_solution", /* 25 */ + "solid_solutions", /* 26 */ + "inverse_modeling", /* 27 */ + "headings", /* 28 */ + "heading", /* 29 */ + "user_graph", /* 30 */ + "echo_input", /* 31 */ + "warning", /* 32 */ + "warnings", /* 33 */ + "initial_isotopes", /* 34 */ + "isotope_ratios", /* 35 */ + "isotope_alphas", /* 36 */ + "censor_species", /* 37 */ + "alkalinity", /* 38 */ + "equilibrium_phase", /* 39 */ + "high_precision" /* 40 */ + }; + + int count_opt_list = 41; + int value; +/* + * Read flags: + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PRINT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* reset */ + value = get_true_false(next_char, TRUE); + pr.kinetics = value; + pr.gas_phase = value; + pr.pp_assemblage = value; + pr.surface = value; + pr.exchange = value; + pr.totals = value; + pr.eh = value; + pr.species = value; + pr.saturation_indices = value; + pr.irrev = value; + pr.mix = value; + pr.reaction = value; + pr.use = value; + pr.inverse = value; + pr.user_print = value; + pr.ss_assemblage = value; + pr.headings = value; + pr.initial_isotopes = value; + pr.isotope_ratios = value; + pr.isotope_alphas = value; + pr.echo_input = value; + break; + case 1: /* gas_phase */ + pr.gas_phase = get_true_false(next_char, TRUE); + break; + case 2: /* pure_phase */ + case 14: /* equilibrium_phases */ + case 15: /* equilibria */ + case 16: /* equilibrium */ + case 17: /* pure */ + case 39: /* equilibrium_phase */ + pr.pp_assemblage = get_true_false(next_char, TRUE); + break; + case 3: /* surface */ + pr.surface = get_true_false(next_char, TRUE); + break; + case 4: /* exchange */ + pr.exchange = get_true_false(next_char, TRUE); + break; + case 5: /* totals */ + pr.totals = get_true_false(next_char, TRUE); + break; + case 6: /* eh */ + pr.eh = get_true_false(next_char, TRUE); + break; + case 7: /* species */ + pr.species = get_true_false(next_char, TRUE); + break; + case 8: + case 9: /* saturation_indices */ + pr.saturation_indices = get_true_false(next_char, TRUE); + break; + case 10: /* reaction, not used, pr.use controls */ + pr.irrev = get_true_false(next_char, TRUE); + break; + case 11: /* mix, not used, pr.use controls */ + pr.mix = get_true_false(next_char, TRUE); + break; + case 12: /* use */ + case 18: /* other */ + pr.use = get_true_false(next_char, TRUE); + break; + case 13: /* selected_output */ + pr.punch = get_true_false(next_char, TRUE); + phrq_io->Set_punch_on(pr.punch == TRUE); + break; + case 19: /* status */ + { + int j; + pr.status = get_true_false(next_char, TRUE); + j = copy_token(token, &next_char, &l); + if (j == UPPER || j == LOWER) + { + j = copy_token(token, &next_char, &l); + } + if (j == DIGIT) + { + const char * tptr = token; + get_num(&tptr, &num); + num = floor(num); + if (num < 0.0) num = 0.0; + //status_interval = (int) floor(num); + status_interval = (clock_t) num; + } + } + //if (status_interval < 0) + // status_interval = 0; + break; + case 20: /* inverse */ + case 27: /* inverse_modeling */ + pr.inverse = get_true_false(next_char, TRUE); + break; + case 21: /* kinetics */ + pr.kinetics = get_true_false(next_char, TRUE); + break; + case 22: /* dump */ + pr.dump = get_true_false(next_char, TRUE); + phrq_io->Set_dump_on(pr.dump == TRUE); + break; + case 23: /* user_print */ + case 24: /* user_pr */ + pr.user_print = get_true_false(next_char, TRUE); + break; + case 25: /* solid_solution */ + case 26: /* solid_solutions */ + pr.ss_assemblage = get_true_false(next_char, TRUE); + break; + case 28: /* headings */ + case 29: /* heading */ + pr.headings = get_true_false(next_char, TRUE); + break; + case 30: /* user_graph */ + pr.user_graph = get_true_false(next_char, TRUE); + break; + case 31: /* echo_input */ + pr.echo_input = get_true_false(next_char, TRUE); + if (pr.echo_input == 0) + phrq_io->Set_echo_on(false); + else + phrq_io->Set_echo_on(true); + break; + case 32: /* warning */ + case 33: /* warnings */ + (void)sscanf(next_char, "%d", &pr.warnings); + break; + case 34: /* initial_isotopes */ + pr.initial_isotopes = get_true_false(next_char, TRUE); + break; + case 35: /* isotope_ratios */ + pr.isotope_ratios = get_true_false(next_char, TRUE); + break; + case 36: /* isotope_alphas */ + pr.isotope_alphas = get_true_false(next_char, TRUE); + break; + case 37: /* censor_species */ + if (copy_token(token, &next_char, &l) != EMPTY) + { + (void)sscanf(token, SCANFORMAT, &censor); + } + else + { + censor = 0; + } + break; + case 38: /* alkalinity */ + pr.alkalinity = get_true_false(next_char, TRUE); + break; + case 40: /* high_precision */ + value = get_true_false(next_char, TRUE); + high_precision = (value != FALSE); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +/* + * Utilitity routines for read + */ +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_key(const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check if string begins with a keyword, returns TRUE or FALSE + * + * Arguments: + * *str is pointer to character string to be checked for keywords + * Returns: + * TRUE, + * FALSE. + */ + const char* cptr; + std::string stdtoken; + char * token1; + token1 = string_duplicate(str); + + cptr = token1; + int j = copy_token(stdtoken, &cptr); + Utilities::str_tolower(stdtoken); + std::string key(stdtoken); + + if (j == EMPTY) + { + next_keyword = Keywords::KEY_END; + } + else + { + next_keyword = Keywords::Keyword_search(key); + } + + free_check_null(token1); + if (next_keyword > 0) + { + return TRUE; + } + return (FALSE); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_units(std::string &tot_units, bool alkalinity, bool check_compatibility, + const char *default_units, bool print) +/* ---------------------------------------------------------------------- */ +{ +#define NUNITS (sizeof(units) / sizeof(char *)) +/* + * Check if legitimate units + * Input: + * tot_units character string to check, + * alkalinity TRUE if alkalinity, FALSE if any other total, + * check_compatibility TRUE check alk and default units, FALSE otherwise + * default_units character string of default units (check /L, /kg, etc) + * print TRUE print warning messages + * Output: + * tot_units standard form for unit + */ + const char *units[] = { + "Mol/l", /* 0 */ + "mMol/l", /* 1 */ + "uMol/l", /* 2 */ + "g/l", /* 3 */ + "mg/l", /* 4 */ + "ug/l", /* 5 */ + "Mol/kgs", /* 6 */ + "mMol/kgs", /* 7 */ + "uMol/kgs", /* 8 */ + "g/kgs", /* 9 = ppt */ + "mg/kgs", /* 10 = ppm */ + "ug/kgs", /* 11 = ppb */ + "Mol/kgw", /* 12 = mol/kg H2O */ + "mMol/kgw", /* 13 = mmol/kg H2O */ + "uMol/kgw", /* 14 = umol/kg H2O */ + "g/kgw", /* 15 = mol/kg H2O */ + "mg/kgw", /* 16 = mmol/kg H2O */ + "ug/kgw", /* 17 = umol/kg H2O */ + "eq/l", /* 18 */ + "meq/l", /* 19 */ + "ueq/l", /* 20 */ + "eq/kgs", /* 21 */ + "meq/kgs", /* 22 */ + "ueq/kgs", /* 23 */ + "eq/kgw", /* 24 */ + "meq/kgw", /* 25 */ + "ueq/kgw", /* 26 */ + }; + Utilities::squeeze_white(tot_units); + Utilities::str_tolower(tot_units); + replace("milli", "m", tot_units); + replace("micro", "u", tot_units); + replace("grams", "g", tot_units); + replace("gram", "g", tot_units); + replace("moles", "Mol", tot_units); + replace("mole", "Mol", tot_units); + replace("mol", "Mol", tot_units); + replace("liter", "l", tot_units); + replace("kgh", "kgw", tot_units); + replace("ppt", "g/kgs", tot_units); + replace("ppm", "mg/kgs", tot_units); + replace("ppb", "ug/kgs", tot_units); + replace("equivalents", "eq", tot_units); + replace("equivalent", "eq", tot_units); + replace("equiv", "eq", tot_units); + + size_t pos; + if ((pos = tot_units.find("/l")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 2); + } + else if ((pos = tot_units.find("/kgs")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 4); + } + else if ((pos = tot_units.find("/kgw")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 4); + } +/* + * Check if unit in list + */ + bool found = false; + size_t i; + for (i = 0; i < NUNITS; i++) + { + if (strcmp(tot_units.c_str(), units[i]) == 0) + { + found = true; + break; + } + } + if (!found) + { + if (print) + { + error_string = sformatf( "Unknown unit, %s.", tot_units.c_str()); + error_msg(error_string, CONTINUE); + } + return (ERROR); + } + +/* + * Check if units are compatible with default_units + */ + if (!check_compatibility) + return (OK); +/* + * Special cases for alkalinity + */ + if (alkalinity && strstr(tot_units.c_str(), "Mol") != NULL) + { + if (print == TRUE) + { + error_string = sformatf( + "Alkalinity given in moles, assumed to be equivalents."); + warning_msg(error_string); + } + replace("Mol", "eq", tot_units); + } + if (!alkalinity && strstr(tot_units.c_str(), "eq") != NULL) + { + if (print) + { + error_msg("Only alkalinity can be entered in equivalents.", + CONTINUE); + } + return (ERROR); + } +/* + * See if default_units are compatible with tot_units + */ + if (strstr(default_units, "/l") && strstr(tot_units.c_str(), "/l")) + return (OK); + if (strstr(default_units, "/kgs") && strstr(tot_units.c_str(), "/kgs")) + return (OK); + if (strstr(default_units, "/kgw") && strstr(tot_units.c_str(), "/kgw")) + return (OK); + + std::string string = default_units; + Utilities::replace("kgs", "kg solution", string); + Utilities::replace("kgs", "kg solution", tot_units); + Utilities::replace("kgw", "kg water", string); + Utilities::replace("kgw", "kg water", tot_units); + Utilities::replace("/l", "/L", string); + Utilities::replace("Mol", "mol", string); + Utilities::replace("/l", "/L", tot_units); + Utilities::replace("Mol", "mol", tot_units); + if (print == TRUE) + { + error_string = sformatf( + "Units for master species, %s, are not compatible with default units, %s.", + tot_units.c_str(), string.c_str()); + error_msg(error_string, CONTINUE); + } + return (ERROR); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +find_option(const char *item, int *n, const char **list, int count_list, int exact) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares a string value to match beginning letters of a list of options + * + * Arguments: + * item entry: pointer to string to compare + * n exit: item in list that was matched + * list entry: pointer to list of character values, assumed to + * be lower case + * count_list entry: number of character values in list + * + * Returns: + * OK item matched + * ERROR item not matched + * n -1 item not matched + * i position of match in list + */ + int i; + std::string stdtoken(item); + + Utilities::str_tolower(stdtoken); + for (i = 0; i < count_list; i++) + { + if (exact == TRUE) + { + if (strcmp(list[i], stdtoken.c_str()) == 0) + { + *n = i; + return (OK); + } + } + else + { + if (strstr(list[i], stdtoken.c_str()) == list[i]) + { + *n = i; + return (OK); + } + } + } + *n = -1; + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_true_false(const char *string, int default_value) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns true unless string starts with "F" or "f" + */ + int l; + char token[MAX_LENGTH]; + const char* cptr; + + cptr = string; + + if (copy_token(token, &cptr, &l) == EMPTY) + { + return (default_value); + } + else + { + if (token[0] == 'F' || token[0] == 'f') + { + return (FALSE); + } + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_option(const char **opt_list, int count_opt_list, const char **next_char) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line and check for options + */ + int j; + int opt; + const char *opt_ptr; + std::string stdoption; +/* + * Read line + */ + j = check_line("get_option", FALSE, TRUE, TRUE, FALSE); + if (j == EOF) + { + j = OPTION_EOF; + } + else if (j == KEYWORD) + { + j = OPTION_KEYWORD; + } + else if (j == OPTION) + { + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + if (find_option(&(stdoption.c_str()[1]), &opt, opt_list, count_opt_list, FALSE) == OK) + { + j = opt; + replace(stdoption.c_str(), opt_list[j], line_save); + replace(stdoption.c_str(), opt_list[j], line); + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + *next_char = opt_ptr; + if (pr.echo_input == TRUE) + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + } + } + else + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + error_msg("Unknown option.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + j = OPTION_ERROR; + *next_char = line; + } + } + else + { + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + if (find_option(&(stdoption.c_str()[0]), &opt, opt_list, count_opt_list, TRUE) == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + j = OPTION_DEFAULT; + *next_char = line; + } + if (pr.echo_input == TRUE) + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + } + } + return (j); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_rates(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + const char* cptr; + int l, n; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + class rate *rate_ptr; + const char* next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; + + n = -1; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + rate_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in RATES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPT_1; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read rate name */ + cptr = line; + copy_token(token, &cptr, &l); + { + const char *name = string_hsave(token); + rate_ptr = rate_search(name, &n); + } + if (rate_ptr == NULL) + { + size_t count_rates = rates.size(); + rates.resize(count_rates + 1); + rate_ptr = &rates[count_rates]; + } + else + { + rate_free(rate_ptr); + } + rate_ptr->new_def = TRUE; + rate_ptr->commands.clear(); + rate_ptr->name = string_hsave(token); + rate_ptr->linebase = NULL; + rate_ptr->varbase = NULL; + rate_ptr->loopbase = NULL; + opt_save = OPT_1; + break; + case OPT_1: /* read command */ + if (rate_ptr == NULL) + { + input_error++; + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + opt_save = OPT_1; + break; + } + rate_ptr->commands.append(";\0"); + rate_ptr->commands.append(line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", rates[0].commands)); + */ + rates_map.clear(); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_PRINT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read first command */ + rate_free(user_print); + user_print->new_def = TRUE; + user_print->commands.clear(); + user_print->linebase = NULL; + user_print->varbase = NULL; + user_print->loopbase = NULL; + user_print->name = + string_hsave("user defined Basic print routine"); + case OPT_1: /* read command */ + user_print->commands.append(";\0"); + user_print->commands.append(line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", rates[0].commands)); + */ return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_punch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt, opt_save; + std::string stdtoken; + const char* next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end", /* 1 */ + "heading", /* 2 */ + "headings" /* 3 */ + }; + int count_opt_list = 4; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + + int n_user; + + UserPunch temp_user_punch; + const char* cptr = line; + temp_user_punch.read_number_description(cptr); + n_user = temp_user_punch.Get_n_user(); + temp_user_punch.Set_PhreeqcPtr(this); + + //std::map < int, UserPunch >::iterator up = UserPunch_map.find(n_user); + //if (up != UserPunch_map.end()) + //{ + // UserPunch_map.erase(up); + //} + + // Malloc rate structure + class rate* r = new class rate; + r->commands.clear(); + r->new_def = TRUE; + r->linebase = NULL; + r->varbase = NULL; + r->loopbase = NULL; + r->name = string_hsave("user defined Basic punch routine"); + + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_PUNCH keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + while (copy_token(stdtoken, &next_char) != EMPTY) + { + temp_user_punch.Get_headings().push_back(stdtoken); + } + break; + case OPTION_DEFAULT: /* read first command */ + { + r->commands.clear(); + } + case OPT_1: /* read command */ + r->commands.append(";\0"); + r->commands.append(line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + UserPunch_map.erase(n_user); + UserPunch_map[n_user] = temp_user_punch; + UserPunch_map[n_user].Set_rate(r); + + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solid_solutions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solid solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user; + std::string token; + + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "component", /* 0 */ + "comp", /* 1 */ + "parms", /* 2 */ + "gugg_nondimensional", /* 3 */ + "gugg_kj", /* 4 */ + "activity_coefficients", /* 5 */ + "distribution_coefficients", /* 6 */ + "miscibility_gap", /* 7 */ + "spinodal_gap", /* 8 */ + "critical_point", /* 9 */ + "alyotropic_point", /* 10 */ + "temp", /* 11 */ + "tempk", /* 12 */ + "tempc", /* 13 */ + "thompson", /* 14 */ + "margules", /* 15 */ + "comp1", /* 16 */ + "comp2" /* 17 */ + }; + int count_opt_list = 18; +/* + * Read ss_assemblage number + */ + cxxSSassemblage temp_ss_assemblage; + + const char* cptr = line; + temp_ss_assemblage.read_number_description(cptr); + n_user = temp_ss_assemblage.Get_n_user(); + temp_ss_assemblage.Set_new_def(true); + + std::vector comps; + cxxSScomp * comp0_ptr = NULL; + cxxSScomp * comp1_ptr = NULL; + cxxSS * ss_ptr = NULL; +/* + * Set use data to first read + */ + if (!use.Get_ss_assemblage_in()) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(n_user); + } +/* + * Read solid solutions + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLID_SOLUTIONS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; +/* + * New component + */ + case 0: /* component */ + case 1: /* comp */ + { + cxxSScomp comp(this->phrq_io); + /* + * Read phase name of component + */ + cptr = next_char; + copy_token(token, &cptr); + comp.Set_name(token); + /* + * Read moles of component + */ + + if (copy_token(token, &cptr) == EMPTY) + { + comp.Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp.Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + comps.push_back(comp); + } + break; + case 2: /* parms */ + case 3: /* gugg_nondimensional */ + /* + * Read parameters + */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[0] = dummy; + } + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[1] = dummy; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_A0_A1); + break; + case 4: /* gugg_kj */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[0] = dummy; + } + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[1] = dummy; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_DIM_GUGG); + break; + case 5: /* activity coefficients */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 4; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 4) + { + error_string = sformatf( + "Expected 4 parameters to calculate a0 and a1 from two activity coefficients, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_GAMMAS); + break; + case 6: /* distribution coefficients */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 4; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 4) + { + error_string = sformatf( + "Expected 4 parameters to calculate a0 and a1 from two distribution coefficients, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_DIST_COEF); + break; + case 7: /* miscibility_gap */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected 2 miscibility gap fractions of component 2 to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_MISCIBILITY); + break; + case 8: /* spinodal_gap */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected 2 spinodal gap fractions of component 2 to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_SPINODAL); + break; + case 9: /* critical point */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected fraction of component 2 and critical temperature to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_CRITICAL); + break; + case 10: /* alyotropic point */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected fraction of component 2 and sigma pi at alyotropic point to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_ALYOTROPIC); + break; + case 12: /* tempk */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + { + cptr = next_char; + int j = 0; + if (copy_token(token, &cptr) != EMPTY) + { + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Set_tk(dummy); + } + if (j != 1) + { + error_string = sformatf( + "Expected temperature (Kelvin) for parameters, assemblage %d, solid solution %s, using 298.15 K", + n_user, + ss_ptr->Get_name().c_str()); + warning_msg(error_string); + ss_ptr->Set_tk(298.15); + } + } + break; + case 11: /* temp */ + case 13: /* tempc */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + { + cptr = next_char; + int j = 0; + if (copy_token(token, &cptr) != EMPTY) + { + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Set_tk(dummy + 298.15); + } + if (j != 1) + { + error_string = sformatf( + "Expected temperature (Celcius) for parameters, assemblage %d, solid solution %s, using 25 C", + n_user, + ss_ptr->Get_name().c_str()); + warning_msg(error_string); + ss_ptr->Set_tk(298.15); + } + } + break; + case 14: /* Thompson and Waldbaum */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected Wg2 and Wg1 Thompson-Waldbaum parameters to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_WALDBAUM); + break; + case 15: /* Margules */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + cptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &cptr) != EMPTY) + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected alpha2 and alpha3 Margules parameters to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_MARGULES); + break; + case 16: /* comp1 */ + + /* + * Read phase name of component + */ + delete comp0_ptr; + comp0_ptr = new cxxSScomp; + cptr = next_char; + copy_token(token, &cptr); + comp0_ptr->Set_name(token); + /* + * Read moles of component + */ + if (copy_token(token, &cptr) == EMPTY) + { + comp0_ptr->Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp0_ptr->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + break; + case 17: /* comp2 */ + delete comp1_ptr; + comp1_ptr = new cxxSScomp; + /* + * Read phase name of component + */ + cptr = next_char; + copy_token(token, &cptr); + comp1_ptr->Set_name(token); + /* + * Read moles of component + */ + if (copy_token(token, &cptr) == EMPTY) + { + comp1_ptr->Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp1_ptr->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + break; +/* + * New solid solution + */ + case OPTION_DEFAULT: + if(ss_ptr) + { + if (comp1_ptr) + { + comps.insert(comps.begin(), *comp1_ptr); + } + if (comp0_ptr) + { + comps.insert(comps.begin(), *comp0_ptr); + } + ss_ptr->Set_ss_comps(comps); + temp_ss_assemblage.Get_SSs()[ss_ptr->Get_name()] = *ss_ptr; + delete ss_ptr; + ss_ptr = NULL; + comps.clear(); + delete comp0_ptr; + delete comp1_ptr; + comp0_ptr = comp1_ptr = NULL; + } + ss_ptr = new cxxSS; + /* + * Read solid solution name + */ + cptr = line; + copy_token(token, &cptr); + ss_ptr->Set_name(token); + ss_ptr->Set_total_moles(0.0); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // add last ss and clean up + if (comp1_ptr) + comps.insert(comps.begin(), *comp1_ptr); + if (comp0_ptr) + comps.insert(comps.begin(), *comp0_ptr); + if (ss_ptr != NULL && comps.size() > 0) + { + ss_ptr->Set_ss_comps(comps); + } + if (ss_ptr != NULL && ss_ptr->Get_name().size() > 0) + { + temp_ss_assemblage.Get_SSs()[ss_ptr->Get_name()] = *ss_ptr; + } + delete ss_ptr; + ss_ptr = NULL; + comps.clear(); + delete comp0_ptr; + delete comp1_ptr; + comp0_ptr = comp1_ptr = NULL; + + // check non ideal ss + std::vector ss_v; + for (size_t i = 0; i < ss_v.size(); i++) + { + if (ss_v[i]->Get_p()[0] != 0.0 || + ss_v[i]->Get_p()[1] != 0.0) + { + if (ss_v[i]->Get_ss_comps().size() != 2) + { + error_string = sformatf( + "Solid solution, %s, is nonideal. Must define exactly two components (-comp1 and -comp2).", + ss_v[i]->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } + + // Add to map + Rxn_ss_assemblage_map[n_user] = temp_ss_assemblage; + Rxn_new_ss_assemblage.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_llnl_aqueous_model_parameters(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads aqueous model parameters + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt; + const char* next_char; + const char* opt_list[] = { + "temperatures", /* 0 */ + "temperature", /* 1 */ + "temp", /* 2 */ + "adh", /* 3 */ + "debye_huckel_a", /* 4 */ + "dh_a", /* 5 */ + "bdh", /* 6 */ + "debye_huckel_b", /* 7 */ + "dh_b", /* 8 */ + "bdot", /* 9 */ + "b_dot", /* 10 */ + "c_co2", /* 11 */ + "co2_coefs" /* 12 */ + }; + int count_opt_list = 13; + /* + * Initialize + */ + /* + * Read aqueous model parameters + */ + return_value = UNKNOWN; + int opt_save = OPTION_DEFAULT; + opt_save = OPTION_DEFAULT; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg + ("Unknown input in LLNL_AQUEOUS_MODEL_PARAMETERS keyword.", + CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* temperatures */ + case 1: /* temperature */ + case 2: /* temp */ + { + std::istringstream iss(next_char); + while (iss >> dummy) + { + llnl_temp.push_back(dummy); + } + opt_save = 2; + } + break; + case 3: /* adh */ + case 4: /* debye_huckel_a */ + case 5: /* dh_a */ + { + std::istringstream iss(next_char); + while (iss >> dummy) + { + llnl_adh.push_back(dummy); + } + opt_save = 5; + } + break; + case 6: /* bdh */ + case 7: /* debye_huckel_b */ + case 8: /* dh_b */ + { + std::istringstream iss(next_char); + while (iss >> dummy) + { + llnl_bdh.push_back(dummy); + } + opt_save = 8; + } + break; + case 9: /* bdot */ + case 10: /* b_dot */ + { + std::istringstream iss(next_char); + while (iss >> dummy) + { + llnl_bdot.push_back(dummy); + } + opt_save = 10; + } + break; + case 11: /* c_co2 */ + case 12: /* co2_coefs */ + { + std::istringstream iss(next_char); + while (iss >> dummy) + { + llnl_co2_coefs.push_back(dummy); + } + opt_save = 12; + } + break; + } + return_value = check_line_return; + if (return_value == EOF || return_value == KEYWORD) + break; + } + /* check consistency */ + if ((llnl_temp.size() == 0) || + (llnl_temp.size() != llnl_adh.size()) || + (llnl_temp.size() != llnl_bdh.size()) || + (llnl_temp.size() != llnl_bdot.size())) + { + error_msg + ("Must define equal number (>0) of temperatures, dh_a, dh_b, and bdot parameters\nin LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + if (llnl_co2_coefs.size() != 5) + { + error_msg + ("Must define 5 CO2 activity coefficient parameters in LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + for (size_t i = 1; i < llnl_temp.size(); i++) + { + if (llnl_temp[i - 1] > llnl_temp[i]) + { + error_msg + ("Temperatures must be in ascending order in LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + } + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +next_keyword_or_option(const char **opt_list, int count_opt_list) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads to next keyword or option or eof + * + * Returns: + * KEYWORD + * OPTION + * EOF + */ + int opt; + const char* next_char; + + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_EOF) + { /* end of file */ + break; + } + else if (opt == OPTION_KEYWORD) + { /* keyword */ + break; + } + else if (opt >= 0 && opt < count_opt_list) + { + break; + } + else + { + error_msg("Expected a keyword or option.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + return (opt); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_named_logk(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads K's that can be used to calculate K's for species + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + + int l; + int i, empty; + class logk *logk_ptr; + char token[MAX_LENGTH]; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "log_k", /* 0 */ + "logk", /* 1 */ + "delta_h", /* 2 */ + "deltah", /* 3 */ + "analytical_expression", /* 4 */ + "a_e", /* 5 */ + "ae", /* 6 */ + "ln_alpha1000", /* 7 */ + "add_logk", /* 8 */ + "add_log_k", /* 9 */ + "vm" /* 10 */ + }; + int count_opt_list = 11; + logk_ptr = NULL; +/* + * Read name followed by options + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* log_k */ + case 1: /* logk */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &logk_ptr->log_k[0]); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 2: /* delta_h */ + case 3: /* deltah */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &logk_ptr->log_k[1], + &logk_ptr->original_units); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 4: /* analytical_expression */ + case 5: /* a_e */ + case 6: /* ae */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(logk_ptr->log_k[T_A1])); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 7: /* ln_alpha1000 */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + empty = TRUE; + for (i = T_A1; i <= T_A6; i++) + { + if (logk_ptr->log_k[i] != 0.0) + { + empty = FALSE; + logk_ptr->log_k[i] = 0.0; + } + } + if (empty == FALSE) + { + error_string = sformatf( + "Analytical expression previously defined for %s in NAMED_EXPRESSIONS\nAnalytical expression will be overwritten.", + logk_ptr->name); + warning_msg(error_string); + } + read_analytical_expression_only(next_char, &(logk_ptr->log_k[T_A1])); + for (i = T_A1; i < T_A6; i++) + { + logk_ptr->log_k[i] /= 1000. * LOG_10; + } + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 8: /* add_logk */ + case 9: /* add_log_k */ + { + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + size_t count_add_logk = logk_ptr->add_logk.size(); + logk_ptr->add_logk.resize(count_add_logk + 1); + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + logk_ptr->add_logk[count_add_logk].name = + string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &logk_ptr->add_logk[count_add_logk].coef); + if (i <= 0) + { + logk_ptr->add_logk[count_add_logk].coef = 1; + } + opt_save = OPTION_DEFAULT; + } + break; + case 10: /* vm, molar volume */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &logk_ptr->log_k[vm0], + &logk_ptr->original_deltav_units); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: +/* + * Get space for logk information + */ + logk_ptr = NULL; + copy_token(token, &next_char, &l); + + logk_ptr = logk_store(token, TRUE); +/* + * Get pointer to each species in the reaction, store new species if necessary + */ + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_copy(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, + * equilibrium_phases, + * exchange, + * surface, + * solid_solution, + * gas_phase, + * kinetics, + * mix, + * reaction, + * reaction_temperature + * + */ + int i, l, n, n_user, n_user_start, n_user_end, return_value; + const char* cptr; + char token[MAX_LENGTH], token1[MAX_LENGTH], nonkeyword[MAX_LENGTH]; +/* + * Read "copy" + */ + cptr = line; + copy_token(token, &cptr, &l); +/* + * Read keyword + */ + copy_token(token, &cptr, &l); + check_key(token); + + switch (next_keyword) + { + case Keywords::KEY_NONE: /* Have not read line with keyword */ + strcpy(nonkeyword, token); + break; + case Keywords::KEY_SOLUTION: /* Solution */ + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + case Keywords::KEY_REACTION: /* Reaction */ + case Keywords::KEY_MIX: /* Mix */ + case Keywords::KEY_EXCHANGE: /* Ex */ + case Keywords::KEY_SURFACE: /* Surface */ + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + case Keywords::KEY_GAS_PHASE: /* Gas */ + case Keywords::KEY_KINETICS: /* Kinetics */ + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + break; + default: + input_error++; + error_msg + ("Expecting keyword solution, mix, kinetics, reaction, reaction_pressure, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions, or cell.", + CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } +/* + * Read source index + */ + strcpy(token1, token); + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + (void)sscanf(token, "%d", &n_user); + //if (n_user < 0) + //{ + // error_msg("Source index number must be a positive integer.", + // CONTINUE); + // error_msg(line_save, CONTINUE); + // input_error++; + // return (ERROR); + //} + //if (strstr(token, "-") != NULL) + //{ + // error_msg + // ("COPY does not accept a range of numbers for source index", + // CONTINUE); + // error_msg(line_save, CONTINUE); + // input_error++; + // return (ERROR); + //} + } + else + { + error_msg("Source index number must be an integer.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Read target index or range of indices + */ + i = copy_token(token, &cptr, &l); + if (i == DIGIT) + { + replace("-", " ", &token[1]); + n = sscanf(token, "%d%d", &n_user_start, &n_user_end); + if (n == 1) + { + n_user_end = n_user_start; + } + } + else + { + error_msg("Target index number must be an integer.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + + switch (next_keyword) + { + case Keywords::KEY_NONE: + str_tolower(nonkeyword); + if (strstr(nonkeyword, "cell") != nonkeyword) + { + error_msg("Unknown input in COPY data block.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + copier_add(©_solution, n_user, n_user_start, n_user_end); + copier_add(©_pp_assemblage, n_user, n_user_start, n_user_end); + copier_add(©_reaction, n_user, n_user_start, n_user_end); + copier_add(©_mix, n_user, n_user_start, n_user_end); + copier_add(©_exchange, n_user, n_user_start, n_user_end); + copier_add(©_surface, n_user, n_user_start, n_user_end); + copier_add(©_temperature, n_user, n_user_start, n_user_end); + copier_add(©_pressure, n_user, n_user_start, n_user_end); + copier_add(©_gas_phase, n_user, n_user_start, n_user_end); + copier_add(©_kinetics, n_user, n_user_start, n_user_end); + copier_add(©_ss_assemblage, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SOLUTION: /* Solution */ + copier_add(©_solution, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + copier_add(©_pp_assemblage, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION: /* Reaction */ + copier_add(©_reaction, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_MIX: /* Mix */ + copier_add(©_mix, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + copier_add(©_exchange, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SURFACE: /* Surface */ + copier_add(©_surface, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + copier_add(©_temperature, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + copier_add(©_pressure, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + copier_add(©_gas_phase, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + copier_add(©_kinetics, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + copier_add(©_ss_assemblage, n_user, n_user_start, n_user_end); + break; + default: + error_msg("Error in switch for READ_COPY.", STOP); + break; + } + return_value = check_line("End of COPY", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_pressure(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_PRESSURE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + // Make instance, set n_user, n_user_end, description + cxxPressure atm(this->phrq_io); + const char* cptr = line; + + atm.read_number_description(cptr); + int n_user = atm.Get_n_user(); + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + atm.read(parser); + if (atm.Get_base_error_count() == 0) + { + Rxn_pressure_map[n_user] = atm; + } + + if (use.Get_pressure_in() == FALSE) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(atm.Get_n_user()); + } + + // Make copies if necessary + if (atm.Get_n_user_end() > n_user) + { + int i; + for (i = n_user + 1; i <= atm.Get_n_user_end(); i++) + { + Utilities::Rxn_copy(Rxn_pressure_map, n_user, i); + } + } + + return cleanup_after_parser(parser); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_pressure_raw(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_PRESSURE_RAW data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + cxxPressure atm(this->phrq_io); + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + atm.read_raw(parser); + + // Store + if (atm.Get_base_error_count() == 0) + { + Rxn_pressure_map[atm.Get_n_user()] = atm; + } + + // Make copies if necessary + + Utilities::Rxn_copies(Rxn_pressure_map, atm.Get_n_user(), atm.Get_n_user_end()); + + + return cleanup_after_parser(parser); +} +int Phreeqc:: +cleanup_after_parser(CParser &parser) +{ + // check_key sets next_keyword + if (parser.get_m_line_type() == PHRQ_io::LT_EOF) + { + strcpy(line, ""); + strcpy(line_save, ""); + next_keyword = Keywords::KEY_END; + return(TRUE); + } + int return_value = check_key(parser.line().c_str()); + + // copy parser line to line and line_save + // make sure there is enough space + size_t l1 = strlen(parser.line().c_str()) + 1; + size_t l2 = strlen(parser.line_save().c_str()) + 1; + size_t l = (l1 > l2) ? l1 : l2; + if (l >= (size_t) max_line) + { + max_line = (int) l * 2; + line_save = (char *) PHRQ_realloc(line_save, + (size_t) max_line * sizeof(char)); + if (line_save == NULL) + malloc_error(); + line = (char *) PHRQ_realloc(line, (size_t) max_line * sizeof(char)); + if (line == NULL) + malloc_error(); + } + strcpy(line, parser.line().c_str()); + strcpy(line_save, parser.line_save().c_str()); + return return_value; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_temperature(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_TEMPERATURE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + // Make instance, set n_user, n_user_end, description + cxxTemperature t_react(this->phrq_io); + const char* cptr = line; + t_react.read_number_description(cptr); + int n_user = t_react.Get_n_user(); + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + t_react.read(parser); + if (t_react.Get_base_error_count() == 0) + { + Rxn_temperature_map[n_user] = t_react; + } + + if (use.Get_temperature_in() == FALSE) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(t_react.Get_n_user()); + } + + // Make copies if necessary + if (t_react.Get_n_user_end() > n_user) + { + int i; + for (i = n_user + 1; i <= t_react.Get_n_user_end(); i++) + { + Utilities::Rxn_copy(Rxn_temperature_map, n_user, i); + } + } + + return cleanup_after_parser(parser); +} diff --git a/phreeqcpp/readtr.cpp b/phreeqcpp/readtr.cpp new file mode 100644 index 00000000..85d579ee --- /dev/null +++ b/phreeqcpp/readtr.cpp @@ -0,0 +1,1495 @@ +#include /* std::cout std::cerr */ +#include +#include +#include "Phreeqc.h" +#include "StorageBin.h" +#include "SS.h" +#ifndef boolean +typedef unsigned char boolean; +#endif +#include "phqalloc.h" +#include "Utils.h" + + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPTION_DEFAULT2 -5 + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_transport(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection and column information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int i, j, l; + int count_length, count_disp, count_punch, count_print, count_por, count_same_model; + int count_length_alloc, count_disp_alloc, count_por_alloc; + char token[MAX_LENGTH]; + LDBLE *length, *disp, *pors; + int *punch_temp, *print_temp, *same_model_temp; + int return_value, opt, opt_save; + const char* next_char; + //char file_name[MAX_LENGTH]; + std::string file_name("phreeqc.dmp"); + + const char *opt_list[] = { + "cells", /* 0 */ + "shifts", /* 1 */ + "print", /* 2 */ + "selected_output", /* 3 */ + "bcond", /* 4 */ + "timest", /* 5 */ + "diffc", /* 6 */ + "tempr", /* 7 */ + "length", /* 8 */ + "disp", /* 9 */ + "punch", /* 10 */ + "stagnant", /* 11 */ + "bc", /* 12 */ + "boundary_conditions", /* 13 */ + "time_step", /* 14 */ + "temp_retardation_factor", /* 15 */ + "diffusion_coefficient", /* 16 */ + "dispersivity", /* 17 */ + "direction", /* 18 */ + "temperature_retardation_factor", /* 19 */ + "print_cells", /* 20 */ + "selected_cells", /* 21 */ + "flow_direction", /* 22 */ + "flow", /* 23 */ + "lengths", /* 24 */ + "dispersivities", /* 25 */ + "dump", /* 26 */ + "output", /* 27 */ + "output_frequency", /* 28 */ + "selected_output_frequency", /* 29 */ + "punch_cells", /* 30 */ + "dump_frequency", /* 31 */ + "dump_restart", /* 32 */ + "punch_frequency", /* 33 */ + "print_frequency", /* 34 */ + "correct_disp", /* 35 */ + "initial_time", /* 36 */ + "warning", /* 37 */ + "warnings", /* 38 */ + "thermal_diffusion", /* 39 */ + "multi_d", /* 40 */ + "interlayer_d", /* 41 */ + "porosities", /* 42 */ + "porosity", /* 43 */ + "fix_current", /* 44 */ + "current", /* 45 */ + "implicit", /* 46 */ + "same_model" /* 47 */ + }; + int count_opt_list = 48; + + /* + * Initialize + */ + simul_tr++; + if (simul_tr == 1) + { + correct_disp = FALSE; + old_cells = 0; + max_cells = 0; + all_cells = 0; + } + else + old_cells = count_cells; + count_length = count_disp = count_punch = count_print = count_por = count_same_model = 0; + + length = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (length == NULL) + malloc_error(); + + disp = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (disp == NULL) + malloc_error(); + + pors = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (pors == NULL) + malloc_error(); + + punch_temp = (int *)PHRQ_malloc(sizeof(int)); + if (punch_temp == NULL) + malloc_error(); + + print_temp = (int *)PHRQ_malloc(sizeof(int)); + if (print_temp == NULL) + malloc_error(); + + same_model_temp = (int *)PHRQ_malloc(sizeof(int)); + if (same_model_temp == NULL) + malloc_error(); + + count_length_alloc = count_disp_alloc = count_por_alloc = 1; + transport_start = 1; + /* + * Set use data to last read + */ + use.Set_trans_in(true); + /* + * Read lines + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + opt = opt_save; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + case OPTION_DEFAULT: + input_error++; + error_msg("Unknown input in TRANSPORT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* cells */ + (void)sscanf(next_char, "%d", &count_cells); + opt_save = OPTION_DEFAULT; + break; + case 1: /* shifts */ + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, "%d", &count_shifts); + else + { + warning_msg + ("Expected the number of shifts. One shift is assumed."); + count_shifts = 1; + } + j = copy_token(token, &next_char, &l); + if (j != EMPTY) + { + if (j == DIGIT) + (void)sscanf(token, "%d", &ishift); + else + { + input_error++; + error_msg + ("Expected shift direction, -1, 0, 1. Use -direction instead.", + CONTINUE); + ishift = 1; + } + } + opt_save = OPTION_DEFAULT; + break; + case 2: /* print */ + case 20: /* print_cells */ + print_temp = + read_list_ints_range(&next_char, &count_print, FALSE, + print_temp); + opt_save = 2; + break; + case 3: /* selected_output */ + case 29: /* selected_output_frequency */ + case 33: /* punch_frequency */ + (void)sscanf(next_char, "%d", &punch_modulus); + opt_save = OPTION_DEFAULT; + if (punch_modulus <= 0) + { + error_string = sformatf( + "Punch frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + punch_modulus = 1000; + } + + break; + case 4: /* bcond */ + case 12: /* bc */ + case 13: /* boundary_conditions */ + /* first cell boundary condition */ + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + { + (void)sscanf(token, "%d", &bcon_first); + if (bcon_first < 1 || bcon_first > 3) + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant' (1), 'closed' (2) , or 'flux' (3).", + CONTINUE); + } + } + else if (i == EMPTY) + bcon_first = 3; + else if (strstr(token, "co") == token) + bcon_first = 1; + else if (strstr(token, "cl") == token) + bcon_first = 2; + else if (strstr(token, "f") == token) + bcon_first = 3; + else + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant', 'closed', or 'flux'.", + CONTINUE); + } + + /* last cell boundary condition */ + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + { + (void)sscanf(token, "%d", &bcon_last); + if (bcon_last < 1 || bcon_last > 3) + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant' (1), 'closed' (2) , or 'flux' (3).", + CONTINUE); + } + } + else if (i == EMPTY) + bcon_last = 3; + else if (strstr(token, "co") == token) + bcon_last = 1; + else if (strstr(token, "cl") == token) + bcon_last = 2; + else if (strstr(token, "f") == token) + bcon_last = 3; + else + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant', 'closed', or 'flux'.", + CONTINUE); + } + opt_save = OPTION_DEFAULT; + break; + case 5: /* timest */ + case 14: /* time_step */ + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, SCANFORMAT, ×t); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + timest = Utilities::convert_time(timest, stdtoken, "s"); + j = copy_token(stdtoken, &next_char); + } + if (j == DIGIT) + { + (void)sscanf(stdtoken.c_str(), SCANFORMAT, &mcd_substeps); + } + } + //if (copy_token(token, &next_char, &l) == DIGIT) + // sscanf(token, SCANFORMAT, &mcd_substeps); + if (mcd_substeps < 1) + { + mcd_substeps = 1.0; + warning_msg("Substep factor in MCD must be >= 1.0\n" + "mcd_substeps = 1.0 assumed."); + } + opt_save = OPTION_DEFAULT; + break; + case 6: /* diffc */ + case 16: /* diffusion_coefficient */ + (void)sscanf(next_char, SCANFORMAT, &diffc); + opt_save = OPTION_DEFAULT; + break; + case 7: /* tempr */ + case 15: /* temp_retardation_factor */ + case 19: /* temperature_retardation_factor */ + case 39: /* thermal_diffusion */ + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, SCANFORMAT, &tempr); + if (tempr < 1) + { + tempr = 1; + warning_msg + ("Temperature retardation factor < 1 is not possible.\n" + "Temperature retardation factor = 1 assumed."); + } + j = copy_token(token, &next_char, &l); + if (j == DIGIT) + (void)sscanf(token, SCANFORMAT, &heat_diffc); + opt_save = OPTION_DEFAULT; + break; + case 8: /* length */ + case 24: /* lengths */ + if (read_line_LDBLEs + (next_char, &length, &count_length, + &count_length_alloc) == ERROR) + { + input_error++; + error_msg("Reading lengths in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 8; + break; + case 9: /* disp */ + case 17: /* dispersivity */ + case 25: /* dispersivities */ + if (read_line_LDBLEs + (next_char, &disp, &count_disp, &count_disp_alloc) == ERROR) + { + input_error++; + error_msg("Reading dispersivities in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 9; + break; + case 10: /* punch */ + case 21: /* selected_cells */ + case 30: /* punch_cells */ + punch_temp = + read_list_ints_range(&next_char, &count_punch, FALSE, + punch_temp); + opt_save = 10; + break; + case 11: /* stagnant */ + if (copy_token(token, &next_char, &l) != EMPTY) + { + /* exchange factor */ + if (sscanf(token, "%d", &(stag_data.count_stag)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting number of stagnant layers."); + error_msg(error_string, CONTINUE); + break; + } + + /* exchange factor */ + j = copy_token(token, &next_char, &l); + if (j != EMPTY) + { + if (sscanf(token, SCANFORMAT, &(stag_data.exch_f)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting exchange factor for stagnant layers."); + error_msg(error_string, CONTINUE); + break; + } + copy_token(token, &next_char, &l); + if (sscanf(token, SCANFORMAT, &(stag_data.th_m)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting porosity in the mobile zone."); + error_msg(error_string, CONTINUE); + break; + } + copy_token(token, &next_char, &l); + if (sscanf(token, SCANFORMAT, &(stag_data.th_im)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting porosity in the immobile zone."); + error_msg(error_string, CONTINUE); + break; + } + } + } + opt_save = OPTION_DEFAULT; + break; + case 18: /* direction */ + case 22: /* flow_direction */ + case 23: /* flow */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + ishift = 1; + else if (strstr(token, "b") == token) + ishift = -1; + else if (strstr(token, "d") == token) + ishift = 0; + else if (strstr(token, "n") == token) + ishift = 0; + else + { + input_error++; + error_msg + ("Expected flow direction to be 'forward', 'back', or 'no_flow'.", + CONTINUE); + } + opt_save = OPTION_DEFAULT; + break; + case 26: /* dump */ + { + dump_in = TRUE; + std::string temp_name(next_char); + string_trim(temp_name); + if (temp_name.size() > 0) + { + file_name = temp_name; + } + opt_save = OPTION_DEFAULT; + break; + } + case 27: /* output */ + case 28: /* output_frequency */ + case 34: /* print_frequency */ + (void)sscanf(next_char, "%d", &print_modulus); + opt_save = OPTION_DEFAULT; + if (print_modulus <= 0) + { + error_string = sformatf( + "Print frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + print_modulus = 1000; + } + break; + case 31: /* dump_frequency */ + dump_in = TRUE; + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, "%d", &dump_modulus); + else + { + warning_msg("Expected integer value for dump_frequency."); + dump_modulus = 0; + } + opt_save = OPTION_DEFAULT; + break; + case 32: /* dump_restart */ + dump_in = TRUE; + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, "%d", &transport_start); + else + { + warning_msg + ("Expected shift number to start calculations, 1 will be used."); + transport_start = 1; + } + opt_save = OPTION_DEFAULT; + break; + case 35: /* correct_dispersion */ + correct_disp = get_true_false(next_char, TRUE); + opt_save = OPTION_DEFAULT; + break; + case 36: /* initial_time */ + if (copy_token(token, &next_char, &l) == DIGIT) + (void)sscanf(token, SCANFORMAT, &initial_total_time); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + initial_total_time = Utilities::convert_time(initial_total_time, stdtoken, "s"); + } + } + opt_save = OPTION_DEFAULT; + break; + case 37: /* warning */ + case 38: /* warnings */ + transport_warnings = get_true_false(next_char, TRUE); + break; + case 40: /* multicomponent diffusion */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + multi_Dflag = 0; + else if (strstr(token, "t") == token) + multi_Dflag = 1; + else + { + input_error++; + error_msg + ("Expected multicomponent diffusion flag: 'true' or 'false'.", + CONTINUE); + } + default_Dw = 1e-9; + multi_Dpor = 0.3; + multi_Dpor_lim = 0.0; + multi_Dn = 1.0; + correct_Dw = 0; + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* default species diffusion coeff */ + if (sscanf(token, SCANFORMAT, &default_Dw) != 1) + { + input_error++; + error_string = sformatf( + "Expected default species diffusion coefficient in water at 25oC, m2/s."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &multi_Dpor) != 1) + { + input_error++; + error_string = sformatf( + "Expected porosity to calculate diffusion coefficient."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &multi_Dpor_lim) != 1) + { + input_error++; + error_string = sformatf( + "Expected porosity limit for diffusive transport."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + if (sscanf(token, SCANFORMAT, &multi_Dn) != 1) + { + input_error++; + error_string = sformatf( + "Expected exponent for porosity reduction of diffusion coefficient (Dp = Dw * (por)^n)."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + str_tolower(token); + if (strstr(token, "f") == token) + correct_Dw = 0; + else if (strstr(token, "t") == token) + correct_Dw = 1; + else + { + input_error++; + error_msg + ("Expected 'true' or 'false' for correcting Dw's as in Specific Conductance.", + CONTINUE); + } + } + opt_save = OPTION_DEFAULT; + break; + case 41: /* interlayer diffusion */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + interlayer_Dflag = 0; + else if (strstr(token, "t") == token) + interlayer_Dflag = 1; + else + { + input_error++; + error_msg + ("Expected interlayer diffusion flag: 'true' or 'false'.", + CONTINUE); + } + interlayer_Dpor = 0.1; + interlayer_Dpor_lim = 0.0; + interlayer_tortf = 100.0; + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &interlayer_Dpor) != 1) + { + input_error++; + error_string = sformatf("Expected interlayer porosity."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity limit */ + if (sscanf(token, SCANFORMAT, &interlayer_Dpor_lim) != 1) + { + input_error++; + error_string = sformatf( + "Expected interlayer porosity limit for diffusive transport."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + if (sscanf(token, SCANFORMAT, &interlayer_tortf) != 1) + { + input_error++; + error_string = sformatf( + "Expected interlayer tortuosity factor (Dp = Dw /t_f)."); + error_msg(error_string, CONTINUE); + break; + } + } + opt_save = OPTION_DEFAULT; + break; + case 42: /* porosities */ + case 43: /* porosity */ + if (read_line_LDBLEs + (next_char, &pors, &count_por, + &count_por_alloc) == ERROR) + { + input_error++; + error_msg("Reading porosities in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 42; + break; + case 44: /* fix_current */ + case 45: /* current */ + if (copy_token(token, &next_char, &l) == DIGIT) + { + (void)sscanf(token, SCANFORMAT, &fix_current); +// fix_current = fabs(fix_current); + } + else + { + warning_msg("Expected the fixed value for the current (Ampere)."); + fix_current = 0.0; + } + opt_save = OPTION_DEFAULT; + break; + case 46: /* implicit diffusion */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + implicit = FALSE; + else if (strstr(token, "t") == token) + implicit = TRUE; + else + { + input_error++; + error_msg + ("Expected flag for implicit diffusion calc`s: 'true' or 'false'.", + CONTINUE); + } + if (copy_token(token, &next_char, &l) == DIGIT) + { + (void)sscanf(token, SCANFORMAT, &max_mixf); + } + else + { + //warning_msg("Expected the maximal value for the mixfactor (= D * Dt / Dx^2) in implicit calc`s of diffusion."); + max_mixf = 1.0; + } + min_dif_LM = -30.0; + if (copy_token(token, &next_char, &l) != EMPTY) + { + /* minimal moles for diffusion */ + if (sscanf(token, SCANFORMAT, &min_dif_LM) != 1) + { + input_error++; + error_string = sformatf( + "Expected the minimal log10(molality) for including a species in multicomponent diffusion,\n taking -30.0"); + warning_msg(error_string); + break; + } + } + opt_save = OPTION_DEFAULT; + break; + case 47: /* same_model */ + same_model_temp = + read_list_ints_range(&next_char, &count_same_model, FALSE, + same_model_temp); + opt_save = 47; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + /* + * Determine number of cells + */ + max_cells = count_cells; + if (count_length > max_cells) + max_cells = count_length; + if (count_disp > max_cells) + max_cells = count_disp; + if (count_por > max_cells * (1 + stag_data.count_stag)) + max_cells = (int)ceil(((double)count_por / (1 + (double)stag_data.count_stag))); + if (max_cells > count_cells) + { + if (max_cells == count_length) + { + sprintf(token, + "Number of cells is increased to number of 'lengths' %d.", + count_length); + warning_msg(token); + } + else if (max_cells == count_disp) + { + sprintf(token, + "Number of cells is increased to number of dispersivities %d.", + count_disp); + warning_msg(token); + } + else + { + sprintf(token, + "Number of mobile cells is increased to (ceil)(number of porosities) / (1 + number of stagnant zones) = %d.", + (int) ceil(((double)count_por / (1 + (double)stag_data.count_stag)))); + warning_msg(token); + } + } + /* + * Allocate space for cell_data + */ + int all_cells_now = max_cells * (1 + stag_data.count_stag) + 2; + cell_data.resize(all_cells_now); // new classes initialized by global_structures.h + // But first two previously allocated for Change_Surf, so may + // need to reinitialize + if (all_cells_now > all_cells) + { + for (int i = all_cells; i < all_cells_now; i++) + { + cell_data[i].length = 1.0; + cell_data[i].mid_cell_x = 1.0; + cell_data[i].disp = 1.0; + cell_data[i].temp = 25.0; + cell_data[i].por = 0.3; + cell_data[i].por_il = 0.01; + cell_data[i].potV = 0; + cell_data[i].punch = FALSE; + cell_data[i].print = FALSE; + cell_data[i].same_model = FALSE; + } + } + all_cells = all_cells_now; + + /* + * Fill in data for lengths + */ + if (count_length == 0) + { + if (old_cells < max_cells) + { + error_string = sformatf( + "No cell-lengths were read; length = 1 m assumed."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].length = 1.0; + } + } + else + { + for (i = 1; i <= count_length; i++) + { + cell_data[i].length = length[i - 1]; + } + if (max_cells > count_length) + { + error_string = sformatf( + "Cell-lengths were read for %d cells. Last value is used till cell %d.", + count_length, max_cells); + warning_msg(error_string); + for (size_t i = count_length; i <= max_cells; i++) + cell_data[i + 1].length = length[count_length - 1]; + } + } + cell_data[0].mid_cell_x = 0; + cell_data[1].mid_cell_x = cell_data[1].length / 2; + for (i = 2; i <= max_cells; i++) + { + cell_data[i].mid_cell_x = cell_data[i - 1].mid_cell_x + + (cell_data[i - 1].length + cell_data[i].length) / 2; + } + cell_data[max_cells + 1].mid_cell_x = + cell_data[max_cells].mid_cell_x + cell_data[max_cells].length / 2; + /* + * Fill in data for dispersivities + */ + if (count_disp == 0) + { + if (old_cells < max_cells) + { + error_string = sformatf( + "No dispersivities were read; disp = 0 assumed."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].disp = 0.0; + } + } + else + { + for (i = 1; i <= count_disp; i++) + cell_data[i].disp = disp[i - 1]; + if (max_cells > count_disp) + { + error_string = sformatf( + "Dispersivities were read for %d cells. Last value is used till cell %d.", + count_disp, max_cells); + warning_msg(error_string); + for (i = count_disp; i <= max_cells; i++) + cell_data[i + 1].disp = disp[count_disp - 1]; + } + } + /* + * Fill in data for porosities + */ + if (count_por == 0) + { + if (old_cells < all_cells && multi_Dflag /*&& simul_tr == 1*/) + { + multi_Dpor = (multi_Dpor < 1e-10 ? 1e-10 : multi_Dpor); + if (multi_Dpor > 1e-10) + error_string = sformatf( + "No porosities were read; used the value %8.2e from -multi_D.", multi_Dpor); + else + error_string = sformatf( + "No porosities were read; set to minimal value of 1e-10 for -multi_D."); + warning_msg(error_string); + if (simul_tr == 1) + j = 1; + else + j = old_cells + 1; + for (i = j; i < all_cells; i++) + cell_data[i].por = multi_Dpor; + } + } + else + { + if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1)) + { + error_string = sformatf( + "Mobile porosities were read, but mobile/immobile porosity was also defined in -stagnant. Using the values from -stagnant for mobile/immobile exchange and tortuosity factors."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].por = stag_data.th_m; + for (i++; i <= 2 * max_cells + 1; i++) + cell_data[i].por = stag_data.th_im; + } + else + { + for (i = 1; i <= count_por; i++) + { + if (i == max_cells + 1) + continue; + cell_data[i].por = pors[i - 1]; + } + if (all_cells - 2 > count_por) + { + int st = stag_data.count_stag ? 1 : 0; + error_string = sformatf( + "Porosities were read for %d cells. Last value is used till cell %d.", + count_por, all_cells - st); + warning_msg(error_string); + for (i = count_por; i < all_cells - st; i++) + { + //if (i == max_cells) + // continue; + //assert((i+1) < all_cells); + if ((i+1) < all_cells) + { + cell_data[i + 1].por = pors[count_por - 1]; + } + } + } + } + } + if (interlayer_Dflag && !multi_Dflag) + { + input_error++; + error_string = sformatf( + "-multi_D must be defined, when -interlayer_D true."); + error_msg(error_string, CONTINUE); + + } + for (i = 0; i < all_cells; i++) + { + interlayer_Dpor = (interlayer_Dpor < 1e-10 ? 1e-10 : interlayer_Dpor); + cell_data[i].por_il = interlayer_Dpor; + } + count_cells = max_cells; + /* + * Account for stagnant cells + */ + if (stag_data.count_stag > 0) + { + max_cells = count_cells * (1 + stag_data.count_stag) + 2; + for (i = 1; i <= count_cells; i++) + { + for (l = 1; l <= stag_data.count_stag; l++) + cell_data[i + 1 + l * count_cells].mid_cell_x = + cell_data[i].mid_cell_x; + } + } + /* + * Fill in data for punch + */ + if (count_punch != 0) + { + for (i = 0; i < all_cells; i++) + cell_data[i].punch = FALSE; + for (i = 0; i < count_punch; i++) + { + if (punch_temp[i] > all_cells - 1 || punch_temp[i] < 0) + { + error_string = sformatf( + "Cell number for punch is out of range, %d. Request ignored.", + punch_temp[i]); + warning_msg(error_string); + } + else + cell_data[punch_temp[i]].punch = TRUE; + } + } + else if (simul_tr == 1 || old_cells != count_cells) + for (i = 0; i < all_cells; i++) + cell_data[i].punch = TRUE; + /* + * Fill in data for print + */ + if (count_print != 0) + { + for (i = 0; i < all_cells; i++) + cell_data[i].print = FALSE; + for (i = 0; i < count_print; i++) + { + if (print_temp[i] > all_cells - 1 || print_temp[i] < 0) + { + error_string = sformatf( + "Cell number for print is out of range, %d. Request ignored.", + print_temp[i]); + warning_msg(error_string); + } + else + cell_data[print_temp[i]].print = TRUE; + } + } + else if (simul_tr == 1 || old_cells != count_cells) + for (i = 0; i < all_cells; i++) + cell_data[i].print = TRUE; + /* + * Fill in data for same_model + */ + if (count_same_model != 0) + { + for (i = 0; i < all_cells; i++) + cell_data[i].same_model = FALSE; + for (i = 0; i < count_same_model; i++) + { + if (same_model_temp[i] > all_cells - 1 || same_model_temp[i] < 0) + { + error_string = sformatf( + "Cell number for same_model is out of range, %d. Request ignored.", + same_model_temp[i]); + warning_msg(error_string); + } + else + cell_data[same_model_temp[i]].same_model = TRUE; + } + } + else if (simul_tr == 1 || old_cells != count_cells) + for (i = 0; i < all_cells; i++) + cell_data[i].same_model = FALSE; +//#define OLD_POROSITY +#if defined(OLD_POROSITY) + /* + * Fill in porosities + */ + if (interlayer_Dflag && !multi_Dflag) + { + input_error++; + error_string = sformatf( + "-multi_D must be defined, when -interlayer_D true."); + error_msg(error_string, CONTINUE); + + } + for (i = 0; i < max_cells; i++) + { + multi_Dpor = (multi_Dpor < 1e-10 ? 1e-10 : multi_Dpor); //Fix for Jenkins !!!!!!!!!!!! + //if (cell_data[i].por < 0) + { + cell_data[i].por = multi_Dpor; //Fix for Jenkins !!!!!!!!!!!! + } + interlayer_Dpor = (interlayer_Dpor < 1e-10 ? 1e-10 : interlayer_Dpor); + cell_data[i].por_il = interlayer_Dpor; + } +#endif +/* + * Calculate dump_modulus + */ + if (dump_in == TRUE) + { + if (dump_modulus == 0) + { + warning_msg + ("Expected dump_modulus. Value of 'shifts/2' will be used."); + dump_modulus = count_shifts / 2; + if (dump_modulus == 0) + dump_modulus = 1; + } + if (transport_start > count_shifts) + { + input_error++; + error_string = sformatf( + "Starting shift for transport, %d, is greater than number of shifts, %d.", + transport_start, count_shifts); + error_msg(error_string, CONTINUE); + } + } + /* + * Check boundary conditions + */ + if ((ishift != 0) && ((bcon_first == 2) || (bcon_last == 2))) + { + warning_msg + ("Boundary condition = 'closed' not possible with advective transport.\n\t Boundary condition = 'flux' assumed."); + if (bcon_first == 2) + bcon_first = 3; + if (bcon_last == 2) + bcon_last = 3; + } + /* + * Retain data from previous run + */ + if (simul_tr > 1) + { + if ((count_length == 0) && (count_disp == 0) && (count_por == 0)) + dup_print("Column data retained from former run", TRUE); + } + /* + * Check heat_diffc + */ + if (heat_diffc < 0) + heat_diffc = diffc; + else if (stag_data.count_stag == 1) + { + if (stag_data.exch_f > 0) + { + if (diffc <= 0 && heat_diffc > 0) + { + input_error++; + error_string = sformatf( + "Must enter diffusion coefficient (-diffc) when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + else if (heat_diffc > diffc && !implicit) + { + error_string = sformatf( + "Thermal diffusion is calculated assuming exchange factor was for\n\t effective (non-thermal) diffusion coefficient = %e.", + (double)diffc); + warning_msg(error_string); + } + } + else + { + if (heat_diffc > diffc && !implicit) + { + input_error++; + error_string = sformatf( + "Must enter value for mobile/stagnant exchange factor when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + } + } + else if (stag_data.count_stag > 1 && heat_diffc > diffc) + { + input_error++; + error_string = sformatf( + "Only one stagnant layer permitted (-stag) when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + /* + * free storage for length, disp, punch + */ + length = (LDBLE *)free_check_null(length); + disp = (LDBLE *)free_check_null(disp); + pors = (LDBLE *)free_check_null(pors); + punch_temp = (int *)free_check_null(punch_temp); + print_temp = (int *)free_check_null(print_temp); + same_model_temp = (int *)free_check_null(same_model_temp); + + if (dump_in == TRUE) + { + dump_file_name_cpp.clear(); + dump_file_name_cpp.append(file_name); + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_line_LDBLEs(const char* next_char, LDBLE ** d, int *count_d, int *count_alloc) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l, n; + LDBLE value; + char token[MAX_LENGTH]; + + for (;;) + { + j = copy_token(token, &next_char, &l); + if (j == EMPTY) + break; + if (j != DIGIT) + return (ERROR); + if (replace("*", " ", token) == TRUE) + { + if (sscanf(token, "%d" SCANFORMAT, &n, &value) != 2) + return (ERROR); + } + else + { + (void)sscanf(token, SCANFORMAT, &value); + n = 1; + } + for (;;) + { + if ((*count_d) + n > (*count_alloc)) + { + *count_alloc *= 2; + *d = (LDBLE *)PHRQ_realloc(*d, (size_t)(*count_alloc) * sizeof(LDBLE)); + if (*d == NULL) + malloc_error(); + } + else + break; + } + for (i = 0; i < n; i++) + (*d)[(*count_d) + i] = value; + *count_d += n; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_cpp(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * dumps solution compositions to file + */ + + int l; + + if (dump_in == FALSE || pr.dump == FALSE) + return (OK); + + cxxStorageBin phreeqcBin(phrq_io); + phreeqc2cxxStorageBin(phreeqcBin); + + std::ofstream fs(dump_file_name_cpp.c_str()); + if (!fs.is_open()) + { + error_string = sformatf("Can`t open file, %s.", dump_file_name_cpp.c_str()); + input_error++; + error_msg(error_string, CONTINUE); + return (OK); + } + + fs << "# Dumpfile" << "\n" << "# Transport simulation " << simul_tr << " Shift " << transport_step << "\n" << "#" << "\n"; + phreeqcBin.dump_raw(fs, 0); + fs << "END" << "\n"; + + char token[MAX_LENGTH]; + sprintf(token, "KNOBS\n"); + fs << token; + sprintf(token, "\t-iter%15d\n", itmax); + fs << token; + sprintf(token, "\t-tol %15.3e\n", (double)ineq_tol); + fs << token; + sprintf(token, "\t-step%15.3e\n", (double)step_size); + fs << token; + sprintf(token, "\t-pe_s%15.3e\n", (double)pe_step_size); + fs << token; + sprintf(token, "\t-diag "); + fs << token; + if (diagonal_scale == TRUE) + { + sprintf(token, "true\n"); + fs << token; + } + else + { + sprintf(token, "false\n"); + fs << token; + } + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for (; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + + sprintf(token, "SELECTED_OUTPUT %d\n", current_selected_output->Get_n_user()); + fs << token; + //sprintf(token, "\t-file %-15s\n", "sel_o$$$.prn"); + //fs << token; + fs << "\t-file " << "sel_o$$$" << current_selected_output->Get_n_user() << ".prn\n"; + //if (punch.count_totals != 0) + if (current_selected_output->Get_totals().size() > 0) + { + sprintf(token, "\t-tot "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_totals()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_molalities().size() > 0) + { + sprintf(token, "\t-mol "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_molalities()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_activities().size() > 0) + { + sprintf(token, "\t-act "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_activities()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_pure_phases().size() > 0) + { + sprintf(token, "\t-equ "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_pure_phases()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_si().size() > 0) + { + sprintf(token, "\t-si "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_si()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_gases().size() > 0) + { + sprintf(token, "\t-gas "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_gases()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_s_s().size() > 0) + { + sprintf(token, "\t-solid_solutions "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_s_s().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_s_s()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_kinetics().size() > 0) + { + sprintf(token, "\t-kin "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_kinetics()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "TRANSPORT\n"); + fs << token; + sprintf(token, "\t-cells %6d\n", count_cells); + fs << token; + sprintf(token, "\t-shifts%6d%6d\n", count_shifts, ishift); + fs << token; + sprintf(token, "\t-output_frequency %6d\n", print_modulus); + fs << token; + sprintf(token, "\t-selected_output_frequency %6d\n", + punch_modulus); + fs << token; + sprintf(token, "\t-bcon %6d%6d\n", bcon_first, bcon_last); + fs << token; + sprintf(token, "\t-timest %13.5e\n", (double)timest); + fs << token; + if (!high_precision) + { + sprintf(token, "\t-diffc %13.5e\n", (double)diffc); + fs << token; + } + else + { + sprintf(token, "\t-diffc %20.12e\n", (double)diffc); + fs << token; + } + sprintf(token, "\t-tempr %13.5e\n", (double)tempr); + fs << token; + if (correct_disp == TRUE) + { + sprintf(token, "\t-correct_disp %s\n", "True"); + fs << token; + } + else + { + sprintf(token, "\t-correct_disp %s\n", "False"); + fs << token; + } + sprintf(token, "\t-length\n"); + fs << token; + for (int i = 1; i <= count_cells; i++) + { + sprintf(token, "%12.3e", (double)cell_data[i].length); + fs << token; + if (i > 0 && (i % 8) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-disp\n"); + fs << token; + for (int i = 1; i <= count_cells; i++) + { + if (!high_precision) + { + sprintf(token, "%12.3e", (double)cell_data[i].disp); + fs << token; + } + else + { + sprintf(token, "%20.12e", (double)cell_data[i].disp); + fs << token; + } + if (i > 0 && (i % 8) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-punch_cells"); + fs << token; + l = 0; + for (int i = 0; i < all_cells; i++) + { + if (cell_data[i].punch != TRUE) + continue; + sprintf(token, " %d", i); + fs << token; + l++; + if ((l % 20) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-print_cells"); + fs << token; + l = 0; + for (int i = 0; i < all_cells; i++) + { + if (cell_data[i].print != TRUE) + continue; + sprintf(token, " %d", i); + fs << token; + l++; + if ((l % 20) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-dump $$$.dmp\n"); + fs << token; + sprintf(token, "\t-dump_frequency %d\n", dump_modulus); + fs << token; + sprintf(token, "\t-dump_restart %d\n", transport_step + 1); + fs << token; + +#if defined MULTICHART + // user graphs + chart_handler.dump(fs, 0); +#endif + + sprintf(token, "END\n"); + fs << token; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * dumps solution compositions to file + */ + if (dump_in == FALSE || pr.dump == FALSE) + return (OK); + + dump_cpp(); + return OK; + +} diff --git a/phreeqcpp/runner.cpp b/phreeqcpp/runner.cpp new file mode 100644 index 00000000..6aa07a0f --- /dev/null +++ b/phreeqcpp/runner.cpp @@ -0,0 +1,158 @@ +#include "runner.h" +#include "Parser.h" +#include "NA.h" +#include "Utils.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +runner::runner(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->time_step = NA; + this->start_time = NA; + this->run_cells = false; + +} +runner::runner(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->time_step = NA; + this->start_time = NA; + this->run_cells = false; + this->Read(parser); +} + +runner::~runner(void) +{ +} +bool runner::Read(CParser & parser) +{ + + bool return_value(true); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + this->Get_cells().Set_defined(true); + opt_save = CParser::OPT_DEFAULT; + + StorageBinListItem item; + for (;;) + { + int opt; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + + // Process other identifiers + std::set < int >::iterator it; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + + case 0: + case 1: + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (j == CParser::TT_DIGIT) + { + item.Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + item.Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + } + } + break; + case 2: //start_time + if (!(parser.get_iss() >> this->start_time)) + { + parser.error_msg("Expected start_time for RUN_CELLS.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + } + { + std::string token; + if (parser.get_iss() >> token) + { + token = trim(token); + this->start_time = Utilities::convert_time(this->start_time, token, "s"); + } + } + break; + case 3: //time_step + case 4: //time_steps + case 5: //step + case 6: //steps + if (!(parser.get_iss() >> this->time_step)) + { + parser.error_msg("Expected time_step for RUN_CELLS.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + } + { + std::string token; + if (parser.get_iss() >> token) + { + token = trim(token); + this->time_step = Utilities::convert_time(this->time_step, token, "s"); + } + } + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading RUN_CELLS definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (item.Get_numbers().size() > 0) + { + this->cells = item; + } + return(return_value); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("cell"), // 0 + std::vector< std::string >::value_type("cells"), // 1 + std::vector< std::string >::value_type("start_time"), // 2 + std::vector< std::string >::value_type("time_step"), // 3 + std::vector< std::string >::value_type("time_steps"), // 4 + std::vector< std::string >::value_type("step"), // 5 + std::vector< std::string >::value_type("steps") // 6 +}; +const std::vector< std::string > runner::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/runner.h b/phreeqcpp/runner.h new file mode 100644 index 00000000..f492a7fd --- /dev/null +++ b/phreeqcpp/runner.h @@ -0,0 +1,33 @@ +#if !defined(RUNNER_H_INCLUDED) +#define RUNNER_H_INCLUDED +#include // std::set +#include // std::string + +#include "phrqtype.h" +#include "StorageBinList.h" +#include "PHRQ_base.h" +class CParser; + +class runner: public PHRQ_base +{ +public: + runner(PHRQ_io *io=NULL); + runner(CParser & parser, PHRQ_io *io=NULL); + virtual ~runner(void); + bool Read(CParser & parser); + StorageBinListItem & Get_cells(void) { return(this->cells); }; + LDBLE Get_time_step() { return(this->time_step); }; + LDBLE Get_start_time() { return(this->start_time); }; + void Set_time_step(LDBLE ts) { this->time_step = ts; }; + void Set_start_time(LDBLE st) { this->start_time = st; }; + bool Get_run_cells() { return(this->run_cells); }; + void Set_run_cells(bool tf) { this->run_cells = tf; }; + +protected: + LDBLE time_step; + LDBLE start_time; + StorageBinListItem cells; + bool run_cells; + const static std::vector < std::string > vopts; +}; +#endif // !defined(RUNNER_H_INCLUDED) diff --git a/phreeqcpp/sit.cpp b/phreeqcpp/sit.cpp new file mode 100644 index 00000000..8f213e98 --- /dev/null +++ b/phreeqcpp/sit.cpp @@ -0,0 +1,1686 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "Solution.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_init(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialization for SIT + */ + sit_model = FALSE; + sit_params.clear(); + OTEMP = -100.; + OPRESS = -100.; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_tidy(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Make lists of species for cations, anions, neutral + */ + int i, j; + /* + * Ensure new parameters are calculated + */ + OTEMP = -100.; + OPRESS = -100.; + /* + * allocate pointers to species structures + */ + spec.clear(); + spec.resize(3 * s.size(), NULL); + + cations = &spec[0]; + neutrals = &(spec[s.size()]); + anions = &(spec[2 * s.size()]); + sit_MAXCATIONS = (int)s.size(); + sit_FIRSTANION = 2 * (int)s.size(); + sit_MAXNEUTRAL = (int)s.size(); + sit_count_cations = 0; + sit_count_anions = 0; + sit_count_neutrals = 0; + if (itmax < 200) itmax = 200; + /* + * allocate other arrays for SIT + */ + sit_IPRSNT.resize(3 * s.size()); + sit_M.resize(3 * s.size()); + sit_LGAMMA.resize(3 * s.size()); + + for (i = 0; i < (int)s.size(); i++) + { + if (s[i] == s_eminus) + continue; + if (s[i] == s_h2o) + continue; + if (s[i]->type == EX || s[i]->type == SURF) + continue; + if (s[i]->z < -.001) + { + anions[sit_count_anions++] = s[i]; + } + else if (s[i]->z > .001) + { + cations[sit_count_cations++] = s[i]; + } + else + { + neutrals[sit_count_neutrals++] = s[i]; + } + } + /* + * no ethetas + */ + /* + * put species numbers in sit_params + */ + for (i = 0; i < (int)sit_params.size(); i++) + { + for (j = 0; j < 3; j++) + { + if (sit_params[i]->species[j] == NULL) + continue; + sit_params[i]->ispec[j] = sit_ISPEC(sit_params[i]->species[j]); + if ((j < 2 && sit_params[i]->ispec[j] == -1) || + (j == 3 + && (sit_params[i]->type == TYPE_PSI + || sit_params[i]->type == TYPE_ZETA) + && sit_params[i]->ispec[j] == -1)) + { + input_error++; + error_string = sformatf( + "Species for Pitzer parameter not defined in SOLUTION_SPECIES, %s", + sit_params[i]->species[j]); + error_msg(error_string, CONTINUE); + } + } + } /* remake map */ + { + sit_param_map.clear(); + for (int j = 0; j < (int)sit_params.size(); j++) + { + std::set< std::string > header; + for (int i = 0; i < 3; i++) + { + if (sit_params[j]->species[i] != NULL) header.insert(sit_params[j]->species[i]); + } + std::ostringstream key_str; + key_str << sit_params[j]->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + sit_param_map[key] = j; + } + assert ((int) sit_param_map.size() == (int)sit_params.size()); + } + if (get_input_errors() > 0) return (ERROR); + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_ISPEC(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Find species number in spec for character string species name + */ +{ + int i; + for (i = 0; i < 3 * (int)s.size(); i++) + { + if (spec[i] == NULL) + continue; + if (name == spec[i]->name) + { + return (i); + } + } + return (-1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_sit(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + /* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + int n; + class pitz_param *pzp_ptr; + pitz_param_type pzp_type; + + int return_value, opt, opt_save; + const char* next_char; + const char *opt_list[] = { + "epsilon", /* 0 */ + "epsilon1" /* 1 */ + }; + int count_opt_list = 2; + /* + * Read lines + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + n = -1; + pzp_type = TYPE_Other; + pitzer_pe = TRUE; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + pzp_ptr = pitz_param_read(line, n); + if (pzp_ptr != NULL) + { + pzp_ptr->type = pzp_type; + sit_param_store(pzp_ptr); + } + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SIT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* epsilon */ + pzp_type = TYPE_SIT_EPSILON; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 1: /* epsilon1 */ + pzp_type = TYPE_SIT_EPSILON_MU; + n = 2; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + sit_model = TRUE; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_sit_param(class pitz_param *pz_ptr, LDBLE TK, LDBLE TR) +/* ---------------------------------------------------------------------- */ +{ + LDBLE param; + /* + */ + + if (fabs(TK - TR) < 0.01) + { + param = pz_ptr->a[0]; + } + else + { + param = (pz_ptr->a[0] + + pz_ptr->a[1] * (1.e0 / TK - 1.e0 / TR) + + pz_ptr->a[2] * log(TK / TR) + + pz_ptr->a[3] * (TK - TR) + + pz_ptr->a[4] * (TK * TK - TR * TR)); + } + pz_ptr->p = param; + switch (pz_ptr->type) + { + case TYPE_SIT_EPSILON: + pz_ptr->U.eps = param; + break; + case TYPE_SIT_EPSILON_MU: + pz_ptr->U.eps1 = param; + break; + case TYPE_Other: + default: + error_msg("Should not be TYPE_Other in function calc_sit_param", + STOP); + break; + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1; + LDBLE param, z0, z1; + LDBLE A, AGAMMA, T; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ +/* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B; +*/ + LDBLE XI, XX, OSUM, DI, F, OSMOT, B; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + //CONV = 1.0 / LOG_10; + XI = 0.0e0; + XX = 0.0e0; + OSUM = 0.0e0; + /*n + I = *I_X; + TK = *TK_X; + */ + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO sit_M + C + */ + double log_min = log10(MIN_TOTAL); + for (size_t j = 0; j < s_list.size(); j++) + { + i = s_list[j]; + if (spec[i]->lm > log_min) + { + sit_M[i] = under(spec[i]->lm); + } + else + { + sit_M[i] = 0.0; + } + } + //for (i = 0; i < 3 * (int)s.size(); i++) + //{ + // sit_IPRSNT[i] = FALSE; + // sit_M[i] = 0.0; + // if (spec[i] != NULL && spec[i]->in == TRUE) + // { + // if (spec[i]->type == EX || + // spec[i]->type == SURF || spec[i]->type == SURF_PSI) + // continue; + // sit_M[i] = under(spec[i]->lm); + // if (sit_M[i] > MIN_TOTAL) + // sit_IPRSNT[i] = TRUE; + // } + //} + /* + C + C COMPUTE SIT COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP_SIT(TK); + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + sit_LGAMMA[i] = 0.0; + XX = XX + sit_M[i] * fabs(spec[i]->z); + XI = XI + sit_M[i] * spec[i]->z * spec[i]->z; + OSUM = OSUM + sit_M[i]; + } + //for (i = 0; i < 2 * (int)s.size() + sit_count_anions; i++) + //{ + // sit_LGAMMA[i] = 0.0; + // if (sit_IPRSNT[i] == TRUE) + // { + // XX = XX + sit_M[i] * fabs(spec[i]->z); + // XI = XI + sit_M[i] * spec[i]->z * spec[i]->z; + // OSUM = OSUM + sit_M[i]; + // } + //} + I = XI / 2.0e0; + I = mu_x; // Added equation for MU + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + AGAMMA = 3*sit_A0; /* Grenthe p 379 */ + A = AGAMMA / LOG_10; + /* + * F is now for log10 gamma + */ + + B = 1.5; + F = -A * (DI / (1.0e0 + B * DI)); + + + /*OSMOT = -(sit_A0) * pow(I, 1.5e0) / (1.0e0 + B * DI);*/ + T = 1.0 + B*DI; + OSMOT = -2.0*A/(B*B*B)*(T - 2.0*log(T) - 1.0/T); + /* + * Sums for sit_LGAMMA, and OSMOT + * epsilons are tabulated for log10 gamma (not ln gamma) + */ + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + i0 = sit_params[i]->ispec[0]; + i1 = sit_params[i]->ispec[1]; + //if (sit_IPRSNT[i0] == FALSE || sit_IPRSNT[i1] == FALSE) continue; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = sit_params[i]->p; + switch (sit_params[i]->type) + { + case TYPE_SIT_EPSILON: + sit_LGAMMA[i0] += sit_M[i1] * param; + sit_LGAMMA[i1] += sit_M[i0] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param; + } + break; + case TYPE_SIT_EPSILON_MU: + sit_LGAMMA[i0] += sit_M[i1] * I * param; + sit_LGAMMA[i1] += sit_M[i0] * I * param; + OSMOT += sit_M[i0] * sit_M[i1] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I; + } + break; + default: + case TYPE_Other: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + } + + /* + * Add F and CSUM terms to sit_LGAMMA + */ + for (size_t j = 0; j < ion_list.size(); j++) + { + int i = ion_list[j]; + z0 = spec[i]->z; + sit_LGAMMA[i] += z0 * z0 * F; + } + //for (i = 0; i < sit_count_cations; i++) + //{ + // z0 = spec[i]->z; + // sit_LGAMMA[i] += z0 * z0 * F; + //} + //for (i = 2 * (int)s.size(); i < 2 * (int)s.size() + sit_count_anions; i++) + //{ + // z0 = spec[i]->z; + // sit_LGAMMA[i] += z0 * z0 * F; + //} + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + /*COSMOT = 1.0e0 + 2.0e0 * OSMOT / OSUM;*/ + COSMOT = 1.0e0 + OSMOT*LOG_10 / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837e0); + /*if (AW > 1.0) AW = 1.0;*/ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + spec[i]->lg_pitzer = sit_LGAMMA[i]; + } +// for (i = 0; i < 2 * (int)s.size() + sit_count_anions; i++) +// { +// if (sit_IPRSNT[i] == FALSE) continue; +// spec[i]->lg_pitzer = sit_LGAMMA[i]; +///* +// output_msg(sformatf( "%d %s:\t%e\t%e\t%e\t%e \n", i, spec[i]->name, sit_M[i], spec[i]->la, spec[i]->lg_pitzer, spec[i]->lg)); +//*/ +// } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_clean_up(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all allocated memory, except strings + */ + int i; + + for (i = 0; i < (int)sit_params.size(); i++) + { + delete sit_params[i]; + } + sit_params.clear(); + sit_param_map.clear(); + sit_LGAMMA.clear(); + sit_IPRSNT.clear(); + spec.clear(); + //delete aphi; + sit_M.clear(); + + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_sit(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg_pitzer = 0.0; + } + if (initial == TRUE || set_and_run_attempt > 0) + { + for (i = 0; i < (int)this->s_x.size(); i++) + { + s_x[i]->lg = 0.0; + } + } +/* + * Set master species activities + */ + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + potV_x = solution_ptr->Get_potV(); + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + AW = pow((LDBLE) 10.0E0, s_h2o->la); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) sit_initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) initial_surface_water(); + sit_revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise molalities species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + + max_iter = 100; + /* gammas(mu_x); */ + l_iter = 0; + repeat = TRUE; + fail = FALSE; + double d = 2; + double logd = log10(d); + while (repeat == TRUE && fail == FALSE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + /*pitzer(); */ + /*s_h2o->la = 0.0; */ + /*molalities(TRUE); */ + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } + /*n + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || + /* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + f = fabs(x[i]->sum); + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += logd; +/*!!!!*/ if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (f > d * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + x[i]->master[0]->s->la += + weight * log10(fabs(x[i]->moles / x[i]->sum)); + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in sit_revise_guesses: %d\n", l_iter)); + /*mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; */ + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + /*gammas(mu_x); */ + return (OK); +} +//#define ORIGINAL +#ifdef ORIGINAL +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_sit(void) +/* ---------------------------------------------------------------------- */ +{ + std::vector base; + LDBLE d, d1, d2; + int i, j; +Restart: + size_t pz_max_unknowns = max_unknowns; + //k_temp(tc_x, patm_x); + if (full_pitzer == TRUE) + { + molalities(TRUE); + sit(); + residuals(); + } + base = residual; // std::vectors + d = 0.0001; + d1 = d * LOG_10; + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case PITZER_GAMMA: + if (!full_pitzer) + continue; + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + mass_water_aq_x *= (1.0 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = log(1.0 + d); + break; + case MH: + s_eminus->la += d; + d2 = d1; + break; + /* + if (pitzer_pe == TRUE) + { + s_eminus->la += d; + d2 = d1; + break; + } + else + { + continue; + } + */ + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = (x[i]->moles > 1 ? 1 : 20); + d2 *= d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + case MU: + //continue; + d2 = d * mu_x; + mu_x += d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case PP: + case SS_MOLES: + continue; + break; + } + molalities(TRUE); + if (max_unknowns > pz_max_unknowns) + { + gammas_sit(); + jacobian_sums(); + goto Restart; + } + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = + -(residual[j] - base[j]) / d2; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0) + { + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = + exp(s_h2->lm * LOG_10) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + mass_water_aq_x /= (1 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + x[i]->moles -= d2; + break; + } + } + molalities(TRUE); + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + return OK; +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_sit(void) +/* ---------------------------------------------------------------------- */ +{ + std::vector base; + LDBLE d, d1, d2; + int i, j; + cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + std::vector phase_ptrs; + std::vector base_phases; + double base_mass_water_bulk_x = 0, base_moles_h2o = 0; + cxxGasPhase base_gas_phase; + cxxSurface base_surface; +Restart: + if (use.Get_surface_ptr() != NULL) + { + base_surface = *use.Get_surface_ptr(); + } + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr(); + base_gas_phase = *gas_phase_ptr; + base_phases.resize(gas_phase_ptr->Get_gas_comps().size()); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp* gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + class phase* phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE); + phase_ptrs.push_back(phase_ptr); + base_phases[i] = *phase_ptr; + } + } + calculating_deriv = 1; + size_t pz_max_unknowns = max_unknowns; + //k_temp(tc_x, patm_x); + molalities(TRUE); + if (full_pitzer == TRUE) + { + + sit(); + } + mb_sums(); + residuals(); + base = residual; // std::vectors + d = 0.0001; + d1 = d * LOG_10; + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case PITZER_GAMMA: + if (!full_pitzer) + continue; + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + mass_water_aq_x *= (1.0 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = log(1.0 + d); + break; + case MH: + s_eminus->la += d; + d2 = d1; + break; + /* + if (pitzer_pe == TRUE) + { + s_eminus->la += d; + d2 = d1; + break; + } + else + { + continue; + } + */ + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = (x[i]->moles > 1 ? 1 : 20); + d2 *= d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + case MU: + //continue; + d2 = d * mu_x; + mu_x += d2; + //k_temp(tc_x, patm_x); + gammas_sit(); + break; + case PP: + case SS_MOLES: + continue; + break; + } + molalities(TRUE); + if (max_unknowns > pz_max_unknowns) + { + gammas_sit(); + jacobian_sums(); + goto Restart; + } + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + for (j = 0; j < count_unknowns; j++) + { + my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = + -(residual[j] - base[j]) / d2; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0) + { + my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = + exp(s_h2->lm * LOG_10) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + mass_water_aq_x /= (1 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //k_temp(tc_x, patm_x); + gammas_sit(); + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + x[i]->moles -= d2; + break; + } + if (use.Get_surface_ptr() != NULL) + { + *use.Get_surface_ptr() = base_surface; + } + if (use.Get_gas_phase_ptr() != NULL) + { + *use.Get_gas_phase_ptr() = base_gas_phase; + for (size_t g = 0; g < base_phases.size(); g++) + { + *phase_ptrs[g] = base_phases[g]; + } + } + } + molalities(TRUE); + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + //for (i = 0; i < count_unknowns; i++) + //{ + // //Debugging + // if (fabs(2.0 * (residual[i] - base[i]) / (residual[i] + base[i])) > 1e-2 && + // fabs(residual[i]) + fabs(base[i]) > 1e-8) + // { + // std::cerr << i << ": " << x[i]->description << " " << residual[i] << " " << base[i] << std::endl; + // } + //} + calculating_deriv = 0; + return OK; +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model_sit(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * sit_revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + gamma_iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + //sit_make_lists(); + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + overall_iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } + /* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + /* + * Calculate jacobian + */ + gammas_sit(); + jacobian_sums(); + jacobian_sit(); + /* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf( "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + gammas_sit(); + if (full_pitzer == TRUE) + sit(); + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + if (molalities(FALSE) == ERROR) + { + sit_revise_guesses(); + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases() == TRUE) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + if (switch_bases() == TRUE) + { + + count_basis_change++; + //count_unknowns -= (int)this->s_x.size(); + count_unknowns -= sit_aqueous_unknowns; + reprep(); + full_pitzer = false; + } + /* debug + species_list_sort(); + sum_species(); + print_species(); + print_exchange(); + print_surface(); + */ + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + /* remove_unstable_phases is set in check_residuals */ + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + gamma_iterations++; + if (gamma_iterations > itmax) + { + error_string = sformatf( "Maximum gamma iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + if (check_gammas_sit() != TRUE) + { + full_pitzer = TRUE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n", iterations)); + log_msg(sformatf( "Number of gamma iterations: %d\n\n", gamma_iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_gammas_sit(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE old_mu, tol; + int converge, i; + + old_mu = mu_x; + sit(); + molalities(TRUE); + mb_sums(); + converge = TRUE; + tol = convergence_tolerance * 10.; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PITZER_GAMMA) + continue; + if (fabs(x[i]->s->lg - x[i]->s->lg_pitzer) > tol) + { + converge = FALSE; + } + } + if (fabs(old_mu - mu_x) > tol) + { + converge = FALSE; + } + if ((pow((LDBLE) 10.0, s_h2o->la) - AW) > tol) + { + converge = FALSE; + } + return converge; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas_sit() +/* ---------------------------------------------------------------------- */ +{ +/* + * Need exchange gammas for pitzer + */ + int i, j; + LDBLE coef; + /* Initialize */ + k_temp(tc_x, patm_x); +/* + * Calculate activity coefficients + */ + for (i = 0; i < (int)this->s_x.size(); i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + break; + case 4: /* Exchange */ + /* Now calculated in next loop */ + break; + case 5: /* Always 1.0 */ + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x.token[j].s->primary->unknown->moles; + break; + } + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(s_x[i]->equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + break; + case 8: /* LLNL CO2 */ + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + /* + * calculate exchange gammas + */ + + if (use.Get_exchange_ptr() != NULL) + { + for (i = 0; i < (int)this->s_x.size(); i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + case 5: /* Always 1.0 */ + case 6: /* Surface */ + case 7: /* LLNL */ + case 8: /* LLNL CO2 */ + case 9: /* activity water */ + break; + case 4: /* Exchange */ + + /* + * Find CEC + * z contains valence of cation for exchange species, alk contains cec + */ + /* !!!!! */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + { + s_x[i]->alk = + s_x[i]->rxn_x.token[j].s->primary->unknown-> + moles; + break; + } + } + /* + * Master species is a dummy variable with meaningless activity and mass + */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + if (s_x[i]->primary != NULL) + { + break; + } + /* + * All other species + */ + + /* modific 29 july 2005... */ + if (s_x[i]->equiv != 0 && s_x[i]->alk > 0) + { + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + } + if (use.Get_exchange_ptr()->Get_pitzer_exchange_gammas()) + { + /* Assume equal gamma's of solute and exchangeable species... */ + for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x.token[j].s->type == EX) + continue; + coef = s_x[i]->rxn_x.token[j].coef; + s_x[i]->lg += coef * s_x[i]->rxn_x.token[j].s->lg; + s_x[i]->dg += coef * s_x[i]->rxn_x.token[j].s->dg; + } + } + } + } + } +/* ...end modific 29 july 2005 */ + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +PTEMP_SIT(LDBLE TK) +/* ---------------------------------------------------------------------- */ +{ +/* +C +C SUBROUTINE TO CALUCLATE TEMPERATURE DEPENDENCE OF PITZER PARAMETER +C +*/ + LDBLE TR = 298.15; + + if (fabs(TK - OTEMP) < 0.001 && fabs(patm_x - OPRESS) < 0.1) return OK; +/* +C Set DW0 +*/ + DW0 = rho_0 = calc_rho_0(TK - 273.15, patm_x); + VP = patm_x; + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + calc_sit_param(sit_params[i], TK, TR); + } + calc_dielectrics(TK - 273.15, patm_x); + sit_A0 = A0; + OTEMP = TK; + OPRESS = patm_x; + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +sit_make_lists(void) +/* ---------------------------------------------------------------------- */ +{ + double log_min = log10(MIN_TOTAL); + s_list.clear(); + cation_list.clear(); + neutral_list.clear(); + anion_list.clear(); + ion_list.clear(); + param_list.clear(); + OTEMP = -100.0; + for (int j = 0; j < 3; j++) + { + int min, max; + switch (j) + { + case 0: + min = 0; + max = sit_count_cations; + break; + case 1: + min = (int)s.size(); + max = (int)s.size() + sit_count_neutrals; + break; + case 2: + min = 2*(int)s.size(); + max = 2*(int)s.size() + sit_count_anions; + break; + } + for (int i = min; i < max; i++) + { + sit_IPRSNT[i] = FALSE; + sit_M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + sit_IPRSNT[i] = TRUE; + s_list.push_back(i); + if (i < (int)s.size()) + { + cation_list.push_back(i); + } + if (i >= (int)s.size() && i < 2*(int)s.size()) + { + neutral_list.push_back(i); + } + if (i >= 2*(int)s.size()) + { + anion_list.push_back(i); + } + if (i < (int)s.size() || i >= 2*(int)s.size()) + { + ion_list.push_back(i); + } + if (spec[i]->lm > log_min) + { + sit_M[i] = under(spec[i]->lm); + } + } + } + } + for (int i = 0; i < (int)sit_params.size(); i++) + { + int i0 = sit_params[i]->ispec[0]; + int i1 = sit_params[i]->ispec[1]; + if (sit_IPRSNT[i0] == FALSE || sit_IPRSNT[i1] == FALSE) continue; + param_list.push_back(i); + } +} diff --git a/phreeqcpp/smalldense.cpp b/phreeqcpp/smalldense.cpp new file mode 100644 index 00000000..53c587a1 --- /dev/null +++ b/phreeqcpp/smalldense.cpp @@ -0,0 +1,324 @@ +/************************************************************************** + * * + * File : smalldense.c * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic DENSE linear * + * solver package, intended for small dense matrices. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include +#include +#include "smalldense.h" +#include "sundialstypes.h" +#include "sundialsmath.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) + +/* Implementation */ + +realtype ** +denalloc(integertype n) +{ + integertype j; + realtype **a; + + if (n <= 0) + return (NULL); + + a = (realtype **) malloc(n * sizeof(realtype *)); + if (a == NULL) + return (NULL); + + a[0] = (realtype *) malloc((size_t)n * (size_t)n * sizeof(realtype)); + if (a[0] == NULL) + { + free(a); + return (NULL); + } + + for (j = 1; j < n; j++) + a[j] = a[0] + (size_t)j * (size_t)n; + + return (a); +} + +integertype * +denallocpiv(integertype n) +{ + if (n <= 0) + return (NULL); + + return ((integertype *) malloc(n * sizeof(integertype))); +} + +integertype +gefa(realtype ** a, integertype n, integertype * p) +{ + integertype i, j, k, l; + realtype *col_j, *col_k, *diag_k; + realtype temp, mult, a_kj; + booleantype swap; + + /* k = elimination step number */ + + for (k = 0; k < n - 1; k++, p++) + { + + col_k = a[k]; + diag_k = col_k + k; + + /* find l = pivot row number */ + + l = k; + for (i = k + 1; i < n; i++) + if (ABS(col_k[i]) > ABS(col_k[l])) + l = i; + *p = l; + + /* check for zero pivot element */ + + if (col_k[l] == ZERO) + return (k + 1); + + /* swap a(l,k) and a(k,k) if necessary */ + + /*if ( (swap = (l != k) )) { */ + swap = (l != k); + if (swap) + { + temp = col_k[l]; + col_k[l] = *diag_k; + *diag_k = temp; + } + + /* Scale the elements below the diagonal in */ + /* column k by -1.0 / a(k,k). After the above swap, */ + /* a(k,k) holds the pivot element. This scaling */ + /* stores the pivot row multipliers -a(i,k)/a(k,k) */ + /* in a(i,k), i=k+1, ..., n-1. */ + + mult = -ONE / (*diag_k); + for (i = k + 1; i < n; i++) + col_k[i] *= mult; + + /* row_i = row_i - [a(i,k)/a(k,k)] row_k, i=k+1, ..., n-1 */ + /* row k is the pivot row after swapping with row l. */ + /* The computation is done one column at a time, */ + /* column j=k+1, ..., n-1. */ + + for (j = k + 1; j < n; j++) + { + + col_j = a[j]; + a_kj = col_j[l]; + + /* Swap the elements a(k,j) and a(k,l) if l!=k. */ + + if (swap) + { + col_j[l] = col_j[k]; + col_j[k] = a_kj; + } + + /* a(i,j) = a(i,j) - [a(i,k)/a(k,k)]*a(k,j) */ + /* a_kj = a(k,j), col_k[i] = - a(i,k)/a(k,k) */ + + if (a_kj != ZERO) + { + for (i = k + 1; i < n; i++) + col_j[i] += a_kj * col_k[i]; + } + } + } + + /* set the last pivot row to be n-1 and check for a zero pivot */ + + *p = n - 1; + if (a[n - 1][n - 1] == ZERO) + return (n); + + /* return 0 to indicate success */ + + return (0); +} + +void +gesl(realtype ** a, integertype n, integertype * p, realtype * b) +{ + integertype k, l, i; + realtype mult, *col_k; + + /* Solve Ly = Pb, store solution y in b */ + + for (k = 0; k < n - 1; k++) + { + l = p[k]; + mult = b[l]; + if (l != k) + { + b[l] = b[k]; + b[k] = mult; + } + col_k = a[k]; + for (i = k + 1; i < n; i++) + b[i] += mult * col_k[i]; + } + + /* Solve Ux = y, store solution x in b */ + + for (k = n - 1; k >= 0; k--) + { + col_k = a[k]; + b[k] /= col_k[k]; + mult = -b[k]; + for (i = 0; i < k; i++) + b[i] += mult * col_k[i]; + } +} + +void +denzero(realtype ** a, integertype n) +{ + integertype i, j; + realtype *col_j; + + for (j = 0; j < n; j++) + { + col_j = a[j]; + for (i = 0; i < n; i++) + col_j[i] = ZERO; + } +} + +void +dencopy(realtype ** a, realtype ** b, integertype n) +{ + integertype i, j; + realtype *a_col_j, *b_col_j; + + for (j = 0; j < n; j++) + { + a_col_j = a[j]; + b_col_j = b[j]; + for (i = 0; i < n; i++) + b_col_j[i] = a_col_j[i]; + } + +} + +void +denscale(realtype c, realtype ** a, integertype n) +{ + integertype i, j; + realtype *col_j; + + for (j = 0; j < n; j++) + { + col_j = a[j]; + for (i = 0; i < n; i++) + col_j[i] *= c; + } +} + +void +denaddI(realtype ** a, integertype n) +{ + integertype i; + + for (i = 0; i < n; i++) + a[i][i] += ONE; +} + +void +denfreepiv(integertype * p) +{ + free(p); +} + +void +denfree(realtype ** a) +{ + free(a[0]); + free(a); +} + +void +denprint(realtype ** a, integertype n) +{ +#if !defined(R_SO) + integertype i, j; + printf("\n"); + for (i = 0; i < n; i++) + { + for (j = 0; j < n; j++) + { + printf("%10g", (double) a[j][i]); + } + printf("\n"); + } + printf("\n"); +#endif +} diff --git a/phreeqcpp/smalldense.h b/phreeqcpp/smalldense.h new file mode 100644 index 00000000..f7a3e79a --- /dev/null +++ b/phreeqcpp/smalldense.h @@ -0,0 +1,261 @@ +/************************************************************************** + * * + * File : smalldense.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *----------------------------------------------------------------- * + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *----------------------------------------------------------------- * + * This is the header file for a generic DENSE linear solver * + * package, intended for small dense matrices. These routines * + * use the type realtype** for dense matrix arguments. * + * * + * These routines begin with "den" (except for the factor and * + * solve routines which are called gefa and gesl, respectively). * + * The underlying matrix storage is described in the * + * documentation for denalloc. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _smalldense_h +#define _smalldense_h + +#include "sundialstypes.h" + + +/****************************************************************** + * * + * Function : denalloc * + * Usage : realtype **a; * + * a = denalloc(n); * + * if (a == NULL) ... memory request failed * + *----------------------------------------------------------------* + * denalloc(n) allocates storage for an n by n dense matrix. It * + * returns a pointer to the newly allocated storage if * + * successful. If the memory request cannot be satisfied, then * + * denalloc returns NULL. The underlying type of the dense matrix * + * returned is realtype **. If we allocate a dense matrix * + * realtype **a by a = denalloc(n), then a[j][i] references the * + * (i,j)th element of the matrix a, 0 <= i,j <= n-1, and a[j] is * + * a pointer to the first element in the jth column of a. * + * The location a[0] contains a pointer to n^2 contiguous * + * locations which contain the elements of a. * + * * + ******************************************************************/ + + realtype **denalloc(integertype n); + + +/****************************************************************** + * * + * Function : denallocpiv * + * Usage : integertype *pivot; * + * pivot = denallocpiv(n); * + * if (pivot == NULL) ... memory request failed * + *----------------------------------------------------------------* + * denallocpiv(n) allocates an array of n integertype. It returns * + * a pointer to the first element in the array if successful. * + * It returns NULL if the memory request could not be satisfied. * + * * + ******************************************************************/ + + integertype *denallocpiv(integertype n); + + +/****************************************************************** + * * + * Function : gefa * + * Usage : integertype ier; * + * ier = gefa(a,n,p); * + * if (ier > 0) ... zero element encountered during * + * the factorization * + *----------------------------------------------------------------* + * gefa(a,n,p) factors the n by n dense matrix a. It overwrites * + * the elements of a with its LU factors and keeps track of the * + * pivot rows chosen in the pivot array p. * + * * + * A successful LU factorization leaves the matrix a and the * + * pivot array p with the following information: * + * * + * (1) p[k] contains the row number of the pivot element chosen * + * at the beginning of elimination step k, k=0, 1, ..., n-1. * + * * + * (2) If the unique LU factorization of a is given by Pa = LU, * + * where P is a permutation matrix, L is a lower triangular * + * matrix with all 1's on the diagonal, and U is an upper * + * triangular matrix, then the upper triangular part of a * + * (including its diagonal) contains U and the strictly lower * + * triangular part of a contains the multipliers, I-L. * + * * + * gefa returns 0 if successful. Otherwise it encountered a zero * + * diagonal element during the factorization. In this case it * + * returns the column index (numbered from one) at which it * + * encountered the zero. * + * * + ******************************************************************/ + + integertype gefa(realtype ** a, integertype n, integertype * p); + + +/****************************************************************** + * * + * Function : gesl * + * Usage : realtype *b; * + * ier = gefa(a,n,p); * + * if (ier == 0) gesl(a,n,p,b); * + *----------------------------------------------------------------* + * gesl(a,n,p,b) solves the n by n linear system ax = b. It * + * assumes that a has been LU factored and the pivot array p has * + * been set by a successful call to gefa(a,n,p). The solution x * + * is written into the b array. * + * * + ******************************************************************/ + + void gesl(realtype ** a, integertype n, integertype * p, realtype * b); + + +/****************************************************************** + * * + * Function : denzero * + * Usage : denzero(a,n); * + *----------------------------------------------------------------* + * denzero(a,n) sets all the elements of the n by n dense matrix * + * a to be 0.0. * + * * + ******************************************************************/ + + void denzero(realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : dencopy * + * Usage : dencopy(a,b,n); * + *----------------------------------------------------------------* + * dencopy(a,b,n) copies the n by n dense matrix a into the * + * n by n dense matrix b. * + * * + ******************************************************************/ + + void dencopy(realtype ** a, realtype ** b, integertype n); + + +/****************************************************************** + * * + * Function : denscale * + * Usage : denscale(c,a,n); * + *----------------------------------------------------------------* + * denscale(c,a,n) scales every element in the n by n dense * + * matrix a by c. * + * * + ******************************************************************/ + + void denscale(realtype c, realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : denaddI * + * Usage : denaddI(a,n); * + *----------------------------------------------------------------* + * denaddI(a,n) increments the n by n dense matrix a by the * + * identity matrix. * + * * + ******************************************************************/ + + void denaddI(realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : denfreepiv * + * Usage : denfreepiv(p); * + *----------------------------------------------------------------* + * denfreepiv(p) frees the pivot array p allocated by * + * denallocpiv. * + * * + ******************************************************************/ + + void denfreepiv(integertype * p); + + +/****************************************************************** + * * + * Function : denfree * + * Usage : denfree(a); * + *----------------------------------------------------------------* + * denfree(a) frees the dense matrix a allocated by denalloc. * + * * + ******************************************************************/ + + void denfree(realtype ** a); + + +/****************************************************************** + * * + * Function : denprint * + * Usage : denprint(a,n); * + *----------------------------------------------------------------* + * denprint(a,n) prints the n by n dense matrix a to standard * + * output as it would normally appear on paper. It is intended as * + * a debugging tool with small values of n. The elements are * + * printed using the %g option. A blank line is printed before * + * and after the matrix. * + * * + ******************************************************************/ + + void denprint(realtype ** a, integertype n); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ diff --git a/phreeqcpp/spread.cpp b/phreeqcpp/spread.cpp new file mode 100644 index 00000000..77ada3e7 --- /dev/null +++ b/phreeqcpp/spread.cpp @@ -0,0 +1,1296 @@ +#ifndef boolean +typedef unsigned char boolean; +#endif +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Solution.h" +#include "Utils.h" + +#define STRING 11 +#define NUMBER 12 +#define MIXED 13 +#define EOL 14 + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPT_1 -5 + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solution_spread(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + class spread_row *heading, *row_ptr, *units; + int count, strings, numbers; + int spread_lines; + const char* cptr; + class defaults soln_defaults; + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "isotope_uncertainty", /* 11 */ + "uncertainty", /* 12 */ + "uncertainties", /* 13 */ + "pressure", /* 14 */ + "press" /* 15 */ + }; + int count_opt_list = 16; +/* + * Initialize defaults + */ + soln_defaults.temp = 25; + soln_defaults.density = 1.0; + soln_defaults.calc_density = false; + soln_defaults.units = string_hsave("mmol/kgw"); + soln_defaults.redox = string_hsave("pe"); + soln_defaults.ph = 7.0; + soln_defaults.pe = 4.0; + soln_defaults.water = 1.0; + soln_defaults.pressure = 1.0; + +#ifdef PHREEQCI_GUI + free_spread(); +#endif + + + /* fill in soln_defaults.iso */ + soln_defaults.iso.resize(count_iso_defaults); + + /* all iso[i].name is hsave'd, so no conflicts */ + // memcpy(&soln_defaults.iso[0], iso_defaults, + // soln_defaults.iso.size() * sizeof(class iso)); + for (size_t i = 0; i < count_iso_defaults; ++i) { + soln_defaults.iso[i].name = iso_defaults[i].name; + soln_defaults.iso[i].value = iso_defaults[i].value; + soln_defaults.iso[i].uncertainty = iso_defaults[i].uncertainty; + } + + heading = NULL; + units = NULL; + return_value = UNKNOWN; + spread_lines = 0; + CParser parser(this->phrq_io); +/* + * Loop on solutions + */ + for (;;) + { + std::string token, token1; + opt = get_option(opt_list, count_opt_list, &next_char); + if (spread_lines == 0 && opt != OPTION_DEFAULT) + { + row_ptr = string_to_spread_row(line); + cptr = line; + count = numbers = strings = 0; + int j; + while (((j = copy_token(token, &cptr)) != EMPTY)) + { + count++; + if (j == UPPER || j == LOWER) + strings++; + if (j == DIGIT) + numbers++; + } + /* + * Is 2nd token all number + */ + cptr = line; + copy_token(token, &cptr); + j = copy_token(token, &cptr); + bool num = false; + if (j == DIGIT) + { + char* ptr; + (void)strtod(token.c_str(), &ptr); + cptr = ptr; + int j1 = copy_token(token1, &cptr); + if (j1 != EMPTY) + { + num = FALSE; + } + else + { + num = TRUE; + } + } + /* + * Starts with hyphen + */ + cptr = line; + copy_token(token, &cptr); + if (token[0] == '-') + { + /* opt = opt; */ + } + else + { + switch (opt) + { + case 0: /* temp */ + case 1: /* temperature */ + case 2: /* dens */ + case 3: /* density */ + case 10: /* water */ + if ((count == 2 || count == 3) && num == TRUE) + { + /* opt = opt; */ + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 6: /* ph */ + case 7: /* pe */ + if ((count == 2 || count == 3 || count == 4) + && num == TRUE) + { + /* opt = opt; */ + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 5: /* redox */ + case 4: /* units */ + case 8: /* unit */ + if (count == 2) + { + /* opt = opt; */ + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 9: /* isotope */ + if (row_ptr->count > 4) + { + opt = OPTION_DEFAULT; + } + else + { + /* opt = opt; */ + } + break; + case 11: /* isotope_uncertainty */ + case 12: /* uncertainty */ + case 13: /* uncertainties */ + if (row_ptr->count > 3) + { + opt = OPTION_DEFAULT; + } + else + { + /* opt = opt; */ + } + break; + case 14: /* pressure */ + case 15: /* press */ + (void)sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure)); + break; + } + } + spread_row_free(row_ptr); + } + if (opt == OPTION_DEFAULT) + { + if (spread_lines == 0) + { + opt = 100; + } + spread_lines++; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* solution definition */ + row_ptr = string_to_spread_row(line); + if (spread_lines == 2) + { + numbers = 0; + strings = 0; + //for (int i = 0; i < heading->count; i++) + for (int i = 0; i < row_ptr->count; i++) + { + if (row_ptr->type_vector[i] == STRING) + { + strings++; + } + else if (row_ptr->type_vector[i] == NUMBER) + { + numbers++; + } + } + if (numbers == 0) + { + units = row_ptr; + break; + } + } + spread_row_to_solution(heading, units, row_ptr, soln_defaults); +#ifdef PHREEQCI_GUI + add_row(row_ptr); +#endif + spread_row_free(row_ptr); + break; + case 0: /* temperature */ + case 1: + (void)sscanf(next_char, SCANFORMAT, &(soln_defaults.temp)); + break; + case 2: /* density */ + case 3: + //sscanf(next_char, SCANFORMAT, &(soln_defaults.density)); + { + copy_token(token, &next_char); + if (sscanf(token.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expecting numeric value for density.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + soln_defaults.density = dummy; + } + int j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + soln_defaults.calc_density = true; + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == EMPTY) + break; + { + if (check_units(token, FALSE, FALSE, NULL, TRUE) == OK) + { + soln_defaults.units = string_hsave(token.c_str()); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == EMPTY) + break; + if (parser.parse_couple(token) == OK) + { + soln_defaults.redox = string_hsave(token.c_str()); + } + else + { + input_error++; + } + break; + case 6: /* ph */ + copy_token(token, &next_char); + (void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.ph)); + if (copy_token(token, &next_char) != EMPTY) + { + warning_msg + ("Not possible to use phase name or saturation index in definition of default pH in SOLUTION_SPREAD."); + } + break; + case 7: /* pe */ + copy_token(token, &next_char); + (void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.pe)); + if (copy_token(token, &next_char) != EMPTY) + { + warning_msg + ("Not possible to use phase name or saturation index in definition of default pe in SOLUTION_SPREAD."); + } + break; + case 11: /* isotope_uncertainty */ + case 12: /* uncertainty */ + case 13: /* uncertainties */ + { + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, CONTINUE); + error_string = sformatf( "In read_solution_spread isotope_uncertainty\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + size_t i; + for (i = 0; i < soln_defaults.iso.size(); i++) + { + if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0) + { + break; + } + } + if (i == soln_defaults.iso.size()) + { + soln_defaults.iso.resize((size_t)i + 1); + soln_defaults.iso[i].name = string_hsave(token.c_str()); + soln_defaults.iso[i].value = NAN; + soln_defaults.iso[i].uncertainty = NAN; + } + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, + &(soln_defaults.iso[i].uncertainty)); + } + } + else + { + soln_defaults.iso[i].uncertainty = NAN; + } + } + break; + case 10: /* water */ + { + int j = copy_token(token, &next_char); + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.water)); + } + } + break; + case 9: /* isotope */ + { + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, CONTINUE); + error_string = sformatf( "In read_solution_spread isotope\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + size_t i; + for (i = 0; i < soln_defaults.iso.size(); i++) + { + if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0) + { + break; + } + } + if (i == soln_defaults.iso.size()) + { + soln_defaults.iso.resize(i + 1); + soln_defaults.iso[i].name = string_hsave(token.c_str()); + soln_defaults.iso[i].value = NAN; + soln_defaults.iso[i].uncertainty = NAN; + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for default isotope ratio."); + error_msg(error_string, CONTINUE); + break; + } + (void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.iso[i].value)); + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, + &(soln_defaults.iso[i].uncertainty)); + } + } + } + break; + case 14: /* pressure */ + (void)sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure)); + break; + case 100: /* read headings */ + heading = string_to_spread_row(line); + { + int i; + for (i = 0; i < heading->count; i++) + { + while (replace(" ", "", heading->str_vector[i]) == TRUE); + while (replace(",", "_", heading->str_vector[i]) == TRUE); + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +#ifdef PHREEQCI_GUI + if (heading) + { + assert(g_spread_sheet.heading == NULL); + g_spread_sheet.heading = copy_row(heading); + } + if (units) + { + assert(g_spread_sheet.units == NULL); + g_spread_sheet.units = copy_row(units); + } + g_spread_sheet.defaults = soln_defaults; +#endif + spread_row_free(heading); + spread_row_free(units); + + soln_defaults.iso.clear(); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +spread_row_to_solution(class spread_row *heading, class spread_row *units, + class spread_row *data, class defaults defaults) +/* ---------------------------------------------------------------------- */ +{ + Keywords::KEYWORDS next_keyword_save; + int n_user, n_user_end; + std::string description; + std::string token, token1, string; + CParser parser(this->phrq_io); + + int return_value, opt; + const char* next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "description", /* 11 */ + "desc", /* 12 */ + "descriptor", /* 13 */ + "pressure", /* 14 */ + "press", /* 15 */ + "potential" /* 16 */ + }; + int count_opt_list = 17; + +/* + * look for solution number + */ + n_user = n_user_end = -1; + { + int i; + for (i = 0; i < heading->count; i++) + { + if (strcmp_nocase(heading->str_vector[i].c_str(), "number") == 0) + { + break; + } + } + if (i == heading->count || data->type_vector[i] == EMPTY + || data->count <= i) + { + n_user = -1; + } + else if (data->type_vector[i] == STRING) + { + input_error++; + error_string = sformatf( + "Expected solution number or number range in 'number' column, found: %s.", + data->str_vector[i].c_str()); + error_msg(error_string, CONTINUE); + } + else + { + string = "solution_s "; + string.append(data->str_vector[i]); + next_keyword_save = next_keyword; + next_keyword = Keywords::KEY_SOLUTION_SPREAD; + cxxNumKeyword nk; + nk.read_number_description(string); + n_user = nk.Get_n_user(); + n_user_end = nk.Get_n_user_end(); + description = nk.Get_description(); + next_keyword = next_keyword_save; + Rxn_new_solution.insert(n_user); + } + } +/* + * set up solution + */ + + cxxSolution temp_solution; + temp_solution.Set_n_user(n_user); + temp_solution.Set_n_user_end(n_user_end); + temp_solution.Set_new_def(true); + temp_solution.Create_initial_data(); + cxxISolution * initial_data_ptr = temp_solution.Get_initial_data(); + if (use.Get_solution_in() == FALSE) + { + use.Set_solution_in(true); + use.Set_n_solution_user(n_user); + } +/* + * Set default ph, temp, density, pe, units + */ + temp_solution.Set_description(description); + temp_solution.Set_tc(defaults.temp); + temp_solution.Set_patm(defaults.pressure); + temp_solution.Set_potV(0); + temp_solution.Set_ph(defaults.ph); + temp_solution.Set_density(defaults.density); + initial_data_ptr->Set_calc_density(defaults.calc_density); + temp_solution.Set_pe(defaults.pe); + temp_solution.Set_mass_water(defaults.water); + temp_solution.Set_ah2o(1.0); + temp_solution.Set_mu(1e-7); + initial_data_ptr->Set_units(defaults.units); + initial_data_ptr->Set_default_pe(defaults.redox); + { + CReaction temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[defaults.redox] = temp_chem_reaction; + } +/* + * Read concentration data + */ + return_value = UNKNOWN; + for (int i = 0; i < heading->count; i++) + { + if (strcmp_nocase(heading->str_vector[i].c_str(), "number") == 0) + continue; + if (strcmp_nocase(heading->str_vector[i].c_str(), "uncertainty") == 0) + continue; + if (strcmp_nocase(heading->str_vector[i].c_str(), "uncertainties") == 0) + continue; + if (strcmp_nocase(heading->str_vector[i].c_str(), "isotope_uncertainty") == + 0) + continue; + /* + * Copy in element name + */ + if (heading->type_vector[i] == EMPTY) + continue; + string = heading->str_vector[i]; + string.append(" "); + /* + * Copy in concentration data + */ + if (i >= data->count || data->type_vector[i] == EMPTY) + continue; + string.append(data->str_vector[i]); + string.append(" "); + /* + * Copy in concentration data + */ + if (units != NULL && i < units->count + && units->type_vector[i] != EMPTY) + { + string.append(units->str_vector[i]); + } +/* + * Parse string just like read_solution input + */ + char * char_string = string_duplicate(string.c_str()); + next_char = char_string; + opt = get_option_string(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT && heading->type_vector[i] == NUMBER) + { + opt = 9; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* temperature */ + case 1: + (void)sscanf(next_char, SCANFORMAT, &dummy); + temp_solution.Set_tc(dummy); + break; + case 2: /* density */ + case 3: + { + int j = copy_token(token, &next_char); + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_density(dummy); + j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + initial_data_ptr->Set_calc_density(true); + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == EMPTY) + break; + { + if (check_units(token, false, false, initial_data_ptr->Get_units().c_str(), true) == OK) + { + initial_data_ptr->Set_units(token); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == EMPTY) + break; + if (parser.parse_couple(token) == OK) + { + const char * pe_str = string_hsave(token.c_str()); + initial_data_ptr->Set_default_pe(pe_str); + CReaction temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[token] = temp_chem_reaction; + } + else + { + input_error++; + } + break; + case 6: /* ph */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + + temp_solution.Set_ph(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + + } + temp_comp.Set_description("H(1)"); + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 7: /* pe */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + temp_solution.Set_pe(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + } + temp_comp.Set_description("E"); + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 9: /* isotope */ + { + next_char = char_string; + cxxSolutionIsotope temp_isotope; + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "In spread_row_to_solution isotope\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "char_string: ", char_string); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); +//class spread_row +//{ +// int count; +// int empty, string, number; +// char **char_vector; +// LDBLE *d_vector; +// int *type_vector; +//}; + error_string = sformatf("Heading spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (heading) + { + for (int ii = 0; ii < heading->count; ii++) + { + error_string = sformatf("%d\t%s\n",ii,heading->str_vector[ii].c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("heading is null", PHRQ_io::OT_CONTINUE); + } + + error_string = sformatf("Data spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (data) + { + for (int ii = 0; ii < data->count; ii++) + { + error_string = sformatf("%d\t%s\t%d\n",ii,data->str_vector[ii].c_str(),data->type_vector[ii]); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("Data is null", PHRQ_io::OT_CONTINUE); + } + error_string = sformatf("Units spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (units) + { + for (int ii = 0; ii < units->count; ii++) + { + error_string = sformatf("%d\t%s\n",ii,units->str_vector[ii].c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("Units is null", PHRQ_io::OT_CONTINUE); + } + free_check_null(char_string); + continue; + } + temp_isotope.Set_isotope_name(token.c_str()); + + /* read and save element name */ + { + char *temp_iso_name = string_duplicate(token.c_str()); + const char* cptr1 = temp_iso_name; + get_num(&cptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (cptr1[0] == '\0' || isupper((int)cptr1[0]) == FALSE) + { + error_msg("Expecting element name.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + temp_iso_name = (char*)free_check_null(temp_iso_name); + char_string = (char*)free_check_null(char_string); + return (CParser::PARSER_ERROR); + } + temp_isotope.Set_elt_name(cptr1); + temp_iso_name = (char*)free_check_null(temp_iso_name); + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for isotope ratio."); + error_msg(error_string, CONTINUE); + free_check_null(char_string); + continue; + } + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + temp_isotope.Set_ratio_uncertainty(NAN); + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != CParser::TT_EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + free_check_null(char_string); + continue; + } + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + } + temp_solution.Get_isotopes()[temp_isotope.Get_isotope_name()] = temp_isotope; + } + break; + case 10: /* water */ + { + int j = copy_token(token, &next_char); + if (j == EMPTY) + { + temp_solution.Set_mass_water(1.0); + } + else if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + (void)sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_mass_water(dummy); + } + } + break; + case 11: /* description */ + case 12: /* desc */ + case 13: /* descriptor */ + { + temp_solution.Set_description(next_char); + } + break; + case 14: /* pressure */ + case 15: /* press */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_patm(dummy); + } + } + break; + case 16: /* pote, V */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_potV(dummy); + } + } + break; + case OPTION_DEFAULT: +/* + * Read concentration + */ + { + next_char = char_string; + if (copy_token(token, &next_char) == LOWER) + { + free_check_null(char_string); + continue; + } + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { +#ifdef SKIP + input_error++; + break; +#endif + } + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + if (temp_comp.Get_pe_reaction().size() > 0) + { + CReaction temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[temp_comp.Get_pe_reaction()] = temp_chem_reaction; + } + } + break; + } + free_check_null(char_string); + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * fix up default units and default pe + */ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = initial_data_ptr->Get_comps().begin(); it != initial_data_ptr->Get_comps().end(); it++) + { + token = it->first; + Utilities::str_tolower(token); + if (it->second.Get_units().size() == 0) + { + it->second.Set_units(initial_data_ptr->Get_units().c_str()); + } + else + { + bool alk = false; + if (strstr(token.c_str(), "alk") == token.c_str()) + alk = true; + std::string token1 = it->second.Get_units(); + if (check_units(token1, alk, true, initial_data_ptr->Get_units().c_str(), true) == CParser::PARSER_ERROR) + { + input_error++; + } + else + { + it->second.Set_units(token1.c_str()); + } + } + if (it->second.Get_pe_reaction().size() == 0) + { + it->second.Set_pe_reaction(initial_data_ptr->Get_default_pe()); + } + } + if (n_user >= 0) + { + Rxn_solution_map[n_user] = temp_solution; + } + else + { + unnumbered_solutions.push_back(temp_solution); + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +class spread_row * Phreeqc:: +string_to_spread_row(char *string) +/* ---------------------------------------------------------------------- */ +{ + int j; + std::string token; + const char* cptr; +/* + * Allocate space + */ + class spread_row* spread_row_ptr = new class spread_row; + if (spread_row_ptr == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr->count = 0; + spread_row_ptr->empty = 0; + spread_row_ptr->string = 0; + spread_row_ptr->number = 0; + cptr = string; +/* + * Split by tabs, reallocate space + */ + for (;;) + { + j = copy_token_tab(token, &cptr); + if (j == EOL) + break; + spread_row_ptr->str_vector.push_back(token); + if (j == EMPTY || token.size() == 0) + { + spread_row_ptr->empty++; + spread_row_ptr->type_vector.push_back(EMPTY); + } + else if (j == UPPER || j == LOWER) + { + spread_row_ptr->string++; + spread_row_ptr->type_vector.push_back(STRING); + } + else if (j == DIGIT) + { + spread_row_ptr->number++; + spread_row_ptr->type_vector.push_back(NUMBER); + } + else + { + input_error++; + error_msg("Unknown input in string_to_spread_row keyword.", CONTINUE); + error_string = sformatf("\tcopy_token j: %d, token: %s\n", j, token.c_str()); + error_msg(error_string, CONTINUE); + error_msg(line_save, CONTINUE); + } + spread_row_ptr->count++; + } + assert(spread_row_ptr->count == spread_row_ptr->str_vector.size()); + assert(spread_row_ptr->count == spread_row_ptr->type_vector.size()); + assert(spread_row_ptr->count == spread_row_ptr->empty + spread_row_ptr->string + spread_row_ptr->number); + return (spread_row_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +spread_row_free(class spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + if (spread_row_ptr == NULL) + return (OK); + spread_row_ptr->str_vector.clear(); + spread_row_ptr->type_vector.clear(); + delete spread_row_ptr; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token_tab(std::string& token, const char **cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **cptr to *token until first tab is encountered. + * + * Arguments: + * *token output, place to store token + * + * **cptr input, character string to read token from + * output, next position after token + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * EOL, + * UNKNOWN. + */ + int i, return_value; + char c; +/* + * Strip leading spaces + */ + token.clear(); + while ((c = **cptr) == ' ') + (*cptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EOL; + return (return_value); + } + else if (c == '\t') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + i = 0; + for (;;) + { + c = **cptr; + if (c == '\t') + { + (*cptr)++; + break; + } + else if (c == '\0') + { + break; + } + else + { + token.push_back(c); + (*cptr)++; + i++; + } + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +get_option_string(const char **opt_list, int count_opt_list, const char **next_char) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line and check for options + */ + int j; + int opt_l, opt; + const char *opt_ptr; + char option[MAX_LENGTH]; + + opt_ptr = *next_char; + if (opt_ptr[0] == '-') + { + opt_ptr++; + copy_token(option, &opt_ptr, &opt_l); + if (find_option(&(option[1]), &opt, opt_list, count_opt_list, FALSE) + == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + error_msg("Unknown option.", CONTINUE); + error_msg(*next_char, CONTINUE); + input_error++; + j = OPTION_ERROR; + } + } + else + { + copy_token(option, &opt_ptr, &opt_l); + if (find_option(&(option[0]), &opt, opt_list, count_opt_list, TRUE) + == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + j = OPTION_DEFAULT; + } + } + return (j); +} + +#if defined(PHREEQCI_GUI) +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +free_spread(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + spread_row_free(g_spread_sheet.heading); + spread_row_free(g_spread_sheet.units); + for (i = 0; i < g_spread_sheet.rows.size(); ++i) + { + spread_row_free(g_spread_sheet.rows[i]); + } + g_spread_sheet.rows.clear(); + g_spread_sheet.defaults.iso.clear(); + g_spread_sheet.defaults.redox = NULL; + g_spread_sheet.defaults.units = NULL; + + g_spread_sheet.heading = NULL; + g_spread_sheet.units = NULL; + g_spread_sheet.defaults.iso.clear(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +add_row(class spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + g_spread_sheet.rows.push_back(copy_row(spread_row_ptr)); +} + +/* ---------------------------------------------------------------------- */ +class spread_row * Phreeqc:: +copy_row(class spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + spread_row *copy = new spread_row(*spread_row_ptr); + if (copy == NULL) + malloc_error(); + return copy; +} +#endif diff --git a/phreeqcpp/step.cpp b/phreeqcpp/step.cpp new file mode 100644 index 00000000..a5503f02 --- /dev/null +++ b/phreeqcpp/step.cpp @@ -0,0 +1,1485 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include "Utils.h" +#include "StorageBin.h" +#include "Solution.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "SS.h" +#include "NameDouble.h" +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step(LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * zero global solution, add solution or mixture, add exchange, + * add surface, add gas phase, add solid solutions, + * set temperature, and add reaction. + * Ensure all elements + * included in any of these are present in small amounts. + * Save result as n_user -1. + */ + LDBLE difftemp; + int step_number; + cxxPPassemblage *pp_assemblage_save = NULL; + cxxSSassemblage *ss_assemblage_save = NULL; +/* + * Zero out global solution data + */ + + xsolution_zero(); +/* + * Set reaction to zero + */ + step_x = 0.0; + step_number = reaction_step; +/* + * Mixing or solution + */ + if (use.Get_mix_ptr() != NULL) + { + add_mix(use.Get_mix_ptr()); + int n = use.Get_n_mix_user_orig(); + if (n == 0 || n == count_cells + 1) + { + cxxSolution *solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n); + if (solution_ptr != NULL && !solution_ptr->Get_new_def()) + potV_x = solution_ptr->Get_potV(); + } + } + else if (use.Get_solution_ptr() != NULL) + { + add_solution(use.Get_solution_ptr(), 1.0, 1.0); + potV_x = use.Get_solution_ptr()->Get_potV(); + cell_no = use.Get_n_solution_user(); + } + else + { + input_error++; + error_msg("Neither mixing nor an initial solution have " + "been defined in reaction step.", STOP); + } +/* + * Reaction + */ + if (use.Get_reaction_ptr() != NULL) + { + add_reaction( use.Get_reaction_ptr(), step_number, step_fraction); + } +/* + * Kinetics + */ + if (use.Get_kinetics_ptr() != NULL) + { + add_kinetics(use.Get_kinetics_ptr()); + } +/* + * Exchange + */ + if (use.Get_exchange_ptr() != NULL) + { + add_exchange(use.Get_exchange_ptr()); + } +/* + * Surface + */ + if (use.Get_surface_ptr() != NULL) + { + add_surface(use.Get_surface_ptr()); + } +/* + * Gases + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + add_gas_phase(gas_phase_ptr); + } +/* + * Temperature + */ + if (use.Get_temperature_ptr() != NULL) + { + cxxTemperature *t_ptr = use.Get_temperature_ptr(); + tc_x = t_ptr->Temperature_for_step(step_number); + } + if ((state == TRANSPORT) && (transport_step != 0) && + (cell > 0) && (cell != count_cells + 1)) // ** needs potV correction + { + difftemp = tc_x - cell_data[cell].temp; + cell_data[cell].temp += difftemp / tempr; + tc_x = cell_data[cell].temp; + } +/* + * Pressure + */ + if (use.Get_pressure_ptr() != NULL) + { + cxxPressure *p_ptr = use.Get_pressure_ptr(); + patm_x = p_ptr->Pressure_for_step(step_number); + } +/* + * Pure phases and solid solutions are added to avoid + * zero or negative concentrations + */ +/* + * Pure phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + add_pp_assemblage(pp_assemblage_ptr); + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + ss_assemblage_save = new cxxSSassemblage(*use.Get_ss_assemblage_ptr()); + add_ss_assemblage(use.Get_ss_assemblage_ptr()); + } +/* + * Check that elements are available for gas components, + * pure phases, and solid solutions + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + gas_phase_check(gas_phase_ptr); + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_check(pp_assemblage_ptr); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + ss_assemblage_check(use.Get_ss_assemblage_ptr()); + } +/* + * Check that element moles are >= zero + */ + if (solution_check() == MASS_BALANCE) + { + /* reset moles and deltas */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + return (MASS_BALANCE); + } +/* + * Copy global into solution n_user = -1 + */ + xsolution_save(-1); + step_save_surf(-1); + step_save_exch(-1); +/* + * Clean up temporary space + */ + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + // + // Solution -1 has sum of solution/mix, exchange, surface, gas_phase + // reaction, kinetics + // + // Determine system totals, calculate maximum mineral precipitation + if (use.Get_pp_assemblage_in() || use.Get_ss_assemblage_in()) + { + cxxStorageBin sys_bin(phrq_io); + cxxSolution *sol = Utilities::Rxn_find(Rxn_solution_map, -1); + cxxSolution soln(*sol); + sys_bin.Set_Solution(-1, soln); + if (use.Get_pp_assemblage_in()) + { + sys_bin.Set_PPassemblage(-1, use.Get_pp_assemblage_ptr()); + } + if (use.Get_ss_assemblage_in()) + { + sys_bin.Set_SSassemblage(-1, *use.Get_ss_assemblage_ptr()); + } + sys_bin.Set_System(-1); + sys_bin.Get_System().totalize(this); + cxxNameDouble sys_tots = sys_bin.Get_System().Get_Totals(); + if (use.Get_pp_assemblage_in()) + { + cxxPPassemblage *pp_assemblage_ptr = sys_bin.Get_PPassemblage(-1); + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int n; + class phase *p_ptr = phase_bsearch((it->first).c_str(), &n, FALSE); + const class elt_list *e_ptr; + LDBLE min = 1e10; + for (e_ptr = &p_ptr->next_elt[0]; e_ptr->elt != NULL; e_ptr++) + { + std::string e(e_ptr->elt->primary->elt->name); + cxxNameDouble::iterator st = sys_tots.find(e.c_str()); + if (st != sys_tots.end()) + { + LDBLE m1 = st->second / e_ptr->coef; + if (m1 < min) min = m1; + } + } + p_ptr->delta_max = min; + } + } + if (use.Get_ss_assemblage_in()) + { + cxxSSassemblage *ss_assemblage_ptr = sys_bin.Get_SSassemblage(-1); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int n; + class phase *p_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &n, FALSE); + + const class elt_list *e_ptr; + LDBLE min = 1e10; + for (e_ptr = &p_ptr->next_elt[0]; e_ptr->elt != NULL; e_ptr++) + { + std::string e(e_ptr->elt->primary->elt->name); + cxxNameDouble::iterator st = sys_tots.find(e.c_str()); + if (st != sys_tots.end()) + { + LDBLE m1 = st->second / e_ptr->coef; + if (m1 < min) + { + min = m1; + } + } + } + p_ptr->delta_max = min; + } + } + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsolution_zero(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Zero out _x variables, master->totals, and species->la + */ + int i; +/* + * Zero totals in master structures + */ + new_x = FALSE; + + tc_x = 0.0; + patm_x = 0; + potV_x = 0; + ph_x = 0.0; + solution_pe_x = 0.0; + mu_x = 0.0; + ah2o_x = 0.0; + density_x = 0.0; + total_h_x = 0.0; + total_o_x = 0.0; + cb_x = 0.0; + mass_water_aq_x = 0.0; + units_x = moles_per_kilogram_string; + + for (i = 0; i < (int)master.size(); i++) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + master[i]->s->la = 0.0; + } + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (i = 0; i < (int)s.size(); i++) + { + s[i]->lg = 0.0; + } + } +/* + * Copy pe data (not sure this will be used + */ +/* + pe_data_free (pe_x); + pe_x = pe_data_alloc(); + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_solution(cxxSolution *solution_ptr, LDBLE extensive, LDBLE intensive) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate solution data in master->totals and _x variables. + * + * extensive is multiplication factor for solution + * intensive is fraction of all multiplication factors for all solutions + */ + class master *master_ptr; + class species *species_ptr; +/* + * Add solution to global variables + */ + tc_x += solution_ptr->Get_tc() * intensive; + ph_x += solution_ptr->Get_ph() * intensive; + patm_x += solution_ptr->Get_patm() * intensive; + //potV_x += solution_ptr->Get_potV() * intensive; + solution_pe_x += solution_ptr->Get_pe() * intensive; + mu_x += solution_ptr->Get_mu() * intensive; + ah2o_x += solution_ptr->Get_ah2o() * intensive; + density_x += solution_ptr->Get_density() * intensive; + + total_h_x += solution_ptr->Get_total_h() * extensive; + total_o_x += solution_ptr->Get_total_o() * extensive; + cb_x += solution_ptr->Get_cb() * extensive; + mass_water_aq_x += solution_ptr->Get_mass_water() * extensive; +/* + * Copy totals data into primary master species + */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch_primary(jit->first.c_str()); + if (master_ptr != NULL) + { + master_ptr->total += jit->second * extensive; + } + else + { + input_error++; + error_msg(sformatf("Undefined element in solution, %s\n", jit->first.c_str()), CONTINUE); + } + } +/* + * Accumulate initial guesses for activities + */ + jit = solution_ptr->Get_master_activity().begin(); + for ( ; jit != solution_ptr->Get_master_activity().end(); jit++) + { + { + master_ptr = master_bsearch(jit->first.c_str()); + if (master_ptr != NULL) + { + master_ptr->s->la += jit->second * intensive; + } + } + } +/* + * Accumulate initial guesses for log gamma + */ + if (pitzer_model == TRUE || sit_model == TRUE) + { + jit = solution_ptr->Get_species_gamma().begin(); + for ( ; jit != solution_ptr->Get_species_gamma().end(); jit++) + { + species_ptr = s_search(jit->first.c_str()); + if (species_ptr != NULL) + { + species_ptr->lg += jit->second * intensive; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_exchange(cxxExchange *exchange_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate exchange data in master->totals and _x variables. + */ + class master *master_ptr; + + if (exchange_ptr == NULL) + return (OK); +/* + * Add element concentrations on exchanger to master species totals + */ + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp comp_ref = exchange_ptr->Get_exchange_comps()[i]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + assert(elt_ptr != NULL && elt_ptr->primary != NULL); + master_ptr = elt_ptr->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_ptr->total += coef; + } + } + } + if (exchange_ptr->Get_new_def()) + { + for (int i = 0; i < (int)master.size(); i++) + { + if (master[i]->type == EX && master[i]->total > 0) + { + master[i]->s->la = log10(0.1 * master[i]->total); + } + } + } + else + { + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp &comp_ref = exchange_ptr->Get_exchange_comps()[i]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + assert(elt_ptr->master); + if (elt_ptr->master->type == EX) + { + elt_ptr->master->s->la = comp_ref.Get_la(); + } + } + cb_x += comp_ref.Get_charge_balance(); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_surface(cxxSurface *surface_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate surface data in master->totals and _x variables. + */ + if (surface_ptr == NULL) + return (OK); +/* + * Add element concentrations on surface to master species totals + */ + dl_type_x = surface_ptr->Get_dl_type(); + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + class element *elt_ptr = element_store(comp_ptr->Get_master_element().c_str()); + if (elt_ptr->master == NULL) + { + error_msg(sformatf("Data not defined for master in SURFACE, %s\n", comp_ptr->Get_formula().c_str()), STOP); + } + class master *master_i_ptr = elt_ptr->master; + + if (surface_ptr->Get_type() == cxxSurface::NO_EDL) + { + cb_x += comp_ptr->Get_charge_balance(); + } + if (!surface_ptr->Get_new_def()) + { + master_i_ptr->s->la = comp_ptr->Get_la(); + } +/* + * Add surface and specifically sorbed elements + */ + cxxNameDouble::iterator jit; + for (jit = comp_ptr->Get_totals().begin(); jit != comp_ptr->Get_totals().end(); jit++) + { + LDBLE coef = jit->second; + class element *elt_j_ptr = element_store(jit->first.c_str()); + class master *master_j_ptr = elt_j_ptr->primary; + if (master_j_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not defined in database, %s.", + elt_j_ptr->name); + error_msg(error_string, STOP); + } + if (master_j_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_j_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_j_ptr->total += coef; + } + } + } + if (surface_ptr->Get_type() != cxxSurface::DDL && surface_ptr->Get_type() != cxxSurface::CCM && surface_ptr->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(surface_ptr->Get_surface_charges()[i]); + if (surface_ptr->Get_type() == cxxSurface::DDL || surface_ptr->Get_type() == cxxSurface::CCM || surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + { + cb_x += charge_ptr->Get_charge_balance(); + } + if (!surface_ptr->Get_new_def()) + { + class master *master_ptr = surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI); + master_ptr->s->la = charge_ptr->Get_la_psi(); + } +/* + * Add diffuse layer elements (including water in Debye layer) + */ + if (surface_ptr->Get_dl_type() != cxxSurface::NO_DL && !surface_ptr->Get_new_def()) + { + cxxNameDouble::const_iterator jit; + for (jit = charge_ptr->Get_diffuse_layer_totals().begin(); jit != charge_ptr->Get_diffuse_layer_totals().end(); jit++) + { + LDBLE coef = jit->second; + class element *elt_j_ptr = element_store(jit->first.c_str()); + class master * master_j_ptr = elt_j_ptr->master; + if (master_j_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_j_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_j_ptr->total += coef; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_mix(cxxMix *mix_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * calls add_solution to accumulate all data in master->totals + * and other variables. + */ + LDBLE sum_fractions, intensive, extensive; + cxxSolution *solution_ptr; + int count_positive; + LDBLE sum_positive; + + if (mix_ptr == NULL) + return (OK); + if (mix_ptr->Get_mixComps().size() == 0) + return (OK); + sum_fractions = 0.0; + sum_positive = 0.0; + count_positive = 0; + std::map::const_iterator it; + for (it = mix_ptr->Get_mixComps().begin(); it != mix_ptr->Get_mixComps().end(); it++) + { + sum_fractions += it->second; + if (it->second > 0) + { + sum_positive += it->second; + count_positive++; + } + } + for (it = mix_ptr->Get_mixComps().begin(); it != mix_ptr->Get_mixComps().end(); it++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, it->first); + if (solution_ptr == NULL) + { + error_string = sformatf( "Mix solution not found, %d.", + it->first); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + extensive = it->second; + intensive = extensive / sum_fractions; + if (count_positive < (int) mix_ptr->Get_mixComps().size()) + { + if (it->second > 0) + { + intensive = extensive / sum_positive; + } + else + { + intensive = 0; + } + } + add_solution(solution_ptr, extensive, intensive); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add a small amount of each phase if necessary to insure + * all elements exist in solution. + */ + int i; + LDBLE amount_to_add, total; + char token[MAX_LENGTH]; + const char* cptr; + class master *master_ptr; + + if (check_pp_assemblage(pp_assemblage_ptr) == OK) + return (OK); +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + count_elts = 0; + paren_count = 0; +/* + * Check that all elements are in solution for phases with greater than zero mass + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + if (comp_ptr->Get_precipitate_only()) continue; + int l; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + amount_to_add = 0.0; + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_add_formula().size() > 0) + { + strcpy(token, comp_ptr->Get_add_formula().c_str()); + cptr = &(token[0]); + get_elts_in_species(&cptr, 1.0); + } + else + { + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + } + if (comp_ptr->Get_moles() > 0.0) + { + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + total = (-master_ptr->total + 1e-10) / elt_list[i].coef; + if (amount_to_add < total) + { + amount_to_add = total; + } + } + } + if (comp_ptr->Get_moles() < amount_to_add) + { + amount_to_add =comp_ptr->Get_moles(); + } + } + if (amount_to_add > 0.0) + { + comp_ptr->Set_moles(comp_ptr->Get_moles() - amount_to_add); + comp_ptr->Set_delta(amount_to_add); +/* + * Add reaction to totals + */ + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[i].coef * amount_to_add; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[i].coef * amount_to_add; + } + else + { + master_ptr->total += elt_list[i].coef * amount_to_add; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +check_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check list of all elements in pure_phase assemblage to see + * if all are in model. Return true if all are present, + * Return false if one or more is missing. + */ + class master *master_ptr; + + cxxNameDouble nd = pp_assemblage_ptr->Get_eltList(); + cxxNameDouble::iterator it; + for (it = nd.begin(); it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->primary == NULL) + { + return FALSE; + } + + master_ptr = elt_ptr->primary; + if (master_ptr->s == s_h2o || master_ptr->s == s_hplus) + continue; + if (master_ptr->total > MIN_TOTAL) + continue; + return (FALSE); + } + return (TRUE); +} + /* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_reaction(cxxReaction *reaction_ptr, int step_number, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add irreversible reaction + */ + char c; + class master *master_ptr; +/* + * Calculate and save reaction + */ +/* !!!!! with kinetics reaction, coeff's may change + * and reaction_calc must be called .... + */ + if (reaction_ptr == NULL) + return OK; + + reaction_calc(reaction_ptr); + +/* + * Step size + */ + if (incremental_reactions == FALSE) + { + if (!reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0 ) + { + if (step_number > (int) reaction_ptr->Get_steps().size()) + { + step_x = reaction_ptr->Get_steps()[reaction_ptr->Get_steps().size() - 1]; + } + else + { + step_x = reaction_ptr->Get_steps()[(size_t)step_number - 1]; + } + } + else if (reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = reaction_ptr->Get_steps()[0]; + } + else + { + step_x = reaction_ptr->Get_steps()[0] * + ((LDBLE) step_number) / + ((LDBLE) (reaction_ptr->Get_reaction_steps())); + } + } + else + { + step_x = 0.0; + } + } + else + { + /* Incremental reactions */ + if (!reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = reaction_ptr->Get_steps()[(size_t)reaction_ptr->Get_reaction_steps() - 1]; + } + else + { + step_x = reaction_ptr->Get_steps()[(size_t)step_number - 1]; + } + } + else if (reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = 0; + } + else + { + step_x = reaction_ptr->Get_steps()[0] / ((LDBLE) (reaction_ptr->Get_reaction_steps())); + } + } + else + { + step_x = 0.0; + } + } +/* + * Convert units + */ + c = reaction_ptr->Get_units().c_str()[0]; + if (c == 'm') + { + step_x *= 1e-3; + } + else if (c == 'u') + { + step_x *= 1e-6; + } + else if (c == 'n') + { + step_x *= 1e-9; + } +/* + * Add reaction to totals + */ + cxxNameDouble::const_iterator it = reaction_ptr->Get_elementList().begin(); + for ( ; it != reaction_ptr->Get_elementList().end(); it++) + { + class element * elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (elt_ptr == NULL) + { + assert (false); + } + else + { + master_ptr = elt_ptr->primary; + if (master_ptr == NULL) + { + // error msg has been called in reaction_calc + continue; + } + if (master_ptr->s == s_hplus) + { + total_h_x += coef * step_x * step_fraction; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef * step_x * step_fraction; + } + else + { + master_ptr->total += coef * step_x * step_fraction; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reaction_calc(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through irreversible reaction initially to + * determine a list of elements and amounts in + * the reaction. + */ + int return_value; + LDBLE coef; + const char* cptr; + class phase *phase_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + return_value = OK; + count_elts = 0; + paren_count = 0; + + cxxNameDouble nd(reaction_ptr->Get_reactantList()); + cxxNameDouble::iterator it; + for (it = nd.begin(); it != nd.end(); it++) + { + coef = it->second; + int j; + phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); +/* + * Reactant is a pure phase, copy formula into token + */ + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, coef); + } + else + { + cptr = it->first.c_str(); + get_elts_in_species(&cptr, coef); + } + } +/* + * Check that all elements are in database + */ + for (int i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master == NULL) + { + error_string = sformatf( + "Element or phase not defined in database, %s.", + elt_list[i].elt->name); + error_msg(error_string, CONTINUE); + input_error++; + return_value = ERROR; + } + } + reaction_ptr->Set_elementList(elt_list_NameDouble()); + + return (return_value); +} + /* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_gas_phase(cxxGasPhase *gas_phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate gas data in master->totals and _x variables. + */ + int i; + class master *master_ptr; + + if (gas_phase_ptr == NULL) + return (OK); +/* + * calculate reaction + */ + count_elts = 0; + paren_count = 0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_msg(sformatf("PHASE not found in database, %s\n", gc_ptr->Get_phase_name().c_str()), CONTINUE); + } + //assert(phase_ptr); + else + { + add_elt_list(phase_ptr->next_elt, gc_ptr->Get_moles()); + } + } +/* + * Sort elements in reaction and combine + */ + elt_list_combine(); +/* + * Add gas elements to totals + */ + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[i].coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[i].coef; + } + else + { + master_ptr->total += elt_list[i].coef; + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE && fabs(gas_phase_ptr->Get_total_p() - patm_x) > 0.01) + { + patm_x = gas_phase_ptr->Get_total_p(); + k_temp(tc_x, patm_x); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_ss_assemblage(cxxSSassemblage *ss_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate solid_solution data in master->totals and _x variables. + */ + int i, j, k; + LDBLE amount_to_add, total; + class master *master_ptr; + const char* cptr; + + if (ss_assemblage_ptr == NULL) + return (OK); + count_elts = 0; + paren_count = 0; +/* + * Check that all elements are in solution for phases with greater than zero mass + */ + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (i = 0; i < (int) ss_ptrs.size(); i++) + { + cxxSS * ss_ptr = ss_ptrs[i]; + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int) ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + class phase * phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + + amount_to_add = 0.0; + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_moles() > 0.0) + { + cptr = phase_ptr->formula; + count_elts = 0; // appt + get_elts_in_species(&cptr, 1.0); + for (k = 0; k < count_elts; k++) + { + master_ptr = elt_list[k].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL_SS) + { + continue; + } + else + { + total = + (-master_ptr->total + 1e-10) / elt_list[k].coef; + if (amount_to_add < total) + { + amount_to_add = total; + } + } + } + } + if (comp_ptr->Get_moles() < amount_to_add) + { + amount_to_add = comp_ptr->Get_moles(); + } + if (amount_to_add > 0.0) + { + comp_ptr->Set_moles(comp_ptr->Get_moles() - amount_to_add); + comp_ptr->Set_delta(amount_to_add); +/* + * Add reaction to totals + */ + for (k = 0; k < count_elts; k++) + { + master_ptr = elt_list[k].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[k].coef * amount_to_add; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[k].coef * amount_to_add; + } + else + { + master_ptr->total += elt_list[k].coef * amount_to_add; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_kinetics(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add kinetic reaction + */ + class master *master_ptr = NULL; +/* + * Add reaction to totals + */ + if (kinetics_ptr->Get_totals().size() == 0) + return (OK); + cxxNameDouble::iterator it = kinetics_ptr->Get_totals().begin(); + for (; it != kinetics_ptr->Get_totals().end(); it++) + { + LDBLE coef = it->second; + class element *elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || (master_ptr = elt_ptr->primary) == NULL) + { + input_error++; + error_string = sformatf( + "Element %s in kinetic reaction not found in database.", + it->first.c_str()); + error_msg(error_string, STOP); + } + else + { + if (master_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_ptr->total += coef; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gas_phase_check(cxxGasPhase *gas_phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + class master *master_ptr; + + if (gas_phase_ptr == NULL) + return (OK); +// set gas pressure to reaction_pressure... + if (use.Get_pressure_ptr() != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + gas_phase_ptr->Set_total_p(patm_x); + k_temp(tc_x, patm_x); + } + +/* + * Check that all elements are in solution for phases with zero mass + */ + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + count_elts = 0; + paren_count = 0; + if (gc_ptr->Get_moles() <= 0.0) + { + add_elt_list(phase_ptr->next_elt, 1.0); + for (int j = 0; j < count_elts; j++) + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in gas %s (which has 0.0 mass),\nbut is not in solution or other phases.", + elt_list[j].elt->name, + phase_ptr->name); + warning_msg(error_string); + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pp_assemblage_check(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + std::string token; + const char* cptr; + class master *master_ptr; + + if (check_pp_assemblage(pp_assemblage_ptr) == OK) + return (OK); +/* + * Check that all elements are in solution for phases with zero mass + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + int l; + class phase * phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_moles() <= 0.0) + { + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_add_formula().size() > 0) + { + token = comp_ptr->Get_add_formula(); + cptr = &(token[0]); + get_elts_in_species(&cptr, 1.0); + } + else + { + token = phase_ptr->formula; + add_elt_list(phase_ptr->next_elt, 1.0); + } + for (int i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in %s (which has 0.0 mass)," + "\t\nbut is not in solution or other phases.", + elt_list[i].elt->name, + phase_ptr->name); + warning_msg(error_string); + } +/* + * Make la's of all master species for the element small, so SI will be small + * and no mass transfer will be calculated + */ + for (int k = 0; k < (int)master.size(); k++) + { + if (master[k]->elt->primary == master_ptr) + { + master[k]->s->la = -9999.999; + } + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_assemblage_check(cxxSSassemblage *ss_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + int j, k; + class master *master_ptr; + + if (ss_assemblage_ptr == NULL) + return (OK); +/* + * Check that all elements are in solution for phases with zero mass + */ + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (k = 0; k < (int) ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_moles() <= 0.0) + { + add_elt_list(phase_ptr->next_elt, 1.0); + for (l = 0; l < count_elts; l++) + { + master_ptr = elt_list[l].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL_SS) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in solid solution %s (which has 0.0 mass),\nbut is not in solution or other phases.", + elt_list[l].elt->name, + phase_ptr->name); + warning_msg(error_string); + } + } + /* + * Make la's of all master species for the element small, + * so SI will be small + * and no mass transfer will be calculated + */ + for (k = 0; k < (int)master.size(); k++) + { + if (master[k]->elt->primary == master_ptr) + { + master[k]->s->la = -9999.999; + } + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solution_check(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + int i; + class master *master_ptr; + +/* + * Check that all elements are in solution for phases with zero mass + */ + for (i = 0; i < (int)master.size(); i++) + { + master_ptr = master[i]; + if (master_ptr->total <= MIN_TOTAL && master_ptr->total >= -MIN_TOTAL) + { + master_ptr->total = 0; + continue; + } + if (master_ptr->total >= 0.0) + continue; + //if (master_ptr->total > -MIN_TOTAL) + //{ + // master_ptr->total = 0; + // continue; + //} + if (master_ptr->s == s_eminus || master_ptr->s == s_h2o + || master_ptr->s == s_hplus || master_ptr->s == s_h3oplus) + { + master_ptr->total = 0; + continue; + } + /* + sprintf (error_string, + "Element %s has negative moles in solution, %e. \n\tErroneous mole balance occurs as moles are added to produce zero moles.\n\tUsually caused by KINETICS, REACTION, or diffuse layer calculation.\n\tMay be due to large time steps in early part of KINETICS simulation or negative concentrations in the diffuse layer.", + master_ptr->elt->name, (LDBLE) master_ptr->total); + */ + if (state != TRANSPORT) + { + error_string = sformatf( + "Negative moles in solution %d for %s, %e. Recovering...", + cell_no, master_ptr->elt->name, (double)master_ptr->total); + warning_msg(error_string); + } + return (MASS_BALANCE); + } + return (OK); +} diff --git a/phreeqcpp/structures.cpp b/phreeqcpp/structures.cpp new file mode 100644 index 00000000..ac1eaa40 --- /dev/null +++ b/phreeqcpp/structures.cpp @@ -0,0 +1,3379 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include + +#include "phqalloc.h" +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Use.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Surface.h" +#include "Solution.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +clean_up(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Free all allocated memory, except strings + */ + int i, j; +#if defined MULTICHART + chart_handler.End_timer(); + output_flush(); +#if 0 + // Wait for charts to end + while (0 != this->chart_handler.Get_active_charts()) + { + System::Threading::Thread::Sleep(60); + } +#endif +#endif + + isotopes_x.clear(); + /* model */ + last_model.gas_phase.clear(); + last_model.pp_assemblage.clear(); + last_model.add_formula.clear(); + last_model.si.clear(); + last_model.ss_assemblage.clear(); + last_model.surface_comp.clear(); + last_model.surface_charge.clear(); + /* model */ + free_model_allocs(); + + /* species */ + + for (j = 0; j < (int)s.size(); j++) + { + s_free(s[j]); + delete s[j]; + } + s.clear(); + + /* master species */ + + for (j = 0; j < (int)master.size(); j++) + { + master_free(master[j]); + } + master.clear(); + + /* elements */ + + for (j = 0; j < (int)elements.size(); j++) + { + delete elements[j]; + } + elements.clear(); + /* solutions */ + Rxn_solution_map.clear(); + /* surfaces */ + Rxn_surface_map.clear(); + /* exchange */ + Rxn_exchange_map.clear(); + /* pp assemblages */ + Rxn_pp_assemblage_map.clear(); + /* s_s assemblages */ + Rxn_ss_assemblage_map.clear(); + /* irreversible reactions */ + Rxn_reaction_map.clear(); + /* temperature */ + Rxn_temperature_map.clear(); + /* pressure */ + Rxn_pressure_map.clear(); + /* unknowns */ + for (j = 0; j < (int)x.size(); j++) + { + unknown_free(x[j]); + } + x.clear(); + /* mixtures */ + Rxn_mix_map.clear(); + /* phases */ + for (j = 0; j < (int)phases.size(); j++) + { + phase_free(phases[j]); + delete phases[j]; + } + phases.clear(); + /* inverse */ + for (j = 0; j < count_inverse; j++) + { + inverse_free(&(inverse[j])); + } + inverse.clear(); + /* gases */ + Rxn_gas_phase_map.clear(); + /* kinetics */ + Rxn_kinetics_map.clear(); + x0_moles.clear(); + m_temp.clear(); + m_original.clear(); + rk_moles.clear(); + /* rates */ + for (j = 0; j < (int)rates.size(); j++) + { + rate_free(&rates[j]); + } + rates.clear(); + /* logk table */ + for (j = 0; j < (int)logk.size(); j++) + { + logk[j]->add_logk.clear(); + delete logk[j]; + } + logk.clear(); + save_values.clear(); + /* working pe*/ + pe_x.clear(); + /*species_list*/ + species_list.clear(); + /* transport data */ + cell_data.clear(); + /* advection */ + advection_punch.clear(); + advection_print.clear(); + /* selected_output */ + SelectedOutput_map.clear(); + /* user_print and user_punch */ + UserPunch_map.clear(); + rate_free(user_print); + delete user_print; + /* + Clear llnl aqueous model parameters + */ + llnl_temp.clear(); + llnl_adh.clear(); + llnl_bdh.clear(); + llnl_bdot.clear(); + llnl_co2_coefs.clear(); + /* master_isotope */ + for (i = 0; i < (int)master_isotope.size(); i++) + { + delete master_isotope[i]; + } + master_isotope.clear(); + master_isotope_map.clear(); + /* calculate_value */ + for (i = 0; i < (int)calculate_value.size(); i++) + { + calculate_value_free(calculate_value[i]); + delete calculate_value[i]; + } + calculate_value.clear(); + calculate_value_map.clear(); + /* isotope_ratio */ + for (i = 0; i < (int)isotope_ratio.size(); i++) + { + delete isotope_ratio[i]; + } + isotope_ratio.clear(); + isotope_ratio_map.clear(); + /* isotope_alpha */ + for (i = 0; i < (int)isotope_alpha.size(); i++) + { + delete isotope_alpha[i]; + } + isotope_alpha.clear(); + isotope_alpha_map.clear(); + /* tally table */ + free_tally_table(); + /* CVODE memory */ + free_cvode(); + /* pitzer */ + pitzer_clean_up(); + /* sit */ + sit_clean_up(); + /* elements, species, phases*/ + elements_map.clear(); + species_map.clear(); + phases_map.clear(); + logk_map.clear(); + /* strings */ + strings_map_clear(); + /* delete basic interpreter */ + basic_free(); + /* change_surf */ + change_surf = (struct Change_Surf *) free_check_null(change_surf); + /* miscellaneous work space */ + elt_list.clear(); + trxn.token.clear(); + mb_unknowns.clear(); + line = (char *) free_check_null(line); + line_save = (char *) free_check_null(line_save); + /* free user database name if defined */ + dump_file_name = (char *) free_check_null(dump_file_name); +#ifdef PHREEQCI_GUI + free_spread(); +#endif + title_x.clear(); + last_title_x.clear(); + count_inverse = 0; + + sformatf_buffer = (char *) free_check_null(sformatf_buffer); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reinitialize(void) +/* ---------------------------------------------------------------------- */ +{ + /* solutions */ + Rxn_solution_map.clear(); + /* surfaces */ + Rxn_surface_map.clear(); + /* exchange */ + Rxn_exchange_map.clear(); + /* pp assemblages */ + Rxn_pp_assemblage_map.clear(); + /* s_s assemblages */ + Rxn_ss_assemblage_map.clear(); + /* gases */ + Rxn_gas_phase_map.clear(); + /* kinetics */ + Rxn_kinetics_map.clear(); + /* irreversible reactions */ + Rxn_reaction_map.clear(); + // Temperature + Rxn_temperature_map.clear(); + // Pressure + Rxn_pressure_map.clear(); + return (OK); +} +/* ********************************************************************** + * + * Routines related to CReaction + * + * ********************************************************************** */ +CReaction::CReaction(void) +{ + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) this->logk[i] = 0.0; + for (size_t i = 0; i < 3; i++) this->dz[i] = 0.0; +} +CReaction::CReaction(size_t ntoken) +{ + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) this->logk[i] = 0.0; + for (size_t i = 0; i < 3; i++) this->dz[i] = 0.0; + this->token.resize(ntoken); +} +void CReaction::Set_logk(double* d) +{ + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++)logk[i] = d[i]; +} +void CReaction::Set_dz(double* d) +{ + for (size_t i = 0; i < 3; i++) dz[i] = d[i]; +} +CReaction Phreeqc::CReaction_internal_copy(CReaction& rxn_ref) +{ + CReaction rxn; + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) rxn.logk[i] = rxn_ref.logk[i]; + for (size_t i = 0; i < 3; i++) rxn.dz[i] = rxn_ref.dz[i]; + rxn.Get_tokens().resize(rxn_ref.Get_tokens().size()); + for (size_t i = 0; i < rxn_ref.Get_tokens().size(); i++) + { + rxn.token[i].s = (rxn_ref.token[i].s == NULL) ? NULL : + s_store(rxn_ref.token[i].s->name, rxn_ref.token[i].s->z, false); + rxn.token[i].coef = rxn_ref.token[i].coef; + rxn.token[i].name = (rxn_ref.token[i].name == NULL) ? NULL : + string_hsave(rxn_ref.token[i].name); + } + return rxn; +} +/* ---------------------------------------------------------------------- */ +double Phreeqc:: +rxn_find_coef(CReaction& r_ref, const char* str) +/* ---------------------------------------------------------------------- */ +{ + /* + * Finds coefficient of token in reaction. + * input: r_ptr, pointer to a reaction structure + * str, string to find as reaction token + * + * Return: 0.0, if token not found + * coefficient of token, if found. + */ + class rxn_token* r_token; + LDBLE coef; + + r_token = &r_ref.token[1]; + coef = 0.0; + while (r_token->s != NULL) + { + if (strcmp(r_token->s->name, str) == 0) + { + coef = r_token->coef; + break; + } + r_token++; + } + return (coef); +} +/* ********************************************************************** + * + * Routines related to structure "element" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +element_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class element *element_ptr1, *element_ptr2; + element_ptr1 = *(const class element **) ptr1; + element_ptr2 = *(const class element **) ptr2; +/* return(strcmp_nocase(element_ptr1->name, element_ptr2->name)); */ + return (strcmp(element_ptr1->name, element_ptr2->name)); + +} + +/* ---------------------------------------------------------------------- */ +class element* Phreeqc:: +element_store(const char * element) +/* ---------------------------------------------------------------------- */ +{ + /* + * Function locates the string "element" in the map for elements. + * + * If found, pointer to the appropriate element structure is returned. + * + * If the string is not found, a new entry is made at the end of + * the elements array (position count_elements) and count_elements is + * incremented. Pointer to the new structure is returned. + * + * Arguments: + * element input, std::string to be located or stored. + * + * Returns: + * The address of an elt structure that contains the element data. + */ + /* + * Search list + */ + std::map::const_iterator it; + it = elements_map.find(element); + if (it != elements_map.end()) + { + return (it->second); + } + /* + * Save new element structure and return pointer to it + */ + class element *elt_ptr = new class element; + elt_ptr->name = string_hsave(element); + elt_ptr->master = NULL; + elt_ptr->primary = NULL; + elt_ptr->gfw = 0.0; + elements.push_back(elt_ptr); + elements_map[element] = elt_ptr; + return (elt_ptr); +} +/* ********************************************************************** + * + * Routines related to structure "elt_list" + * + * ********************************************************************** */ + /* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_elt_list(const cxxNameDouble& nd, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::const_iterator cit = nd.begin(); + for (; cit != nd.end(); cit++) + { + if (count_elts >= (int)elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + elt_list[count_elts].elt = element_store(cit->first.c_str()); + elt_list[count_elts].coef = cit->second * coef; + count_elts++; + } + return (OK); +} +int Phreeqc:: +add_elt_list(const std::vector& el, double coef) +/* ---------------------------------------------------------------------- */ +{ + const class elt_list* elt_list_ptr = &el[0]; + + for (; elt_list_ptr->elt != NULL; elt_list_ptr++) + { + if (count_elts >= elt_list.size()) + { + elt_list.resize(count_elts + 1); + } + elt_list[count_elts].elt = elt_list_ptr->elt; + elt_list[count_elts].coef = elt_list_ptr->coef * coef; + count_elts++; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +change_hydrogen_in_elt_list(LDBLE charge) +/* ---------------------------------------------------------------------- */ +{ + int j; + int found_h, found_o; + LDBLE coef_h, coef_o, coef; + found_h = -1; + found_o = -1; + coef_h = 0.0; + coef_o = 0.0; + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + found_h = j; + coef_h = elt_list[j].coef; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + found_o = j; + coef_o = elt_list[j].coef; + } + } + coef = coef_h - 2 * coef_o - charge; + if (found_h < 0 && found_o < 0) + return (OK); + if (found_h >= 0 && found_o < 0) + return (OK); + if (found_h < 0 && found_o >= 0) + { + elt_list[count_elts].elt = s_hplus->primary->elt; + elt_list[count_elts].coef = coef; + count_elts++; + elt_list_combine(); + return (OK); + } + elt_list[found_h].coef = coef; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_combine(void) +/* ---------------------------------------------------------------------- */ +/* + * Function goes through the list of elements pointed to by elt_list + * and combines the coefficients of elements that are the same. + * Assumes elt_list has been sorted by element name. + */ +{ + int i, j; + + //if (count_elts < 1) + //{ + // output_msg("elt_list_combine: How did this happen?\n"); + // return (ERROR); + //} + if (count_elts <= 1) + { + return (OK); + } + qsort(&elt_list[0], count_elts, + sizeof(class elt_list), Phreeqc::elt_list_compare); + j = 0; + for (i = 1; i < count_elts; i++) + { + if (elt_list[i].elt == elt_list[j].elt) + { + elt_list[j].coef += elt_list[i].coef; + } + else + { + j++; + if (i != j) + { + elt_list[j].elt = elt_list[i].elt; + elt_list[j].coef = elt_list[i].coef; + } + } + } + count_elts = j + 1; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_compare(const void* ptr1, const void* ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class elt_list* a, * b; + + a = (const class elt_list*)ptr1; + b = (const class elt_list*)ptr2; + return (strncmp(a->elt->name, b->elt->name, MAX_LENGTH)); +} +/* ---------------------------------------------------------------------- */ +std::vector Phreeqc:: +elt_list_internal_copy(const std::vector& el) +/* ---------------------------------------------------------------------- */ +{ + std::vector new_elt_list; + if (el.size() == 0) return new_elt_list; + const class elt_list* elt_list_ptr = &el[0]; + + new_elt_list.resize(el.size()); + size_t count = 0; + for (; elt_list_ptr->elt != NULL; elt_list_ptr++) + { + new_elt_list[count].elt = element_store(elt_list_ptr->elt->name); + new_elt_list[count].coef = elt_list_ptr->coef; + count++; + } + new_elt_list[count].elt = NULL; + return new_elt_list; +} +/* ---------------------------------------------------------------------- */ +std::vector Phreeqc:: +elt_list_vsave(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Takes data from work space elt_list, allocates a new elt_list structure, + * copies data from work space to new structure, and returns pointer to + * new structure. + */ + size_t j; + std::vector new_elt_list; + /* + * Sort elements in reaction and combine + */ + elt_list_combine(); + /* + * Malloc space and store element data + */ + new_elt_list.resize(count_elts + 1); + for (j = 0; j < count_elts; j++) + { + new_elt_list[j].elt = elt_list[j].elt; + new_elt_list[j].coef = elt_list[j].coef; + } + new_elt_list[count_elts].elt = NULL; + return new_elt_list; +} + +/* ---------------------------------------------------------------------- */ +cxxNameDouble Phreeqc:: +elt_list_NameDouble(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Takes data from work space elt_list, makes NameDouble + */ + cxxNameDouble nd; + for (int i = 0; i < count_elts; i++) + { + nd.add(elt_list[i].elt->name, elt_list[i].coef); + } + return (nd); +} +/* ********************************************************************** + * + * Routines related to structure "inverse" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class inverse * Phreeqc:: +inverse_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space for a new inverse structure at position count_inverse. + * Initializes structure. + * arguments + * input: none + * output: pointer to an inverse structure + * return: OK + */ +{ + class inverse *inverse_ptr = NULL; + inverse.resize(count_inverse + 1); + inverse_ptr = &(inverse[count_inverse++]); +/* + * Initialize variables + */ + inverse_ptr->description = NULL; + inverse_ptr->count_solns = 0; +/* + * allocate space for pointers in structure to NULL + */ + inverse_ptr->count_solns = 0; + + return (inverse_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare inverse values for n_user + */ + const class inverse *nptr1; + const class inverse *nptr2; + + nptr1 = (const class inverse *) ptr1; + nptr2 = (const class inverse *) ptr2; + if (nptr1->n_user > nptr2->n_user) + return (1); + if (nptr1->n_user < nptr2->n_user) + return (-1); + return (0); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Deletes inverse i from list (i is not user number), + * Frees memory allocated to inverse struct + * Input: i, number of inverse struct to delete + * Return: OK + */ + inverse_free(&(inverse[i])); + inverse.erase(inverse.begin() + (size_t)i); + count_inverse--; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_free(class inverse *inverse_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all memory for an inverse structure. + */ + int i; + + inverse_ptr->description = + (char *) free_check_null(inverse_ptr->description); +/* Free solns */ + inverse_ptr->solns.clear(); + +/* Free uncertainties */ + inverse_ptr->uncertainties.clear(); + inverse_ptr->ph_uncertainties.clear(); + +/* Free force_solns */ + inverse_ptr->force_solns.clear(); + +/* Free elts */ + for (i = 0; i < inverse_ptr->elts.size(); i++) + { + inverse_ptr->elts[i].uncertainties.clear(); + }; + inverse_ptr->elts.clear(); + +/* Free isotopes */ + for (i = 0; i < inverse_ptr->isotopes.size(); i++) + { + inverse_ptr->isotopes[i].uncertainties.clear(); + }; + inverse_ptr->isotopes.clear(); + + for (i = 0; i < inverse_ptr->i_u.size(); i++) + { + inverse_ptr->i_u[i].uncertainties.clear(); + }; + inverse_ptr->i_u.clear(); + +/* Free phases */ + for (i = 0; i < inverse_ptr->phases.size(); i++) + { + inverse_ptr->phases[i].isotopes.clear(); + } + inverse_ptr->phases.clear(); + +/* Free carbon derivatives */ + inverse_ptr->dalk_dph.clear(); + inverse_ptr->dalk_dc.clear(); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_isotope_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int i; + const class inv_isotope *iso_ptr1, *iso_ptr2; + + iso_ptr1 = (const class inv_isotope *) ptr1; + iso_ptr2 = (const class inv_isotope *) ptr2; + i = strcmp_nocase(iso_ptr1->elt_name, iso_ptr2->elt_name); + if (i != 0) + return (i); + if (iso_ptr1->isotope_number < iso_ptr2->isotope_number) + { + return (-1); + } + else if (iso_ptr1->isotope_number > iso_ptr2->isotope_number) + { + return (1); + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +class inverse * Phreeqc:: +inverse_search(int n_user, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* Linear search of the structure array "inverse" for user number n_user. + * + * Arguments: + * n_user input, user number + * n output, position in inverse + * + * Returns: + * if found, the address of the inverse element + * if not found, NULL + * + */ + int i; + for (i = 0; i < count_inverse; i++) + { + if (inverse[i].n_user == n_user) + { + *n = i; + return (&(inverse[i])); + } + } +/* + * An inverse structure with n_user was not found + */ + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort array of inverse structures + */ + if (count_inverse > 1) + { + qsort(&inverse[0], (size_t) count_inverse, + sizeof(class inverse), inverse_compare); + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "master" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +master_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a master structure and initializes the space. + * arguments: void + * return: pointer to a master structure + */ +{ + class master *ptr = new class master; +/* + * set pointers in structure to NULL + */ + ptr->in = FALSE; + ptr->number = -1; + ptr->last_model = -1; + ptr->type = 0; + ptr->primary = FALSE; + ptr->coef = 0.0; + ptr->total = 0.0; + ptr->isotope_ratio = 0; + ptr->isotope_ratio_uncertainty = 0; + ptr->isotope = 0; + ptr->total_primary = 0; + ptr->elt = NULL; + ptr->alk = 0.0; + ptr->gfw = 0.0; + ptr->gfw_formula = NULL; + ptr->unknown = NULL; + ptr->s = NULL; + ptr->pe_rxn = NULL; + ptr->minor_isotope = FALSE; + return (ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_delete(const char* cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete master species: Free memory of master species structure, free + * the structure, and remove from array master. + * + * Input + * ptr character string with name of element or valence state + * Returns + * TRUE if master species was deleted. + * FALSE if master species was not found. + */ + int n; + + if (master_search(cptr, &n) == NULL) + return (FALSE); + master_free(master[n]); + master.erase(master.begin() + n); + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_free(class master *master_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free memory pointed to by master species pointer, master_ptr. + * Frees master_ptr itself. + */ + if (master_ptr == NULL) + return (ERROR); + delete master_ptr; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +master_bsearch(const char* cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Uses binary search. Assumes master is in sort order. + * Find master species for string (*cptr) containing name of element or valence state. + * + * Input: cptr pointer to string containing element name + * + * Return: pointer to master structure containing name cptr or NULL. + */ + void *void_ptr; + if (master.size() == 0) + { + return (NULL); + } + void_ptr = bsearch((const char *) cptr, + (char *) &master[0], + master.size(), + sizeof(class master *), master_compare_string); + if (void_ptr == NULL) + { + void_ptr = bsearch(cptr, + (char*)&master[0], + master.size(), + sizeof(class master*), master_compare_string); + } + if (void_ptr == NULL) + { + return (NULL); + } + else + { + return (*(class master **) void_ptr); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *string_ptr; + const class master *master_ptr; + + string_ptr = (const char *) ptr1; + master_ptr = *(const class master **) ptr2; + return (strcmp_nocase(string_ptr, master_ptr->elt->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class master *master_ptr1, *master_ptr2; + master_ptr1 = *(const class master **) ptr1; + master_ptr2 = *(const class master **) ptr2; + return (strcmp_nocase(master_ptr1->elt->name, master_ptr2->elt->name)); +} + +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +master_bsearch_primary(const char* cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find primary master species for first element in the string, cptr. + * Uses binary search. Assumes master is in sort order. + */ + int l; + const char* cptr1; + class master *master_ptr_primary; +/* + * Find element name + */ + cptr1 = cptr; + { + std::string elt; + get_elt(&cptr1, elt, &l); + /* + * Search master species list + */ + master_ptr_primary = master_bsearch(elt.c_str()); + } + if (master_ptr_primary == NULL) + { + input_error++; + error_string = sformatf( + "Could not find primary master species for %s.", cptr); + error_msg(error_string, CONTINUE); + } + return (master_ptr_primary); +} +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +master_bsearch_secondary(const char* cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find secondary master species that corresponds to the primary master species. + * i.e. S(6) for S. + */ + int l; + const char* cptr1; + std::string elt; + class master *master_ptr_primary, *master_ptr=NULL, *master_ptr_secondary=NULL; +/* + * Find element name + */ + cptr1 = cptr; + get_elt(&cptr1, elt, &l); +/* + * Search master species list + */ + master_ptr_primary = master_bsearch(elt.c_str()); + if (master_ptr_primary == NULL) + { + input_error++; + error_string = sformatf( + "Could not find primary master species for %s.", cptr); + error_msg(error_string, CONTINUE); + } +/* + * If last in list or not redox +*/ + if (master_ptr_primary) + { + if ((master_ptr_primary->number >= (int)master.size() - 1) || + (master[(size_t)master_ptr_primary->number + 1]->elt->primary != master_ptr_primary)) + { + return(master_ptr_primary); + } + /* + * Find secondary master with same species as primary + */ + master_ptr = NULL; + for (size_t j = master_ptr_primary->number + 1; j < master.size(); j++) + { + if (master[j]->s == master_ptr_primary->s) + { + master_ptr = master[j]; + } + } + } +/* + * + */ + if (master_ptr != NULL && master_ptr->elt != NULL && (master_ptr->elt->primary == master_ptr_primary)) + { + master_ptr_secondary = master_ptr; + } + else + { + input_error++; + error_string = sformatf( + "Could not find secondary master species for %s.", cptr); + error_msg(error_string, STOP); + } + + + return (master_ptr_secondary); +} +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +master_search(const char* cptr, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Linear search of master to find master species in string, cptr. + * Returns pointer if found. n contains position in array master. + * Returns NULL if not found. + */ + int i; + class master *master_ptr; +/* + * Search master species list + */ + *n = -999; + for (i = 0; i < (int)master.size(); i++) + { + if (strcmp(cptr, master[i]->elt->name) == 0) + { + *n = i; + master_ptr = master[i]; + return (master_ptr); + } + } + return (NULL); +} +/* ********************************************************************** + * + * Routines related to structure "phases" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class phase * Phreeqc:: +phase_alloc(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space to a phase structure and initializes + * arguments: void + * return: pointer to new phase structure + */ + class phase *phase_ptr; +/* + * Allocate space + */ + phase_ptr = new class phase; +/* + * Initialize space + */ + phase_init(phase_ptr); + return (phase_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares names of phases for sort + */ + const class phase *phase_ptr1, *phase_ptr2; + phase_ptr1 = *(const class phase **) ptr1; + phase_ptr2 = *(const class phase **) ptr2; + return (strcmp_nocase(phase_ptr1->name, phase_ptr2->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *char_ptr; + const class phase *phase_ptr; + char_ptr = (const char *) ptr1; + phase_ptr = *(const class phase **) ptr2; + return (strcmp_nocase(char_ptr, phase_ptr->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Deletes phase i from list, phases + * Frees memory allocated to phase[i] and renumbers phases to remove number i. + * Input: i, number of phase + * Return: OK + */ + phase_free(phases[i]); + phases.erase(phases.begin() + (size_t)i); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_free(class phase *phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within phase[i], does not free phase structure + * Input: i, number of phase + * Return: OK + */ + if (phase_ptr == NULL) + return (ERROR); + phase_ptr->next_elt.clear(); + phase_ptr->next_sys_total.clear();; + phase_ptr->add_logk.clear(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class phase * Phreeqc:: +phase_bsearch(const char* cptr, int *j, int print) +/* ---------------------------------------------------------------------- */ +{ +/* Binary search the structure array "phases" for a name that is equal to + * cptr. Assumes array phases is in sort order. + * + * Arguments: + * name input, a character string to be located in phases. + * j index number in array phases. + * + * Returns: + * if found, pointer to phase structure. + * if not found, NULL + * + */ + void *void_ptr; + + void_ptr = NULL; + if ((int)phases.size() > 0) + { + void_ptr = (void *) + bsearch((char *) cptr, + (char *) &phases[0], + phases.size(), + sizeof(class phase *), phase_compare_string); + } + if (void_ptr == NULL && print == TRUE) + { + error_string = sformatf( "Could not find phase in list, %s.", cptr); + error_msg(error_string, CONTINUE); + } + + if (void_ptr == NULL) + { + *j = -1; + return (NULL); + } + + *j = (int) ((class phase **) void_ptr - &phases[0]); + return (*(class phase **) void_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +phase_init(class phase *phase_ptr) +/* ---------------------------------------------------------------------- */ +/* + * set pointers in phase structure to NULL + */ +{ + int i; + + phase_ptr->name = NULL; + phase_ptr->formula = NULL; + phase_ptr->in = FALSE; + phase_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + phase_ptr->logk[i] = 0.0; + phase_ptr->original_units = kjoules; + phase_ptr->add_logk.clear(); + phase_ptr->moles_x = 0; + phase_ptr->delta_max = 0; + phase_ptr->p_soln_x = 0; + phase_ptr->fraction_x = 0; + phase_ptr->log10_lambda = 0; + phase_ptr->log10_fraction_x = 0; + phase_ptr->dn = 0; + phase_ptr->dnb = 0; + phase_ptr->dnc = 0; + phase_ptr->gn = 0; + phase_ptr->gntot = 0; + phase_ptr->t_c = 0.0; + phase_ptr->p_c = 0.0; + phase_ptr->omega = 0.0; + phase_ptr->pr_a = 0.0; + phase_ptr->pr_b = 0.0; + phase_ptr->pr_alpha = 0.0; + phase_ptr->pr_tk = 0; + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1.0; + phase_ptr->pr_aa_sum2 = 0; + for (i = 0; i < 9; i++) + phase_ptr->delta_v[i] = 0.0; + phase_ptr->pr_si_f = 0; + phase_ptr->pr_in = false; + phase_ptr->type = SOLID; + phase_ptr->check_equation = TRUE; + phase_ptr->replaced = 0; + phase_ptr->in_system = 1; + phase_ptr->original_deltav_units = cm3_per_mol; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class phase * Phreeqc:: +phase_store(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for phases. + * + * If found, pointer to the appropriate phase structure is returned. + * + * If the string is not found, a new entry is made at the end of + * the phases array (position count_phases), it is added to the map, + * and the new structure is returned. + * + * Arguments: + * name input, character string to be located or stored. + * + * Returns: + * The address of a phase structure that contains the phase data. + * If phase existed, it is reinitialized. The structure returned + * contains only the name of the phase. + */ + class phase *phase_ptr = NULL; +/* + * Search list + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator p_it = + phases_map.find(name); + if (p_it != phases_map.end()) + { + phase_ptr = p_it->second; + phase_free(phase_ptr); + phase_init(phase_ptr); + phase_ptr->name = string_hsave(name_in); + return (phase_ptr); + } +/* + * Make new phase structure and return pointer to it + */ + size_t n = phases.size(); + phases.resize(n + 1); + phases[n] = phase_alloc(); + /* set name in phase structure */ + phases[n]->name = string_hsave(name_in); +/* + * Update map + */ + phases_map[name] = phases[n]; + return (phases[n]); +} +/* ********************************************************************** + * + * Routines related to structure "rates" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class rate * Phreeqc:: +rate_bsearch(const char* cptr, int *j) +/* ---------------------------------------------------------------------- */ +{ +/* Binary search the structure array "rates" for a name that is equal to + * cptr. Assumes array rates is in sort order. + * + * Arguments: + * name input, a character string to be located in rates. + * j index number in array rates. + * + * Returns: + * if found, pointer to rate structure. + * if not found, NULL + * + */ + void *void_ptr; + + if (rates.size() == 0) + { + *j = -1; + return (NULL); + } + void_ptr = (void *) + bsearch((char *) cptr, + (char *) &rates[0], + rates.size(), + sizeof(class rate *), rate_compare_string); + + if (void_ptr == NULL) + { + *j = -1; + return (NULL); + } + + *j = (int) ((class rate *) void_ptr - &rates[0]); + return ((class rate *) void_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares names of rates for sort + */ + const class rate *rate_ptr1, *rate_ptr2; + rate_ptr1 = *(const class rate **) ptr1; + rate_ptr2 = *(const class rate **) ptr2; + return (strcmp_nocase(rate_ptr1->name, rate_ptr2->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *char_ptr; + const class rate *rate_ptr; + char_ptr = (const char *) ptr1; + rate_ptr = *(const class rate **) ptr2; + return (strcmp_nocase(char_ptr, rate_ptr->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_free(class rate *rate_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within rate[i], does not free rate structure + * Input: i, number of rate + * Return: OK + */ + + + if (rate_ptr == NULL) + return (ERROR); + rate_ptr->commands.clear(); + if (rate_ptr->linebase != NULL) + { + char cmd[] = "new; quit"; + basic_run(cmd, rate_ptr->linebase, rate_ptr->varbase, rate_ptr->loopbase); + rate_ptr->linebase = NULL; + rate_ptr->varbase = NULL; + rate_ptr->loopbase = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class rate * Phreeqc:: +rate_copy(const class rate *rate_ptr) +/* ---------------------------------------------------------------------- */ +{ + /* + * Copies a rate to new allocated space + */ + if (rate_ptr == NULL) + return (NULL); + class rate* rate_new = new class rate; + rate_new->name = string_hsave(rate_ptr->name); + rate_new->commands = rate_ptr->commands; + rate_new->new_def = TRUE; + rate_new->linebase = NULL; + rate_new->varbase = NULL; + rate_new->loopbase = NULL; + return (rate_new); +} + +/* ---------------------------------------------------------------------- */ +class rate * Phreeqc:: +rate_search(const char *name_in, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* Linear search of the structure array "rates" for name. + * + * Arguments: + * name input, name of rate + * n output, position in rates + * + * Returns: + * if found, the address of the pp_assemblage element + * if not found, NULL + */ + std::map::iterator it; + + const char * name; + name = string_hsave(name_in); + + it = rates_map.find(name); + if (it != rates_map.end()) + { + *n = it->second; + if (*n >= 0) + { + return &(rates[it->second]); + } + return NULL; + } + + int i; + *n = -1; + for (i = 0; i < (int)rates.size(); i++) + { + if (strcmp_nocase(rates[i].name, name) == 0) + { + *n = i; + rates_map[name] = i; + return (&(rates[i])); + } + } +/* + * rate name not found + */ + rates_map[name] = *n; + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort array of rate structures + */ + if (rates.size() > 1) + { + qsort(&rates[0], rates.size(), sizeof(class rate), + rate_compare); + } + return (OK); +} +/* ********************************************************************** + * + * Routines related to structure "species" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class species * Phreeqc:: +s_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a species structure, initializes + * arguments: void + * return: pointer to a species structure + */ +{ + class species *s_ptr; + s_ptr = new class species; +/* + * set pointers in structure to NULL, variables to zero + */ + s_init(s_ptr); + + return (s_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class species *s_ptr1, *s_ptr2; + s_ptr1 = *(const class species **) ptr1; + s_ptr2 = *(const class species **) ptr2; + return (strcmp(s_ptr1->name, s_ptr2->name)); + +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete species i: free memory and renumber array of pointers, s. + */ + s_free(s[i]); + s[i] = (class species *) free_check_null(s[i]); + s.erase(s.begin() + i); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_free(class species *s_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free space allocated for species structure, s_ptr. Does not free s_ptr. + */ + if (s_ptr == NULL) + return (ERROR); + s_ptr->next_elt.clear(); + s_ptr->next_secondary.clear(); + s_ptr->next_sys_total.clear(); + s_ptr->add_logk.clear(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +s_init(class species *s_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a species structure + */ +{ + int i; +/* + * set pointers in structure to NULL + */ + s_ptr->name = NULL; + s_ptr->mole_balance = NULL; + s_ptr->in = FALSE; + s_ptr->number = 0; + s_ptr->primary = NULL; + s_ptr->secondary = NULL; + s_ptr->gfw = 0.0; + s_ptr->z = 0.0; + s_ptr->dw = 0.0; + s_ptr->dw_t = 0.0; + s_ptr->dw_a = 0.0; + s_ptr->dw_a2 = 0.0; + s_ptr->erm_ddl = 1.0; + s_ptr->equiv = 0; + s_ptr->alk = 0.0; + s_ptr->carbon = 0.0; + s_ptr->co2 = 0.0; + s_ptr->h = 0.0; + s_ptr->o = 0.0; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + s_ptr->a_f = 0.0; + s_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + s_ptr->logk[i] = 0.0; + } + for (i = 0; i < 10; i++) + { + s_ptr->Jones_Dole[i] = 0.0; + } +/* VP: Density Start */ + for (i = 0; i < 6; i++) + { + s_ptr->millero[i] = 0.0; + } +/* VP: Density End */ + s_ptr->original_units = kjoules; + s_ptr->add_logk.clear(); + s_ptr->lg = 0.0; + s_ptr->lg_pitzer = 0.0; + s_ptr->lm = 0.0; + s_ptr->la = 0.0; + s_ptr->dg = 0.0; + s_ptr->dg_total_g = 0; + s_ptr->moles = 0.0; + s_ptr->type = 0; + s_ptr->gflag = 0; + s_ptr->exch_gflag = 0; + s_ptr->check_equation = TRUE; + s_ptr->tot_g_moles = 0; + s_ptr->tot_dh2o_moles = 0; + for (i = 0; i < 5; i++) + { + s_ptr->cd_music[i] = 0.0; + } + for (i = 0; i < 3; i++) + { + s_ptr->dz[i] = 0.0; + } + s_ptr->original_deltav_units = cm3_per_mol; + return (OK); +} +/* ---------------------------------------------------------------------- */ +class species* Phreeqc:: +s_search(const char* name) +/* ---------------------------------------------------------------------- */ +{ + /* + * Function locates the string "name" in the species_map. + * + * Arguments: + * name input, a character string to be located in species. + * + * Returns: + * If found, pointer to the appropriate species structure is returned. + * else, NULL pointer is returned. + */ + class species* s_ptr = NULL; + std::map::iterator s_it = + species_map.find(name); + if (s_it != species_map.end()) + { + s_ptr = s_it->second; + } + return (s_ptr); +} +/* ---------------------------------------------------------------------- */ +class species * Phreeqc:: +s_store(const char *name, LDBLE l_z, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for species. + * + * Pointer to a species structure is always returned. + * + * If the string is not found, a new entry is made at the end of + * the elements array (position count_elements) and count_elements is + * incremented. A new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old species structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old species structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "species". + * l_z input, charge on "name" + * replace_if_found input, TRUE means reinitialize species if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to species structure "s" where "name" can be found. + */ + +/* + * Search list + */ + class species* s_ptr = NULL; + s_ptr = s_search(name); + if (s_ptr != NULL && replace_if_found == FALSE) + { + return (s_ptr); + } + else if (s_ptr != NULL && replace_if_found == TRUE) + { + s_free(s_ptr); + s_init(s_ptr); + } + else + { + size_t n = s.size(); + s.resize(n + 1); + /* Make new species structure */ + s[n] = s_alloc(); + s_ptr = s[n]; + } + /* set name and z in pointer in species structure */ + s_ptr->name = string_hsave(name); + s_ptr->z = l_z; +/* + * Update map + */ + species_map[name] = s_ptr; + return (s_ptr); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isotope_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int i; + const class isotope *iso_ptr1, *iso_ptr2; + + iso_ptr1 = (const class isotope *) ptr1; + iso_ptr2 = (const class isotope *) ptr2; + i = strcmp_nocase(iso_ptr1->elt_name, iso_ptr2->elt_name); + if (i != 0) + return (i); + if (iso_ptr1->isotope_number < iso_ptr2->isotope_number) + { + return (-1); + } + else if (iso_ptr1->isotope_number > iso_ptr2->isotope_number) + { + return (1); + } + return (0); +} +/* ********************************************************************** + * + * Routines related to structure "species_list" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int j; + const char *name1, *name2; + const class species_list *nptr1, *nptr2; + + nptr1 = (const class species_list *) ptr1; + nptr2 = (const class species_list *) ptr2; + +/* + * Put H+ first + */ + if (nptr1->master_s != nptr2->master_s) + { + /* + if (nptr1->master_s == s_hplus) + return (-1); + if (nptr2->master_s == s_hplus) + return (1); + */ + if ((strcmp(nptr1->master_s->name,"H+") == 0) || (strcmp(nptr1->master_s->name,"H3O+") == 0)) + return (-1); + if ((strcmp(nptr2->master_s->name,"H+") == 0) || (strcmp(nptr2->master_s->name,"H3O+") == 0)) + return (1); + } +/* + * Other element valence states + */ + if (nptr1->master_s->secondary != NULL) + { + name1 = nptr1->master_s->secondary->elt->name; + } + else + { + name1 = nptr1->master_s->primary->elt->name; + } + if (nptr2->master_s->secondary != NULL) + { + name2 = nptr2->master_s->secondary->elt->name; + } + else + { + name2 = nptr2->master_s->primary->elt->name; + } +/* + * Compare name of primary or secondary master species; log molality + */ + + j = strcmp(name1, name2); + +/* + * Different master species + */ + if (j != 0) + return (j); + +/* + * Else, descending order by log molality + */ + if (nptr1->s->lm > nptr2->s->lm) + { + return (-1); + } + else if (nptr1->s->lm < nptr2->s->lm) + { + return (1); + } + else + { + return (0); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_compare_alk(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class species_list *nptr1, *nptr2; + LDBLE alk1, alk2; + + nptr1 = (const class species_list *) ptr1; + nptr2 = (const class species_list *) ptr2; +/* + * Else, descending order by log molality + */ + alk1 = fabs(under(nptr1->s->lm) * nptr1->s->alk); + alk2 = fabs(under(nptr2->s->lm) * nptr2->s->alk); + + if (alk1 > alk2) + { + return (-1); + } + else if (alk1 < alk2) + { + return (1); + } + else + { + return (0); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_compare_master(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *name1, *name2; + const class species_list *nptr1, *nptr2; + + nptr1 = (const class species_list *) ptr1; + nptr2 = (const class species_list *) ptr2; + +/* + * Put H+ first + */ + if (nptr1->master_s != nptr2->master_s) + { + /* + if (nptr1->master_s == s_hplus) + return (-1); + if (nptr2->master_s == s_hplus) + return (1); + */ + if ((strcmp(nptr1->master_s->name,"H+") == 0) || (strcmp(nptr1->master_s->name,"H3O+") == 0)) + return (-1); + if ((strcmp(nptr2->master_s->name,"H+") == 0) || (strcmp(nptr2->master_s->name,"H3O+") == 0)) + return (1); + } +/* + * Other element valence states + */ + if (nptr1->master_s->secondary != NULL) + { + name1 = nptr1->master_s->secondary->elt->name; + } + else + { + name1 = nptr1->master_s->primary->elt->name; + } + if (nptr2->master_s->secondary != NULL) + { + name2 = nptr2->master_s->secondary->elt->name; + } + else + { + name2 = nptr2->master_s->primary->elt->name; + } +/* + * Compare name of primary or secondary master species; log molality + */ + + return (strcmp(name1, name2)); +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort list using rules in species_list_compare + */ + if (species_list.size() > 1) + { + qsort(&species_list[0], species_list.size(), + sizeof(class species_list), species_list_compare); + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "surface" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct Change_Surf * Phreeqc:: +change_surf_alloc(int count) +/* ---------------------------------------------------------------------- */ +{ + if (count == 1) + return (change_surf); + change_surf = + (struct Change_Surf *) PHRQ_realloc(change_surf, + (size_t) count * + sizeof(struct Change_Surf)); + if (change_surf == NULL) + malloc_error(); + change_surf[count - 1].cell_no = -99; + change_surf[count - 1].next = FALSE; + change_surf[count - 2].next = TRUE; + + return (change_surf); +} +/* ---------------------------------------------------------------------- */ +class master * Phreeqc:: +surface_get_psi_master(const char *name, int plane) +/* ---------------------------------------------------------------------- */ +{ + class master *master_ptr; + std::string token; + + if (name == NULL) + return (NULL); + token = name; + token.append("_psi"); + switch (plane) + { + case SURF_PSI: + break; + case SURF_PSI1: + token.append("b"); + break; + case SURF_PSI2: + token.append("d"); + break; + default: + error_msg("Unknown plane for surface_get_psi_master", STOP); + } + master_ptr = master_bsearch(token.c_str()); + return (master_ptr); +} +/* ********************************************************************** + * + * Routines related to structure "trxn" + * + * ********************************************************************** */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +phase_rxn_to_trxn(class phase* phase_ptr, CReaction& rxn_ref) +/* ---------------------------------------------------------------------- */ +{ + /* + * Copy reaction from reaction structure to + * temp reaction structure. + */ + int l; + const char* cptr; + LDBLE l_z; + trxn.token.resize(rxn_ref.size()); + trxn.token[0].name = phase_ptr->formula; + /* charge */ + cptr = phase_ptr->formula; + { + std::string token; + get_token(&cptr, token, &l_z, &l); + } + trxn.token[0].z = l_z; + trxn.token[0].s = NULL; + trxn.token[0].unknown = NULL; + /*trxn.token[0].coef = -1.0; */ + /* check for leading coefficient of 1.0 for phase did not work */ + trxn.token[0].coef = phase_ptr->rxn.token[0].coef; + for (size_t i = 1; rxn_ref.token[i].s != NULL; i++) + { + trxn.token[i].name = rxn_ref.token[i].s->name; + trxn.token[i].z = rxn_ref.token[i].s->z; + trxn.token[i].s = NULL; + trxn.token[i].unknown = NULL; + trxn.token[i].coef = rxn_ref.token[i].coef; + count_trxn = i + 1; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +trxn_add(CReaction& r_ref, double coef, bool combine) +/* ---------------------------------------------------------------------- */ +{ + /* + * Adds reactions together. + * + * Global variable count_trxn determines which position in trxn is used. + * If count_trxn=0, then the equation effectively is copied into trxn. + * If count_trxn>0, then new equation is added to existing equation. + * + * Arguments: + * *r_ptr points to rxn structure to add. + * + * coef added equation is multiplied by coef. + * combine if TRUE, reaction is reaction is sorted and + * like terms combined. + */ + /* + * Accumulate log k for reaction + */ + if (count_trxn == 0) + { + for (int i = 0; i < MAX_LOG_K_INDICES; i++) trxn.logk[i] = r_ref.Get_logk()[i]; + for (int i = 0; i < 3; i++) trxn.dz[i] = r_ref.Get_dz()[i]; + } + else + { + for (int i = 0; i < MAX_LOG_K_INDICES; i++) trxn.logk[i] += coef * r_ref.Get_logk()[i]; + for (int i = 0; i < 3; i++) trxn.dz[i] += coef * r_ref.Get_dz()[i]; + } + /* + * Copy equation into work space + */ + class rxn_token* next_token = &r_ref.token[0]; + while (next_token->s != NULL) + { + if (count_trxn + 1 > trxn.token.size()) + trxn.token.resize(count_trxn + 1); + trxn.token[count_trxn].name = next_token->s->name; + trxn.token[count_trxn].s = next_token->s; + trxn.token[count_trxn].coef = coef * next_token->coef; + count_trxn++; + next_token++; + } + if (combine == TRUE) + trxn_combine(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +trxn_add_phase(CReaction& r_ref, double coef, bool combine) +/* ---------------------------------------------------------------------- */ +{ + /* + * Adds reactions together. + * + * Global variable count_trxn determines which position in trxn is used. + * If count_trxn=0, then the equation effectively is copied into trxn. + * If count_trxn>0, then new equation is added to existing equation. + * + * Arguments: + * *r_ptr points to rxn structure to add. + * + * coef added equation is multiplied by coef. + * combine if TRUE, reaction is reaction is sorted and + * like terms combined. + */ + int i; + class rxn_token* next_token; + /* + * Accumulate log k for reaction + */ + if (count_trxn == 0) + { + memcpy((void*)trxn.logk, (void*)r_ref.Get_logk(), + (size_t)MAX_LOG_K_INDICES * sizeof(double)); + } + else + { + for (i = 0; i < MAX_LOG_K_INDICES; i++) trxn.logk[i] += coef * r_ref.Get_logk()[i]; + } + /* + * Copy equation into work space + */ + next_token = &r_ref.token[0]; + while (next_token->s != NULL || next_token->name != NULL) + { + if (count_trxn + 1 > trxn.token.size()) + trxn.token.resize(count_trxn + 1); + if (next_token->s != NULL) + { + trxn.token[count_trxn].name = next_token->s->name; + trxn.token[count_trxn].s = next_token->s; + } + else + { + trxn.token[count_trxn].name = next_token->name; + trxn.token[count_trxn].s = NULL; + } + trxn.token[count_trxn].coef = coef * next_token->coef; + count_trxn++; + next_token++; + } + if (combine) + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_combine(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Combines coefficients of tokens that are equal in temporary + * reaction structure, trxn. + */ + int j, k; +/* + * Sort trxn species + */ + trxn_sort(); +/* + * Combine trxn tokens + */ + j = 1; + for (k = 2; k < count_trxn; k++) + { + if (trxn.token[k].s != NULL) + { + if ((j > 0) && (trxn.token[k].s == trxn.token[j].s)) + { + trxn.token[j].coef += trxn.token[k].coef; + if (equal(trxn.token[j].coef, 0.0, 1e-5)) + j--; + } + else + { + j++; + if (k != j) + { + trxn.token[j].name = trxn.token[k].name; + trxn.token[j].s = trxn.token[k].s; + trxn.token[j].coef = trxn.token[k].coef; + } + } + } + else + { + if ((j > 0) && (trxn.token[k].s == trxn.token[j].s) + && (trxn.token[k].name == trxn.token[j].name)) + { + trxn.token[j].coef += trxn.token[k].coef; + if (equal(trxn.token[j].coef, 0.0, 1e-5)) + j--; + } + else + { + j++; + if (k != j) + { + trxn.token[j].name = trxn.token[k].name; + trxn.token[j].s = trxn.token[k].s; + trxn.token[j].coef = trxn.token[k].coef; + } + } + } + } + count_trxn = j + 1; /* number excluding final NULL */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_compare(const void* ptr1, const void* ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class rxn_token_temp* rxn_token_temp_ptr1, * rxn_token_temp_ptr2; + rxn_token_temp_ptr1 = (const class rxn_token_temp*)ptr1; + rxn_token_temp_ptr2 = (const class rxn_token_temp*)ptr2; + return (strcmp(rxn_token_temp_ptr1->name, rxn_token_temp_ptr2->name)); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +trxn_copy(CReaction& rxn_ref) +/* ---------------------------------------------------------------------- */ +{ + /* + * Copies trxn to a reaction structure. + * + * Input: rxn_ptr, pointer to reaction structure to copy trxn to. + * + */ + int i; + /* + * Copy logk data + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + rxn_ref.logk[i] = trxn.logk[i]; + } + /* + * Copy dz data + */ + for (i = 0; i < 3; i++) + { + rxn_ref.dz[i] = trxn.dz[i]; + } + /* + * Copy tokens + */ + rxn_ref.Get_tokens().resize(count_trxn + 1); + for (size_t i = 0; i < count_trxn; i++) + { + rxn_ref.Get_tokens()[i].s = trxn.token[i].s; + rxn_ref.Get_tokens()[i].name = trxn.token[i].name; + rxn_ref.Get_tokens()[i].coef = trxn.token[i].coef; + } + rxn_ref.token[count_trxn].s = NULL; + rxn_ref.token[count_trxn].name = NULL; + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +trxn_find_coef(const char *str, int start) +/* ---------------------------------------------------------------------- */ +{ +/* + * Finds coefficient of specified token in trxn. + * Input: str, token name in reaction. + * + * Return: 0.0, if token not found. + * coefficient of token, if token found. + */ + int i; + LDBLE coef; + + coef = 0.0; + for (i = start; i < count_trxn; i++) + { + if (strcmp(trxn.token[i].s->name, str) == 0) + { + coef = trxn.token[i].coef; + break; + } + } + return (coef); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_multiply(LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Multiplies temporary reaction, trxn, by a constant + * + * Arguments: + * input: coef multiplier. + */ + int i; +/* + * Multiply log k for reaction + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] *= coef; + } +/* + * Multiply dz for reaction + */ + for (i = 0; i < 3; i++) + { + trxn.dz[i] *= coef; + } +/* + * Multiply coefficients of reaction + */ + for (i = 0; i < count_trxn; i++) + { + trxn.token[i].coef *= coef; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints trxn + */ + int i; +/* + * Print log k for reaction + */ + + output_msg(sformatf( "\tlog k data:\n")); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + output_msg(sformatf( "\t\t%f\n", (double) trxn.logk[i])); + } + +/* + * Print dz for reaction + */ + output_msg(sformatf( "\tdz data:\n")); + for (i = 0; i < 3; i++) + { + output_msg(sformatf( "\t\t%f\n", (double) trxn.dz[i])); + } +/* + * Print stoichiometry + */ + output_msg(sformatf( "\tReaction stoichiometry\n")); + for (i = 0; i < count_trxn; i++) + { + output_msg(sformatf( "\t\t%-20s\t%10.2f\n", trxn.token[i].name, + (double) trxn.token[i].coef)); + } + output_msg(sformatf( "\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_reverse_k(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Changes K from dissociation to association and back + */ + int i; +/* + * Accumulate log k for reaction + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = -trxn.logk[i]; + } + for (i = 0; i < 3; i++) + { + trxn.dz[i] = -trxn.dz[i]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare names in tokens in trxn array for sorting + */ + if (count_trxn - 1 > 1) + { + qsort(&trxn.token[1], + (size_t)count_trxn - 1, + sizeof(class rxn_token_temp), + trxn_compare); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_swap(const char *token) +/* ---------------------------------------------------------------------- */ +{ +/* + * Moves specified token to initial position in reaction. + * Input: token, token name to move to initial position. + * + * Return: ERROR, if token not found. + * OK, if token moved to initial position. + */ + int i, j; + LDBLE coef; +/* + * Locate token + */ + for (j = 0; j < count_trxn; j++) + { + if (strcmp(trxn.token[j].s->name, token) == 0) + break; + } + if (j >= count_trxn) + { + input_error++; + error_string = sformatf( "Could not find token in equation, %s.", token); + error_msg(error_string, CONTINUE); + for (i = 0; i < count_trxn; i++) + { + output_msg(sformatf( "%f\t%s\t", + (double) trxn.token[i].coef, trxn.token[i].name)); + } + output_msg(sformatf( "\n")); + return (ERROR); + } +/* + * Swap token to first position + */ + trxn.token[count_trxn].name = trxn.token[0].name; + trxn.token[count_trxn].s = trxn.token[0].s; + trxn.token[count_trxn].coef = trxn.token[0].coef; + + trxn.token[0].name = trxn.token[j].name; + trxn.token[0].s = trxn.token[j].s; + trxn.token[0].coef = trxn.token[j].coef; + + trxn.token[j].name = trxn.token[count_trxn].name; + trxn.token[j].s = trxn.token[count_trxn].s; + trxn.token[j].coef = trxn.token[count_trxn].coef; +/* + * Make coefficient of token -1.0 + */ + coef = -1.0 / trxn.token[0].coef; + trxn_multiply(coef); + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "unknown" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +class unknown * Phreeqc:: +unknown_alloc(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space to an "unknown" structure + * arguments: void + * return: pointer to an "unknown" structure + */ + class unknown *unknown_ptr; +/* + * Allocate space + */ + unknown_ptr = new class unknown; +/* + * set pointers in structure to NULL + */ + unknown_ptr->type = 0; + unknown_ptr->moles = 0.0; + unknown_ptr->ln_moles = 0.0; + unknown_ptr->f = 0.0; + unknown_ptr->sum = 0.0; + unknown_ptr->delta = 0.0; + unknown_ptr->la = 0.0; + unknown_ptr->number = 0; + unknown_ptr->description = NULL; + unknown_ptr->phase = NULL; + unknown_ptr->si = 0.0; + unknown_ptr->s = NULL; + unknown_ptr->exch_comp = NULL; + unknown_ptr->pp_assemblage_comp_name = NULL; + unknown_ptr->pp_assemblage_comp_ptr = NULL; + unknown_ptr->ss_name = NULL; + unknown_ptr->ss_ptr = NULL; + unknown_ptr->ss_comp_name = NULL; + unknown_ptr->ss_comp_ptr = NULL; + unknown_ptr->ss_comp_number = 0; + unknown_ptr->ss_in = FALSE; + unknown_ptr->surface_comp = NULL; + unknown_ptr->related_moles = 0.0; + unknown_ptr->potential_unknown = NULL; + unknown_ptr->potential_unknown1 = NULL; + unknown_ptr->potential_unknown2 = NULL; + unknown_ptr->phase_unknown = NULL; + unknown_ptr->surface_charge = NULL; + unknown_ptr->mass_water = 0.0; + unknown_ptr->dissolve_only = FALSE; + unknown_ptr->inert_moles = 0.0; + unknown_ptr->V_m = 0.0; + unknown_ptr->pressure = 0.0; + unknown_ptr->mb_number = 0; + unknown_ptr->iteration = 0; + + return (unknown_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +unknown_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete unknow from list x + */ + unknown_free(x[i]); + x.erase(x.begin() + (size_t)i); + count_unknowns--; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +unknown_free(class unknown *unknown_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees space allocated to an unknown structure, frees unknown_ptr. + */ + if (unknown_ptr == NULL) + return (ERROR); + unknown_ptr->master.clear(); + if (unknown_ptr->type == SURFACE_CB) + { + /* + surface_charge_free(unknown_ptr->surface_charge); + unknown_ptr->surface_charge = (struct surface_charge *) free_check_null(unknown_ptr->surface_charge); + */ + } + unknown_ptr->comp_unknowns.clear(); + delete unknown_ptr; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_duplicate(int i, int save_old) +/* ---------------------------------------------------------------------- */ +{ + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + + Utilities::Rxn_copy(Rxn_pp_assemblage_map, i, save_old); + + Utilities::Rxn_copy(Rxn_exchange_map, i, save_old); + + Utilities::Rxn_copy(Rxn_surface_map, i, save_old); + + Utilities::Rxn_copy(Rxn_gas_phase_map, i, save_old); + + Utilities::Rxn_copy(Rxn_kinetics_map, i, save_old); + + Utilities::Rxn_copy(Rxn_ss_assemblage_map, i, save_old); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class logk * Phreeqc:: +logk_store(const char *name_in, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for logk. + * + * Pointer to a logk structure is always returned. + * + * If the string is not found, a new entry is made in the map. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old logk structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old logk structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "logk". + * replace_if_found input, TRUE means reinitialize logk structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to logk structure "logk" where "name" can be found. + */ +/* + * Search list + */ + class logk* logk_ptr = NULL; + std::string name = name_in; + str_tolower(name); + std::map::iterator it = + logk_map.find(name); + + if (it != logk_map.end() && replace_if_found == FALSE) + { + logk_ptr = it->second; + return (logk_ptr); + } + else if (it != logk_map.end() && replace_if_found == TRUE) + { + logk_ptr = it->second; + logk_init(logk_ptr); + } + else + { + /* Make new logk structure */ + size_t n = logk.size(); + logk.resize(n + 1); + logk[n] = logk_alloc(); + logk_ptr = logk[n]; + } + /* set name and z in pointer in logk structure */ + logk_ptr->name = string_hsave(name_in); +/* + * Update map + */ + logk_map[name] = logk_ptr; + return (logk_ptr); +} + +/* ---------------------------------------------------------------------- */ +class logk * Phreeqc:: +logk_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a logk structure, initializes + * arguments: void + * return: pointer to a logk structure + */ +{ + class logk *logk_ptr; + logk_ptr = new class logk; +/* + * set pointers in structure to NULL, variables to zero + */ + logk_init(logk_ptr); + + return (logk_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +logk_init(class logk *logk_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a logk structure + */ +{ + int i; +/* + * set pointers in structure to NULL + */ + logk_ptr->name = NULL; +/* + * set varibles = 0 + */ + logk_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk_ptr->log_k[i] = 0.0; + logk_ptr->log_k_original[i] = 0.0; + } + logk_ptr->add_logk.clear(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +logk_copy2orig(class logk *logk_ptr) +/* ---------------------------------------------------------------------- */ +/* + * Copies log k data to logk_original + */ +{ + int i; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk_ptr->log_k_original[i] = logk_ptr->log_k[i]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +class logk * Phreeqc:: +logk_search(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the map for logk. + * + * Arguments: + * name input, character string to be found in "logk". + * + * Returns: + * pointer to logk structure "logk" where "name" can be found. + * or NULL if not found. + */ + class logk *logk_ptr; +/* + * Search list + */ + std::string name = name_in; + str_tolower(name); + std::map::iterator l_it = + logk_map.find(name); + if (l_it != logk_map.end()) + { + logk_ptr = l_it->second; + return (logk_ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +entity_exists(const char *name, int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, 0 Solution + * reaction, 1 Reaction + * exchange, 2 Exchange + * surface, 3 Surface + * gas_phase, 4 Gas_phase + * equilibrium_phases, 5 Pure_phase + * solid_solution, 6 Ss_phase + * kinetics, 7 Kinetics + * mix, 8 Mix + * reaction_temperature 9 Temperature + * unknown 10 UnKnown + */ + int return_value; + char token[MAX_LENGTH]; + enum entity_type type; +/* + * Read keyword + */ + strncpy(token, name, MAX_LENGTH-1); + token[MAX_LENGTH-1] = '\0'; + type = get_entity_enum(token); + return_value = TRUE; + switch (type) + { + case UnKnown: + warning_msg + ("EXISTS expecting keyword solution, mix, kinetics, reaction, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions."); + return_value = 2; + break; + case Solution: /* Solution */ + if (Utilities::Rxn_find(Rxn_solution_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Pure_phase: /* Pure phases */ + if (Utilities::Rxn_find(Rxn_pp_assemblage_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Reaction: /* Reaction */ + if (Utilities::Rxn_find(Rxn_reaction_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Mix: /* Mix */ + if (Utilities::Rxn_find(Rxn_mix_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Exchange: /* Ex */ + if (Utilities::Rxn_find(Rxn_exchange_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Surface: /* Surface */ + if (Utilities::Rxn_find(Rxn_surface_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Temperature: + if (Utilities::Rxn_find(Rxn_temperature_map, n_user) == NULL) + { + return_value = FALSE; + } + case Pressure: + if (Utilities::Rxn_find(Rxn_pressure_map, n_user) == NULL) + { + return_value = FALSE; + } + case Gas_phase: /* Gas */ + if (Utilities::Rxn_find(Rxn_gas_phase_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Kinetics: /* Kinetics */ + if (Utilities::Rxn_find(Rxn_kinetics_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Ss_phase: /* solid_solutions */ + if (Utilities::Rxn_find(Rxn_ss_assemblage_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +enum entity_type Phreeqc:: +get_entity_enum(char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, 0 Solution + * reaction, 1 Reaction + * exchange, 2 Exchange + * surface, 3 Surface + * gas_phase, 4 Gas_phase + * equilibrium_phases, 5 Pure_phase + * solid_solution, 6 Ss_phase + * kinetics, 7 Kinetics + * mix, 8 Mix + * reaction_temperature 9 Temperature + * reaction_pressure + * unknown 10 UnKnown + * + */ + int i; + const char* cptr; + char token[MAX_LENGTH]; +/* + * Read keyword + */ + cptr = name; + copy_token(token, &cptr, &i); + check_key(token); + + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + return (Solution); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + return (Pure_phase); + break; + case Keywords::KEY_REACTION: /* Reaction */ + return (Reaction); + break; + case Keywords::KEY_MIX: /* Mix */ + return (Mix); + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + return (Exchange); + break; + case Keywords::KEY_SURFACE: /* Surface */ + return (Surface); + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + return (Temperature); + break; + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + return (Pressure); + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + return (Gas_phase); + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + return (Kinetics); + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + return (Ss_phase); + break; + default: + warning_msg + ("EXISTS expecting keyword solution, mix, kinetics, reaction, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions."); + break; + } + return (UnKnown); +} + +/* + * copier routines + */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copier_add(class copier *copier_ptr, int n_user, int start, int end) +/* ---------------------------------------------------------------------- */ +/* + * add new set of copy instructions + */ +{ + copier_ptr->n_user.push_back(n_user); + copier_ptr->start.push_back(start); + copier_ptr->end.push_back(end); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copier_clear(class copier* copier_ptr) +/* ---------------------------------------------------------------------- */ +/* + * clear copier + */ +{ + copier_ptr->n_user.clear(); + copier_ptr->start.clear(); + copier_ptr->end.clear(); + return (OK); +} + +#include "StorageBin.h" + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +Use2cxxStorageBin(cxxStorageBin & sb) +/* ---------------------------------------------------------------------- */ +{ + //Add everything from use structure to storagebin sb + + sb.Get_system().Set_io(sb.Get_io()); + if (use.Get_mix_in()) + { + cxxMix *entity = use.Get_mix_ptr(); + if (entity != NULL) + { + sb.Set_Mix(use.Get_n_mix_user(), entity); + } + + // put mix solutions in sb + cxxMix * mix_ptr = use.Get_mix_ptr(); + std::map::const_iterator cit; + for (cit = mix_ptr->Get_mixComps().begin(); cit != mix_ptr->Get_mixComps().end(); cit++) + { + cxxSolution *entity = Utilities::Rxn_find(Rxn_solution_map, cit->first); + if (entity != NULL) + { + sb.Set_Solution(cit->first, entity); + } + } + } + else if (use.Get_solution_in()) + { + cxxSolution *entity = Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user()); + if (entity != NULL) + { + sb.Set_Solution(use.Get_n_solution_user(), entity); + } + } + if (use.Get_pp_assemblage_in()) + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user()); + if (entity_ptr != NULL) + { + sb.Set_PPassemblage(use.Get_n_pp_assemblage_user(), entity_ptr); + } + } + if (use.Get_exchange_in()) + { + cxxExchange *entity_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + if (entity_ptr != NULL) + { + sb.Set_Exchange(use.Get_n_exchange_user(), entity_ptr); + } + } + if (use.Get_surface_in()) + { + cxxSurface *entity_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + if (entity_ptr != NULL) + { + sb.Set_Surface(use.Get_n_surface_user(), entity_ptr); + } + } + if (use.Get_gas_phase_in()) + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user()); + if (entity_ptr != NULL) + { + sb.Set_GasPhase(use.Get_n_gas_phase_user(), entity_ptr); + } + } + if (use.Get_ss_assemblage_in()) + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user()); + if (entity_ptr != NULL) + { + sb.Set_SSassemblage(use.Get_n_ss_assemblage_user(), entity_ptr); + } + } + if (use.Get_kinetics_in()) + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + if (entity_ptr != NULL) + { + sb.Set_Kinetics(use.Get_n_kinetics_user(), entity_ptr); + } + } + if (use.Get_reaction_in()) + { + cxxReaction *entity = Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user()); + if (entity != NULL) + { + sb.Set_Reaction(use.Get_n_reaction_user(), entity); + } + } + if (use.Get_temperature_in()) + { + cxxTemperature *entity = Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user()); + if (entity != NULL) + { + sb.Set_Temperature(use.Get_n_temperature_user(), entity); + } + } + if (use.Get_pressure_in()) + { + cxxPressure *entity = Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user()); + if (entity != NULL) + { + sb.Set_Pressure(use.Get_n_pressure_user(), entity); + } + } +} + +void Phreeqc:: +phreeqc2cxxStorageBin(cxxStorageBin & sb) + // + // Fills StorageBin sb with all reactants from phreeqc instance. + // equivalent to old import_phreeqc. + // +{ + // Solutions + { + std::map::iterator it; + for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + { + sb.Set_Solution(it->second.Get_n_user(), &(it->second)); + } + } + // Exchangers + { + std::map::iterator it; + for (it = Rxn_exchange_map.begin(); it != Rxn_exchange_map.end(); it++) + { + sb.Set_Exchange(it->second.Get_n_user(), &(it->second)); + } + } + // GasPhases + { + std::map::iterator it; + for (it = Rxn_gas_phase_map.begin(); it != Rxn_gas_phase_map.end(); it++) + { + sb.Set_GasPhase(it->second.Get_n_user(), &(it->second)); + } + } + + // Kinetics + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + sb.Set_Kinetics(it->second.Get_n_user(), &(it->second)); + } + } + // PPassemblages + { + std::map::iterator it; + for (it = Rxn_pp_assemblage_map.begin(); it != Rxn_pp_assemblage_map.end(); it++) + { + sb.Set_PPassemblage(it->second.Get_n_user(), &(it->second)); + } + } + // SSassemblages + { + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + sb.Set_SSassemblage(it->second.Get_n_user(), &(it->second)); + } + } + // Surfaces + { + std::map::iterator it; + for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) + { + sb.Set_Surface(it->second.Get_n_user(), &(it->second)); + } + } + // Mixes + { + std::map::iterator it; + for (it = Rxn_mix_map.begin(); it != Rxn_mix_map.end(); it++) + { + sb.Set_Mix(it->second.Get_n_user(), &(it->second)); + } + } + + // Reactions + { + std::map::iterator it; + for (it = Rxn_reaction_map.begin(); it != Rxn_reaction_map.end(); it++) + { + sb.Set_Reaction(it->second.Get_n_user(), &(it->second)); + } + } + + // Temperatures + { + std::map::iterator it; + for (it = Rxn_temperature_map.begin(); it != Rxn_temperature_map.end(); it++) + { + sb.Set_Temperature(it->second.Get_n_user(), &(it->second)); + } + } + + // Pressures + { + std::map::iterator it; + for (it = Rxn_pressure_map.begin(); it != Rxn_pressure_map.end(); it++) + { + sb.Set_Pressure(it->second.Get_n_user(), &(it->second)); + } + } +} + +void Phreeqc:: +phreeqc2cxxStorageBin(cxxStorageBin & sb, int n) + // + // copy phreeqc reactants numbered n to StorageBin sb + // +{ + // Solutions + { + cxxSolution *entity_ptr = Utilities::Rxn_find(Rxn_solution_map, n); + if (entity_ptr != NULL) + { + sb.Set_Solution(n, entity_ptr); + } + } + // Exchangers + { + cxxExchange *entity_ptr = Utilities::Rxn_find(Rxn_exchange_map, n); + if (entity_ptr != NULL) + { + sb.Set_Exchange(n, entity_ptr); + } + } + + // GasPhases + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, n); + if (entity_ptr != NULL) + { + sb.Set_GasPhase(n, entity_ptr); + } + } + + // Kinetics + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n); + if (entity_ptr != NULL) + { + sb.Set_Kinetics(n, entity_ptr); + } + } + // PPassemblages + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + if (entity_ptr != NULL) + { + sb.Set_PPassemblage(n, entity_ptr); + } + } + // SSassemblages + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, n); + if (entity_ptr != NULL) + { + sb.Set_SSassemblage(n, entity_ptr); + } + } + // Surfaces + { + cxxSurface *entity_ptr = Utilities::Rxn_find(Rxn_surface_map, n); + if (entity_ptr != NULL) + { + sb.Set_Surface(n, entity_ptr); + } + } +} +void Phreeqc:: +cxxStorageBin2phreeqc(cxxStorageBin & sb, int n) +// +// copy all reactants from storage bin number n to phreeqc +// replaces any existing reactants in phreeqc +// +{ + // Solutions + { + std::map < int, cxxSolution >::const_iterator it = sb.Get_Solutions().find(n); + if (it != sb.Get_Solutions().end()) + { + Rxn_solution_map[n] = it->second; + } + } + // Exchangers + { + std::map < int, cxxExchange >::const_iterator it = sb.Get_Exchangers().find(n); + if (it != sb.Get_Exchangers().end()) + { + Rxn_exchange_map[n] = it->second; + } + } + + // GasPhases + { + std::map < int, cxxGasPhase >::const_iterator it = sb.Get_GasPhases().find(n); + if (it != sb.Get_GasPhases().end()) + { + Rxn_gas_phase_map[n] = it->second; + } + } + + // Kinetics + { + std::map < int, cxxKinetics >::const_iterator it = sb.Get_Kinetics().find(n); + if (it != sb.Get_Kinetics().end()) + { + Rxn_kinetics_map[n] = it->second; + } + } + // PPassemblages + { + std::map < int, cxxPPassemblage >::const_iterator it = sb.Get_PPassemblages().find(n); + if (it != sb.Get_PPassemblages().end()) + { + Rxn_pp_assemblage_map[n] = it->second; + } + } + // SSassemblages + { + std::map < int, cxxSSassemblage >::const_iterator it = sb.Get_SSassemblages().find(n); + if (it != sb.Get_SSassemblages().end()) + { + Rxn_ss_assemblage_map[n] = it->second; + } + } + // Surfaces + { + std::map < int, cxxSurface >::const_iterator it = sb.Get_Surfaces().find(n); + if (it != sb.Get_Surfaces().end()) + { + Rxn_surface_map[n] = it->second; + } + } + // Mixes + { + std::map < int, cxxMix >::const_iterator it = sb.Get_Mixes().find(n); + if (it != sb.Get_Mixes().end()) + { + Rxn_mix_map[n] = it->second; + } + } + + // Reactions + { + std::map < int, cxxReaction >::const_iterator it = sb.Get_Reactions().find(n); + if (it != sb.Get_Reactions().end()) + { + Rxn_reaction_map[n] = it->second; + } + } + // Temperatures + { + std::map < int, cxxTemperature >::const_iterator it = sb.Get_Temperatures().find(n); + if (it != sb.Get_Temperatures().end()) + { + Rxn_temperature_map[n] = it->second; + } + } + // Pressures + { + std::map < int, cxxPressure >::const_iterator it = sb.Get_Pressures().find(n); + if (it != sb.Get_Pressures().end()) + { + Rxn_pressure_map[n] = it->second; + } + } +} +void Phreeqc:: +cxxStorageBin2phreeqc(cxxStorageBin & sb) +// +// copy data from storage bin to phreeqc +// replaces any existing reactants in phreeqc +// +{ + // Solutions + { + std::map < int, cxxSolution >::const_iterator it = sb.Get_Solutions().begin(); + for ( ; it != sb.Get_Solutions().end(); it++) + { + Rxn_solution_map[it->first] = it->second; + } + } + // Exchangers + { + std::map < int, cxxExchange >::const_iterator it = sb.Get_Exchangers().begin(); + for ( ; it != sb.Get_Exchangers().end(); it++) + { + Rxn_exchange_map[it->first] = it->second; + } + } + + // GasPhases + { + std::map < int, cxxGasPhase >::const_iterator it = sb.Get_GasPhases().begin(); + for ( ; it != sb.Get_GasPhases().end(); it++) + { + Rxn_gas_phase_map[it->first] = it->second; + } + } + + // Kinetics + { + std::map < int, cxxKinetics >::const_iterator it = sb.Get_Kinetics().begin(); + for ( ; it != sb.Get_Kinetics().end(); it++) + { + Rxn_kinetics_map[it->first] = it->second; + } + } + // PPassemblages + { + std::map < int, cxxPPassemblage >::const_iterator it = sb.Get_PPassemblages().begin(); + for ( ; it != sb.Get_PPassemblages().end(); it++) + { + Rxn_pp_assemblage_map[it->first] = it->second; + } + } + // SSassemblages + { + std::map < int, cxxSSassemblage >::const_iterator it = sb.Get_SSassemblages().begin(); + for ( ; it != sb.Get_SSassemblages().end(); it++) + { + Rxn_ss_assemblage_map[it->first] = it->second; + } + } + // Surfaces + { + std::map < int, cxxSurface >::const_iterator it = sb.Get_Surfaces().begin(); + for ( ; it != sb.Get_Surfaces().end(); it++) + { + Rxn_surface_map[it->first] = it->second; + } + } + // Mixes + { + std::map < int, cxxMix >::const_iterator it = sb.Get_Mixes().begin(); + for ( ; it != sb.Get_Mixes().end(); it++) + { + Rxn_mix_map[it->first] = it->second; + } + } + + // Reactions + { + std::map < int, cxxReaction >::const_iterator it = sb.Get_Reactions().begin(); + for ( ; it != sb.Get_Reactions().end(); it++) + { + Rxn_reaction_map[it->first] = it->second; + } + } + // Temperatures + { + std::map < int, cxxTemperature >::const_iterator it = sb.Get_Temperatures().begin(); + for ( ; it != sb.Get_Temperatures().end(); it++) + { + Rxn_temperature_map[it->first] = it->second; + } + } + // Pressures + { + std::map < int, cxxPressure >::const_iterator it = sb.Get_Pressures().begin(); + for ( ; it != sb.Get_Pressures().end(); it++) + { + Rxn_pressure_map[it->first] = it->second; + } + } +} + + diff --git a/phreeqcpp/sundialsmath.cpp b/phreeqcpp/sundialsmath.cpp new file mode 100644 index 00000000..0f0c4578 --- /dev/null +++ b/phreeqcpp/sundialsmath.cpp @@ -0,0 +1,133 @@ +/************************************************************************** + * * + * File : sundialsmath.c * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a C math library. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + + +#include +#include +#include "sundialsmath.h" +#include "sundialstypes.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) +#define TWO RCONST(2.0) + +realtype +UnitRoundoff(void) +{ + realtype u; + volatile realtype one_plus_u; + + u = ONE; + one_plus_u = ONE + u; + while (one_plus_u != ONE) + { + u /= TWO; + one_plus_u = ONE + u; + } + u *= TWO; + + return (u); +} + + +realtype +RPowerI(realtype base, int exponent) +{ + int i, expt; + realtype prod; + + prod = ONE; + expt = ABS(exponent); + for (i = 1; i <= expt; i++) + prod *= base; + if (exponent < 0) + prod = ONE / prod; + return (prod); +} + + +realtype +RPowerR(realtype base, realtype exponent) +{ + + if (base <= ZERO) + return (ZERO); + + /* return((realtype)pow((double)base,(double)exponent)); */ + return ((realtype) pow(base, exponent)); +} + + +realtype +RSqrt(realtype x) +{ + if (x <= ZERO) + return (ZERO); + + /* return((realtype) sqrt((double) x)); */ + return ((realtype) sqrt(x)); +} diff --git a/phreeqcpp/sundialsmath.h b/phreeqcpp/sundialsmath.h new file mode 100644 index 00000000..59e49d3a --- /dev/null +++ b/phreeqcpp/sundialsmath.h @@ -0,0 +1,162 @@ +/************************************************************************** + * * + * File : sundialsmath.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a C math library. The routines * + * listed here work with the type realtype as defined in * + * sundialstypes.h. * + * To do single precision floating point arithmetic, set the type * + * realtype to be float. To do double precision arithmetic, set * + * the type realtype to be double. The default implementations * + * for RPowerR and RSqrt call standard math library functions * + * which do double precision arithmetic. If this is unacceptable * + * when realtype is float, then the user should re-implement * + * these two routines by calling single precision routines * + * available on his/her machine. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _sundialsmath_h +#define _sundialsmath_h + +#include "sundialstypes.h" + + +/****************************************************************** + * * + * Macros : MIN, MAX, ABS, SQR * + *----------------------------------------------------------------* + * MIN(A, B) returns the minimum of A and B. * + * * + * MAX(A, B) returns the maximum of A and B. * + * * + * ABS(A) returns the absolute value of A. * + * * + * SQR(A) returns the square of A. * + * * + ******************************************************************/ +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif + +#ifndef MAX +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif + +#ifndef ABS +#define ABS(A) ((A < 0) ? -(A) : (A)) +#endif + +#ifndef SQR +#define SQR(A) ((A) * (A)) +#endif + +/****************************************************************** + * * + * Function : UnitRoundoff * + * Usage : realtype uround; * + * uround = UnitRoundoff(); * + *----------------------------------------------------------------* + * UnitRoundoff returns the unit roundoff u for real floating * + * point arithmetic, where u is defined to be the smallest * + * positive real such that 1.0 + u != 1.0. * + * * + ******************************************************************/ + + realtype UnitRoundoff(void); + + +/****************************************************************** + * * + * Function : RPowerI * + * Usage : int exponent; * + * realtype base, ans; * + * ans = RPowerI(base,exponent); * + *----------------------------------------------------------------* + * RPowerI returns the value base^exponent, where base is a * + * realtype and exponent is an int. * + * * + ******************************************************************/ + + realtype RPowerI(realtype base, int exponent); + + +/****************************************************************** + * * + * Function : RPowerR * + * Usage : realtype base, exponent, ans; * + * ans = RPowerR(base,exponent); * + *----------------------------------------------------------------* + * RPowerR returns the value base^exponent, where both base and * + * exponent are realtype. If base < 0.0, then RPowerR returns 0.0 * + * * + ******************************************************************/ + + realtype RPowerR(realtype base, realtype exponent); + + +/****************************************************************** + * * + * Function : RSqrt * + * Usage : realtype sqrt_x; * + * sqrt_x = RSqrt(x); * + *----------------------------------------------------------------* + * RSqrt(x) returns the square root of x. If x < 0.0, then RSqrt * + * returns 0.0. * + * * + ******************************************************************/ + + realtype RSqrt(realtype x); + + +#endif diff --git a/phreeqcpp/sundialstypes.h b/phreeqcpp/sundialstypes.h new file mode 100644 index 00000000..1003410f --- /dev/null +++ b/phreeqcpp/sundialstypes.h @@ -0,0 +1,183 @@ +#if defined(WIN32) +#include +#endif +#include "phrqtype.h" +#ifndef _INC_SUNDIALSTYPES_H +#define _INC_SUNDIALSTYPES_H +/************************************************************************** + * * + * File : sundialstypes.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This header file exports three types: realtype, integertype, * + * and booleantype, as well as the constants TRUE and FALSE. * + * * + * Users should #include "sundialstypes.h" in any file that * + * shhould be easily modifiable to work with different real or * + * integer types and use the exported names realtype and * + * integertype within such a file. * + * The types for realtype and integertype below have been set to * + * double and long int, respectively. A user should modify these * + * type declarations as he/she sees fit. For example, if a user * + * wants the work with type float because double precision * + * floating point arithmetic is too expensive on the user's * + * machine, then the definition below should be changed to: * + * * + * typedef float realtype; * + * * + * Similarly, if a user does not need to work with extremely large * + * integers (see the system header file for the limits * + * on type int and long int on your machine), then the user * + * should change the definition below to: * + * * + * typedef int integertype; * + * * + * The constants SUNDIALS_FLOAT, SUNDIALS_DOUBLE, SUNDIALS_INT, * + * SUNDIALS_LONG indicate the underlying types for realtype and * + * integertype. They should be set as follows: * + * * + * (1) #define SUNDIALS_FLOAT 1 * + * #define SUNDIALS_DOUBLE 0 (real is float) * + * * + * (2) #define SUNDIALS_FLOAT 0 * + * #define SUNDIALS_DOUBLE 1 (real is double) * + * * + * (3) #define SUNDIALS_INT 1 * + * #define SUNDIALS_LONG 0 (integer is int) * + * * + * (4) #define SUNDIALS_INT 0 * + * #define SUNDIALS_LONG 1 (integer is long int) * + * * + * Thus the legal types for realtype are float and double, while * + * the legal types for integertype are int and long int. * + * The macro RCONST gives a user a convenient way to define real * + * constants. To use the real constant 1.0, for example, the * + * user should write * + * * + * #define ONE RCONST(1.0) * + * * + * If realtype is double, then RCONST(1.0) expands to 1.0. * + * If realtype is float, then RCONST(1.0) expands to 1.0F. * + * There is never a need to explicitly cast 1.0 to (realtype). * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _sundialstypes_h +#define _sundialstypes_h + + +/****************************************************************** + * * + * Types : realtype, integertype * + *----------------------------------------------------------------* + * The types realtype and integertype are currently set to double * + * and int, respectively. See the documentation at the top for * + * usage details and a description of associated constants and * + * macros. * + * * + ******************************************************************/ + + typedef LDBLE realtype; + typedef long int integertype; + +#define SUNDIALS_FLOAT 0 +#define SUNDIALS_DOUBLE 1 + +#define SUNDIALS_LONG 1 +#define SUNDIALS_INT 0 + +#if SUNDIALS_FLOAT + +#define RCONST(x) x##F + +#elif SUNDIALS_DOUBLE + +#define RCONST(x) x + +#endif + + +/****************************************************************** + * * + * Type : booleantype * + * Constants : FALSE, TRUE * + *----------------------------------------------------------------* + * ANSI C does not have a built-in boolean type. Below is the * + * definition for a new type booleantype. The advantage of using * + * the name booleantype (instead of int) is an increase in code * + * readability. * + * It allows the programmer to make a distinction between int and * + * boolean data. Variables of type booleantype are intended to * + * have only the two values FALSE and TRUE which are defined * + * below to be equal to 0 and 1, respectively. * + * * + ******************************************************************/ + +#ifndef booleantype +#define booleantype int +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_SUNDIALSTYPES_H */ diff --git a/phreeqcpp/tally.cpp b/phreeqcpp/tally.cpp new file mode 100644 index 00000000..969b165d --- /dev/null +++ b/phreeqcpp/tally.cpp @@ -0,0 +1,1288 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Temperature.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* + Calling sequence + +Initialization: +--------------- + +build_tally_table(); finds all elements (rows), + possible return values (columns), + allocates space +get_tally_table_rows_columns(int *rows, int *columns) + returns number of rows and columns in table +get_tally_table_row_heading(int row, char *string) + row is C row number + returns row descripter for row +get_tally_table_column_heading(int column, int *type, char *string) + column is C column number + returns column heading for column + +Each call to phreeqc: +--------------------- + +zero_tally_table(); initialize table to 0s +set_reaction_moles(n_user, moles) n_user is reservoir number + moles is number of moles of reaction to add + +int set_reaction_temperature(n_user, tc) + +fill_tally_table(int *n_user, int index_conservative, int n_buffer) + n_user is reservoir number + index_conservative is solution number + where conservative mixing is stored + slot is 0 for initial + + run phreeqc here. + +fill_tally_table(int *n_user, int index_conservative, int n_buffer) + n_user is reservoir number + index_conservative is solution number + where conservative mixing is stored + slot is 1 for final +store_tally_table(LDBLE *array, int row_dim, int col_dim, LDBLE fill_factor) + row_dim is Fortran dimension + col_dim is Fortran dimension + array is space from Fortran + stores conservative mixing (column 0) + stores reaction (column 1) + difference between slot 1 and slot 0 for + all other entities (columns 2-n) + +Finalization: +------------- +int free_tally_table(void); Frees space + +*/ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_all_components(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Counts components in any defined solution, gas_phase, exchanger, + * surface, or pure_phase_assemblage + * + * Returns n_comp, which is total, including H, O, elements, and Charge + * names contains character strings with names of components + */ + int i, j; +/* + * Accumulate all aqueous components + */ + add_all_components_tally(); + + // add secondary master species + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ && master[i]->primary == TRUE) + { + for (int j = i + 1; j < (int)master.size(); j++) + { + if (master[j]->elt->primary == master[i]) + { + master[j]->total = 1.0; + } + else + { + break; + } + } + } + } + + +/* + * Count components + Alkalinity + total_h + total_o + */ + tally_count_component = 3; + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + tally_count_component++; + } + } +/* + * Put information in buffer. + * Buffer contains an entry for every primary master + * species that can be used in the transport problem. + */ + t_buffer = (class tally_buffer *) PHRQ_malloc( + (size_t)tally_count_component * sizeof(class tally_buffer)); + + // store alkalinity + j = 0; + t_buffer[j].name = string_hsave("Alkalinity"); + t_buffer[j].master = master_bsearch("Alkalinity"); + t_buffer[j].gfw = t_buffer[j].master->elt->gfw; + j++; + + // store total_h + t_buffer[j].name = string_hsave("Total_H"); + t_buffer[j].master = NULL; + compute_gfw("H", &(t_buffer[j].gfw)); + j++; + + // store total_o + t_buffer[j].name = string_hsave("Total_O"); + t_buffer[j].master = NULL; + compute_gfw("O", &(t_buffer[j].gfw)); + j++; + + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + t_buffer[j].name = master[i]->elt->name; + t_buffer[j].master = master[i]; + t_buffer[j].gfw = master[i]->elt->gfw; + j++; + } + } + /* + * Return value + */ + /**n_comp = count_component;*/ + count_tally_table_rows = tally_count_component; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_tally_table(LDBLE * l_array, int row_dim_in, int col_dim, LDBLE fill_factor) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + int row_dim = row_dim_in + 1; + //if (tally_table == NULL) + if (tally_table.size() == 0) + { + input_error++; + error_msg("Tally table not defined, get_tally_table_rows_columns", + CONTINUE); + return (ERROR); + } + if (count_tally_table_rows > row_dim) + { + input_error++; + error_msg + ("Too many tally table rows for Fortran storage, store_tally_table", + CONTINUE); + return (ERROR); + } + if (count_tally_table_columns > col_dim) + { + input_error++; + error_msg + ("Too many tally table columns for Fortran storage, store_tally_table", + CONTINUE); + return (ERROR); + } + /* + * store conservative mixing solution + */ + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[j] = tally_table[0].total[1][j].moles; + } + /* + * store reaction solution + */ + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[row_dim + j] = tally_table[1].total[1][j].moles; + } + /* + * Calculate deltas + */ + + diff_tally_table(); + + /* + * store deltas for all other columns + */ + for (i = 2; i < count_tally_table_columns; i++) + { + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[i * row_dim + j] = + tally_table[i].total[2][j].moles / fill_factor; + } + } + + /* + * Add row for total moles of reactant + */ + for (size_t i = 0; i < count_tally_table_columns; i++) + { + l_array[i * (size_t)row_dim + count_tally_table_rows] = + tally_table[i].moles / fill_factor; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_rows_columns(int *rows, int *columns) +/* ---------------------------------------------------------------------- */ +{ + *rows = 0; + *columns = 0; + if (tally_table.size() == 0) + { + input_error++; + error_msg("tally table not defined, get_tally_table_rows_columns", + CONTINUE); + return (ERROR); + } + *rows = (int)count_tally_table_rows; + *columns = (int)count_tally_table_columns; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_row_heading(int row, char *string) +/* ---------------------------------------------------------------------- */ +{ + /* + * row is C row number + */ + strcpy(string, ""); + if (tally_table.size() == 0) + { + input_error++; + error_msg("Tally table not defined, get_tally_table row_heading", + CONTINUE); + return (ERROR); + } + if (row >= count_tally_table_rows) + { + input_error++; + error_msg + ("Row exceeds tally table size, get_tally_table row_heading", + CONTINUE); + return (ERROR); + } + strcpy(string, t_buffer[row].name); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_column_heading(int column, int *type, char *string) +/* ---------------------------------------------------------------------- */ +{ + /* + * column is C column number + */ + *type = -1; + strcpy(string, ""); + if (tally_table.size() == 0) + { + input_error++; + error_msg("tally table not defined, get_tally_table_column_heading", + CONTINUE); + return (ERROR); + } + if (column >= count_tally_table_columns) + { + input_error++; + error_msg + ("column exceeds tally table size, get_tally_table_column_heading", + CONTINUE); + return (ERROR); + } + strcpy(string, tally_table[column].name); + *type = tally_table[column].type; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, k; + if (tally_table.size() == 0) + return (OK); + for (i = 0; i < count_tally_table_columns; i++) + { + if (tally_table[i].formula.size() != 0) + tally_table[i].formula.clear(); + for (k = 0; k < 3; k++) + { + tally_table[i].total[k] = (class tally_buffer *) free_check_null( + tally_table[i].total[k]); + } + } + //tally_table = (class tally *) free_check_null(tally_table); + t_buffer = (class tally_buffer *) free_check_null(t_buffer); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +zero_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + for (i = 0; i < count_tally_table_columns; i++) + { + tally_table[i].moles = 0.0; + for (j = 0; j < count_tally_table_rows; j++) + { + for (k = 0; k < 3; k++) + { + tally_table[i].total[k][j].moles = 0; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +diff_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + /* + output_msg("Difference\n\n"); + */ + for (i = 0; i < count_tally_table_columns; i++) + { + for (j = 0; j < count_tally_table_rows; j++) + { + tally_table[i].total[2][j].moles = + tally_table[i].total[1][j].moles - + tally_table[i].total[0][j].moles; + } + + /* + output_msg(sformatf( "Column %d\t%s\tType: %d\n", i, tally_table[i].name, tally_table[i].type)); + for (j = 0; j < count_tally_table_rows; j++) { + output_msg(sformatf( "\t%d\t%s\t%e\n", j, tally_table[i].total[2][j].name, (double) tally_table[i].total[2][j].moles)); + } + */ + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + output_msg(sformatf( "Tally_table\n\n")); + for (i = 0; i < count_tally_table_columns; i++) + { + output_msg(sformatf( "%s\tType: %d\n", tally_table[i].name, + tally_table[i].type)); + output_msg(sformatf( "\n")); + output_msg(sformatf( "\t%15s\t%15s\t%15s\n", "Initial", + "Final", "Difference")); + for (j = 0; j < count_tally_table_rows; j++) + { + output_msg(sformatf( "%5s\t%15g\t%15g\t%15g\n", + t_buffer[j].name, tally_table[i].total[0][j].moles, + tally_table[i].total[1][j].moles, + tally_table[i].total[2][j].moles)); + } + output_msg(sformatf( "\n")); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_tally_table(int *n_user, int index_conservative, int n_buffer) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. + */ + int found; + LDBLE moles; + //const char* cptr; + /* + * Cycle through tally table columns + */ + for (int i = 0; i < count_tally_table_columns; i++) + { + switch (tally_table[i].type) + { + case Solution: +/* + * fill solution + */ + if (n_user[Solution] < 0 || n_buffer == 0) + break; + { + cxxSolution *solution_ptr = NULL;; + if (i == 0) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, index_conservative); + } + else if (i == 1) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user[Solution]); + } + else + { + error_msg("Solution is not in first two columns of tally_table", STOP); + } + if (solution_ptr == NULL) + break; + /* + * Add secondary master species + */ + + + xsolution_zero(); + + // adds primary master species + add_solution(solution_ptr, 1.0, 1.0); + + // adds secondary master species + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + class master *master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + // Fill table + master_to_tally_table(tally_table[i].total[n_buffer]); + + // Add alkalinity + tally_table[i].total[n_buffer][0].moles = solution_ptr->Get_total_alkalinity(); + + // Add total_h + tally_table[i].total[n_buffer][1].moles = solution_ptr->Get_total_h(); + + // Add total_o + tally_table[i].total[n_buffer][2].moles = solution_ptr->Get_total_o(); + } + break; + case Reaction: + /* + * fill reaction + */ + if (n_user[Reaction] < 0) + break; + { + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user[Reaction]); + if (reaction_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + if (n_buffer == 1) + { + moles = reaction_ptr->Get_steps()[0]; + } + else + { + moles = 0.0; + } + reaction_calc(reaction_ptr); + add_elt_list(reaction_ptr->Get_elementList(), moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Pure_phase: + /* + * fill an equilibrium phase + */ + if (n_user[Pure_phase] < 0) + break; + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n_user[Pure_phase]); + if (pp_assemblage_ptr == NULL) + break; + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (string_hsave(it->second.Get_name().c_str()) == + tally_table[i].name) + break; + if (strcmp_nocase(it->second.Get_name().c_str(), + tally_table[i].name) == 0) + break; + } + if (it == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + break; + count_elts = 0; + paren_count = 0; + moles = it->second.Get_moles(); + tally_table[i].moles = moles; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Exchange: + { + /* + * fill exchange + */ + if (n_user[Exchange] < 0) + break; + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user[Exchange]); + if (exchange_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + add_elt_list(exchange_ptr->Get_exchange_comps()[j].Get_totals(), 1.0); + } + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Surface: + /* + * fill surface + */ + if (n_user[Surface] < 0) + break; + { + cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user[Surface]); + if (surface_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + add_elt_list(surface_ptr->Get_surface_comps()[j].Get_totals(), 1.0); + } + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Ss_phase: + if (n_user[Ss_phase] < 0) + break; + { + /* + * fill an solid solution phase + */ + + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, n_user[Ss_phase]); + if (ss_assemblage_ptr == NULL) + break; + found = FALSE; + moles = 0.0; + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + cxxSScomp * comp_ptr = NULL; + size_t k; + for (k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + if (phase_ptr->name == tally_table[i].name) + break; + if (strcmp_nocase(phase_ptr->name, tally_table[i].name) == 0) + break; + } + if (k < ss_ptr->Get_ss_comps().size() && comp_ptr) + { + moles = comp_ptr->Get_moles(); + found = TRUE; + break; + } + } + if (found == FALSE) + break; + count_elts = 0; + paren_count = 0; + tally_table[i].moles = moles; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Gas_phase: + /* + * fill in gas phase + */ + if (n_user[Gas_phase] < 0) + break; + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, n_user[Gas_phase]); + if (gas_phase_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + const std::vector *gc = &(gas_phase_ptr->Get_gas_comps()); + for (size_t l = 0; l < gc->size(); l++) + { + int k; + class phase *phase_ptr = phase_bsearch((*gc)[l].Get_phase_name().c_str(), &k, FALSE); + + add_elt_list(phase_ptr->next_elt, (*gc)[l].Get_moles()); + } + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + break; + } + case Kinetics: + { + /* + * fill in kinetics + */ + if (n_user[Kinetics] < 0) + break; + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user[Kinetics]); + if (kinetics_ptr == NULL) + break; + cxxKineticsComp * kinetics_comp_ptr = NULL; + size_t j; + for (j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()) == tally_table[i].name) + break; + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), tally_table[i].name) == 0) + break; + } + if (j >= kinetics_ptr->Get_kinetics_comps().size()) + break; + moles = 0.0; + if (kinetics_comp_ptr) + { + moles = kinetics_comp_ptr->Get_m(); + } + tally_table[i].moles = moles; + count_elts = 0; + paren_count = 0; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Mix: + break; + case Temperature: + case Pressure: + break; + case UnKnown: + break; + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_to_tally_table(class tally_buffer *buffer_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + for (i = 0; i < count_tally_table_rows; i++) + { + buffer_ptr[i].moles = 0.0; + } + /* + * copy element list amounts to buffer in tally table + * for column number + */ + + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->primary->s == s_h2o) + continue; + if (elt_list[j].elt->primary->s == s_hplus) + continue; + if (elt_list[j].elt->primary->s == s_h3oplus) + continue; + if (elt_list[j].elt->primary->type != AQ) + continue; + for (i = 0; i < count_tally_table_rows; i++) + { + if (buffer_ptr[i].master != NULL) + { + if (elt_list[j].elt->primary == + buffer_ptr[i].master->elt->primary) + { + buffer_ptr[i].moles = elt_list[j].coef; + break; + } + } + } + if (i >= count_tally_table_rows) + { + error_msg("Should not be here in elt_list_to_tally_table", STOP); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_to_tally_table(class tally_buffer *buffer_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + for (i = 0; i < count_tally_table_rows; i++) + { + buffer_ptr[i].moles = 0.0; + } + /* + * copy element list amounts to buffer in tally table + * for column number + */ + for (j = 0; j < (int)master.size(); j++) + { + if (master[j]->total <= 0) + continue; + if (master[j]->elt->primary->s == s_h2o) + continue; + if (master[j]->elt->primary->s == s_hplus) + continue; + if (master[j]->elt->primary->s == s_h3oplus) + continue; + if (master[j]->elt->primary->type != AQ) + continue; + for (i = 0; i < count_tally_table_rows; i++) + { + if (master[j] == buffer_ptr[i].master) + { + buffer_ptr[i].moles = master[j]->total; + break; + } + } + if (i >= count_tally_table_rows) + { + error_msg("Should not be here in master_to_tally_table", STOP); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. Counts number of aqueous components + * to transport. Stores in global variable tally_count_component. + * Also calculates a number greater than all user numbers and + * stores in global variable first_user_number. + */ + int j, k, l, p, save_print_use; + size_t n; + int count_tt_pure_phase, count_tt_ss_phase, count_tt_kinetics; + class phase *phase_ptr; + char token[MAX_LENGTH]; + const char* cptr; +/* + * make list of all elements in all entitites + * defines the number of rows in the table + */ + get_all_components(); + + save_print_use = pr.use; + pr.use = FALSE; +/* + * find nuber of columns + */ + count_tally_table_columns = 0; +/* + * add one for conservative mixing + */ + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Solution_conservative"); + tally_table[n].type = Solution; +/* + * add one for mixing plus reaction + */ + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Solution_reaction"); + tally_table[n].type = Solution; +/* + * add one for reactions + */ + if (Rxn_reaction_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Reaction"); + tally_table[n].type = Reaction; + } +/* + * add one for exchangers + */ + if (Rxn_exchange_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Exchange"); + tally_table[n].type = Exchange; + } +/* + * add one for surface + */ + if (Rxn_surface_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Surface"); + tally_table[n].type = Surface; + } +/* + * add one for gases + */ + if (Rxn_gas_phase_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Gas_phase"); + tally_table[n].type = Gas_phase; + } +/* + * Count pure phases + */ + count_tt_pure_phase = 0; + if (Rxn_pp_assemblage_map.size() > 0) + { + /* + * Go through all pure phases in pure phase assemblages + */ + std::map::iterator it; + for (it = Rxn_pp_assemblage_map.begin(); it != Rxn_pp_assemblage_map.end(); it++) + { + cxxPPassemblage * pp_assemblage_ptr = &(it->second); + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + cxxPPassemblageComp * comp_ptr = &(jit->second); + int l; + class phase * phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + /* + * check if already in tally_table + */ + for (k = 1; k < count_tally_table_columns; k++) + { + if (tally_table[k].type == Pure_phase && + tally_table[k].name == phase_ptr->name && + tally_table[k].add_formula == + string_hsave(comp_ptr->Get_add_formula().c_str())) + break; + } + if (k < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_pure_phase++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = phase_ptr->name; + tally_table[n].type = Pure_phase; + tally_table[n].add_formula = string_hsave(comp_ptr->Get_add_formula().c_str()); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_add_formula().size() > 0) + { + strcpy(token, comp_ptr->Get_add_formula().c_str()); + cptr = &(token[0]); + get_elts_in_species(&cptr, 1.0); + } + else + { + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + } + elt_list_combine(); + tally_table[n].formula = elt_list_vsave(); + } + } + } +/* + * Add solid-solution pure phases + */ + count_tt_ss_phase = 0; + if (Rxn_ss_assemblage_map.size() > 0) + { + /* + * Go through all components of all solid solutions in solid-solution assemblages + */ + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + cxxSSassemblage *ss_assemblage_ptr = &(it->second); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (k = 0; k < (int) ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + /* + * check if already in tally_table + */ + for (l = 1; l < count_tally_table_columns; l++) + { + if (tally_table[l].type == Ss_phase && + tally_table[l].name == phase_ptr->name) + break; + } + if (l < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_ss_phase++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = phase_ptr->name; + tally_table[n].type = Ss_phase; + count_elts = 0; + paren_count = 0; + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + elt_list_combine(); + tally_table[n].formula = elt_list_vsave(); + } + } + } + } +/* + * Add kinetic reactants + */ + count_tt_kinetics = 0; + if (Rxn_kinetics_map.size() > 0) + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + cxxKinetics *kinetics_ptr = &(it->second); + for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + /* + * check if already in tally_table + */ + for (l = 1; l < count_tally_table_columns; l++) + { + if (tally_table[l].type == Kinetics && + tally_table[l].name == string_hsave(kinetics_comp_ptr->Get_rate_name().c_str())) + break; + } + if (l < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_kinetics++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()); + tally_table[n].type = Kinetics; + /* + * get formula for kinetic component + */ + count_elts = 0; + paren_count = 0; + phase_ptr = NULL; + if (kinetics_comp_ptr->Get_namecoef().size() == 1) + { + strcpy(token, kinetics_comp_ptr->Get_namecoef().begin()->first.c_str()); + phase_ptr = phase_bsearch(token, &p, FALSE); + } + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, 1.0); + } + else + { + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef = it->second; + cptr = name.c_str(); + get_elts_in_species(&cptr, 1.0 * coef); + } + } + elt_list_combine(); + tally_table[n].formula = elt_list_vsave(); + } + } + } + + pr.use = save_print_use; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +add_all_components_tally(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. Counts number of aqueous components + * to transport. Stores in global variable tally_count_component. + * Also calculates a number greater than all user numbers and + * stores in global variable first_user_number. + */ + int save_print_use; + + save_print_use = pr.use; + pr.use = FALSE; +/* + * Delete solutions less than -1 + */ + +/* + * add all solutions + */ + xsolution_zero(); + { + std::map::iterator it = Rxn_solution_map.begin(); + for (; it != Rxn_solution_map.end(); it++) + { + add_solution(&it->second, 1.0 / it->second.Get_mass_water(), 1.0); + } + } +/* + * add all irrev reactions + */ + { + std::map::iterator it = Rxn_reaction_map.begin(); + for (; it != Rxn_reaction_map.end(); it++) + { + add_reaction(&it->second, 1, 1.0); + } + } +/* + * Add pure phases + */ + { + std::map::iterator it = Rxn_pp_assemblage_map.begin(); + for (; it != Rxn_pp_assemblage_map.end(); it++) + { + add_pp_assemblage(&(it->second)); + } + } +/* + * Exchangers + */ + { + std::map::iterator it = Rxn_exchange_map.begin(); + for (; it != Rxn_exchange_map.end(); it++) + { + add_exchange(&it->second); + } + } +/* + * Surfaces + */ + { + std::map::iterator it = Rxn_surface_map.begin(); + for (; it != Rxn_surface_map.end(); it++) + { + add_surface(&it->second); + } + } +/* + * Gases + */ + { + std::map::iterator it = Rxn_gas_phase_map.begin(); + for ( ; it != Rxn_gas_phase_map.end(); it++) + { + add_gas_phase(&it->second); + } + } +/* + * Add solid-solution pure phases + */ + { + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + add_ss_assemblage(&(it->second)); + } + } +/* + * Add elements in kinetic reactions + */ + { + std::map::iterator it = Rxn_kinetics_map.begin(); + for ( ; it != Rxn_kinetics_map.end(); it++) + { + calc_dummy_kinetic_reaction_tally(&(it->second)); + add_kinetics(&(it->second)); + } + } +/* + * reset pr.use + */ + pr.use = save_print_use; + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_dummy_kinetic_reaction_tally(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components and add positive amount of each reactant + */ + LDBLE coef; + const char* cptr; + class phase *phase_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + kinetics_ptr->Get_totals().clear(); + count_elts = 0; + paren_count = 0; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + coef = 1.0; +/* + * Reactant is a pure phase, copy formula into token + */ + phase_ptr = NULL; + if (kinetics_comp_ptr->Get_namecoef().size() == 1) + { + std::string name = kinetics_comp_ptr->Get_namecoef().begin()->first; + int j; + phase_ptr = phase_bsearch(name.c_str(), &j, FALSE); + } + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, coef); + } + else + { + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + cptr = it->first.c_str(); + get_elts_in_species(&cptr, coef); + } + } + } + kinetics_ptr->Set_totals(elt_list_NameDouble()); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +extend_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + /* + * adds another column to tally_table + * increments number of columns + */ + //tally_table = (class tally *) PHRQ_realloc((void *) tally_table, + // (count_tally_table_columns + 1) * sizeof(class tally)); + //if (tally_table == NULL) + // malloc_error(); + tally_table.resize(count_tally_table_columns + 1); + for (i = 0; i < 3; i++) + { + tally_table[count_tally_table_columns].total[i] = (class tally_buffer *) + PHRQ_malloc(count_tally_table_rows * sizeof(class tally_buffer)); + if (tally_table[count_tally_table_columns].total[i] == NULL) + malloc_error(); + for (j = 0; j < count_tally_table_rows; j++) + { + tally_table[count_tally_table_columns].total[i][j].name = + t_buffer[j].name; + tally_table[count_tally_table_columns].total[i][j].master = + t_buffer[j].master; + } + } + tally_table[count_tally_table_columns].name = NULL; + tally_table[count_tally_table_columns].type = UnKnown; + tally_table[count_tally_table_columns].add_formula = NULL; + tally_table[count_tally_table_columns].moles = 0.0; + count_tally_table_columns++; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction_moles(int n_user, LDBLE moles) +/* ---------------------------------------------------------------------- */ +{ + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user); + if (reaction_ptr == NULL) + return (ERROR); + std::vector v; + v.push_back(moles); + reaction_ptr->Set_steps(v); + reaction_ptr->Set_countSteps(1); + reaction_ptr->Set_equalIncrements(true); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction_temperature(int n_user, LDBLE tc) +/* ---------------------------------------------------------------------- */ +{ + cxxTemperature * temperature_ptr = Utilities::Rxn_find(Rxn_temperature_map, n_user); + if (temperature_ptr == NULL) + return (ERROR); + temperature_ptr->Get_temps().clear(); + temperature_ptr->Get_temps().push_back(tc); + temperature_ptr->Set_equalIncrements(false); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_kinetics_time(int n_user, LDBLE step) +/* ---------------------------------------------------------------------- */ +{ + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user); + + if (kinetics_ptr == NULL) + return (ERROR); + kinetics_ptr->Get_steps().clear(); + kinetics_ptr->Get_steps().push_back(step); + kinetics_ptr->Set_equalIncrements(false); + return (OK); +} + diff --git a/phreeqcpp/test.xml b/phreeqcpp/test.xml new file mode 100644 index 00000000..ed476808 --- /dev/null +++ b/phreeqcpp/test.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + diff --git a/phreeqcpp/tidy.cpp b/phreeqcpp/tidy.cpp new file mode 100644 index 00000000..4c95089b --- /dev/null +++ b/phreeqcpp/tidy.cpp @@ -0,0 +1,5576 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +#define ZERO_TOL 1.0e-30 + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_model(void) +/* ---------------------------------------------------------------------- */ +{ + int n_user, last; + int new_named_logk; + /* + * Determine if any new elements, species, phases have been read + */ + overall_iterations = 0; + state = INITIALIZE; + new_model = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_exchange = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_surface = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_pitzer = FALSE; + new_named_logk = FALSE; + + if (keycount[Keywords::KEY_SOLUTION_SPECIES] > 0 || /*"species" */ + keycount[Keywords::KEY_SOLUTION_MASTER_SPECIES] > 0 || /*"master" */ + keycount[Keywords::KEY_PHASES] > 0 || /*"phases" */ + keycount[Keywords::KEY_EXCHANGE_SPECIES] > 0 || /*"exchange_species" */ + keycount[Keywords::KEY_EXCHANGE_MASTER_SPECIES] > 0 || /*"master_exchange_species" */ + keycount[Keywords::KEY_SURFACE_SPECIES] > 0 || /*"surface_species" */ + keycount[Keywords::KEY_SURFACE_MASTER_SPECIES] > 0 || /*"master_surface_species" */ + keycount[Keywords::KEY_RATES] > 0 || /*"rates" */ + keycount[Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS] > 0 || /*"llnl_aqueous_model_parameters" */ + (keycount[Keywords::KEY_DATABASE] > 0 && simulation == 0) || /*"database" */ + keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0 || /*"named_analytical_expressions" */ + keycount[Keywords::KEY_ISOTOPES] > 0 || /*"isotopes" */ + keycount[Keywords::KEY_CALCULATE_VALUES] > 0 || /*"calculate_values" */ + keycount[Keywords::KEY_ISOTOPE_RATIOS] > 0 || /*"isotopes_ratios", */ + keycount[Keywords::KEY_ISOTOPE_ALPHAS] > 0 || /*"isotopes_alphas" */ + keycount[Keywords::KEY_PITZER] > 0 || /*"pitzer" */ + keycount[Keywords::KEY_SIT] > 0 /*"sit" */ + ) + { + new_model = TRUE; + } + if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY]) + { + new_pp_assemblage = TRUE; /*"pure_phases" */ + } + if (keycount[Keywords::KEY_SURFACE] > 0 || + keycount[Keywords::KEY_SURFACE_RAW] > 0 || + keycount[Keywords::KEY_SURFACE_MODIFY]) + { + new_surface = TRUE; /*"surface" */ + } + if (keycount[Keywords::KEY_EXCHANGE] > 0 || + keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || + keycount[Keywords::KEY_EXCHANGE_MODIFY]) + { + new_exchange = TRUE; /*"exchange" */ + } + if (keycount[Keywords::KEY_REACTION] > 0 /*|| + keycount[Keywords::KEY_REACTION_RAW] > 0 || + keycount[Keywords::KEY_REACTION_MODIFY]*/) + { + new_reaction = TRUE; /*"reaction" */ + } + if (keycount[Keywords::KEY_REACTION_TEMPERATURE] > 0 /*|| + keycount[Keywords::KEY_REACTION_TEMPERATURE_RAW] > 0 || + keycount[Keywords::KEY_REACTION_TEMPERATURE_MODIFY]*/) + { + new_temperature = TRUE; /*"reacton_temperature" */ + } + if (keycount[Keywords::KEY_MIX] > 0 || + keycount[Keywords::KEY_MIX_RAW] > 0) + { + new_mix = TRUE; /*"mix" */ + } + if (keycount[Keywords::KEY_SOLUTION] > 0 || + keycount[Keywords::KEY_SOLUTION_SPREAD] > 0 || + keycount[Keywords::KEY_SOLUTION_RAW] > 0 || + keycount[Keywords::KEY_SOLUTION_MODIFY]) + { /*"solution" */ + new_solution = TRUE; + } + if (keycount[Keywords::KEY_GAS_PHASE] > 0 || + keycount[Keywords::KEY_GAS_PHASE_RAW] > 0 || + keycount[Keywords::KEY_GAS_PHASE_MODIFY]) + { + new_gas_phase = TRUE; /*"gas_phase" */ + } + if (keycount[Keywords::KEY_SOLID_SOLUTIONS] > 0 || + keycount[Keywords::KEY_SOLID_SOLUTIONS_RAW] > 0 || + keycount[Keywords::KEY_SOLID_SOLUTIONS_MODIFY]) + { + new_ss_assemblage = TRUE; /*"solid_solutions" */ + } + if (keycount[Keywords::KEY_KINETICS] > 0 /*|| + keycount[Keywords::KEY_KINETICS_RAW] > 0 || + keycount[Keywords::KEY_KINETICS_MODIFY]*/) + { + new_kinetics = TRUE; /*"kinetics" */ + } + if (keycount[Keywords::KEY_INVERSE_MODELING] > 0) + { + new_inverse = TRUE; /*"inverse_modeling" */ + } + if (keycount[Keywords::KEY_SELECTED_OUTPUT] > 0 || /*"selected_output" */ + keycount[Keywords::KEY_USER_PUNCH] > 0) /*"user_punch" */ + { + new_punch = TRUE; + } + + if (keycount[Keywords::KEY_COPY] > 0) + { + new_copy = TRUE; /*"copy" */ + } + if (keycount[Keywords::KEY_PITZER] > 0) + { + new_pitzer = TRUE; /*"pitzer" */ + } + if (keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0) + { + new_named_logk = TRUE; /*"named_log_k" */ + } + +/* + * Sort arrays + */ + +/* species */ + if (new_model == TRUE) + { + if (s.size() > 1) qsort(&s[0], s.size(), sizeof(class species *), s_compare); + +/* master species */ + if (master.size() > 1) qsort(&master[0], master.size(), sizeof(class master *), master_compare); +/* elements */ + if (elements.size() > 1) qsort(&elements[0], elements.size(), sizeof(class element *), element_compare); +/* phases */ + if (phases.size() > 1) qsort(&phases[0], phases.size(), sizeof(class phase *), phase_compare); + + } + +/* named_log_k */ + if (new_named_logk) + { + tidy_logk(); + } +/* + * Check pointers, write reactions for species + */ + if (new_model) + { + sum_species_map.clear(); + + tidy_species(); + + tidy_phases(); + + tidy_master_isotope(); +/* + * calculate gfw of water, kg/mole + */ + compute_gfw("H2O", &gfw_water); + gfw_water *= 0.001; + } +/* + * tidy surface data + */ + if (new_model || new_surface) + { + tidy_surface(); + } +/* + * tidy inverse data + */ + if (new_inverse) + { + tidy_inverse(); + } +/* + * tidy gas phase data + */ + if (new_gas_phase) + { + tidy_gas_phase(); + } +/* + * tidy pp_assemblage data + */ + if (new_model || new_pp_assemblage) + { + tidy_pp_assemblage(); + } +/* + * tidy ss_assemblage data + */ + if (new_model || new_ss_assemblage) + { + tidy_ss_assemblage(); + } +/* + * tidy exchange data, after pp_assemblages + */ + if (new_exchange) + { + tidy_exchange(); + tidy_min_exchange(); + tidy_kin_exchange(); + } +/* + * tidy surface data + */ + if (new_surface) + { + tidy_min_surface(); + tidy_kin_surface(); + } +/* + * tidy solution isotope data + */ + if (new_solution) + { + tidy_isotopes(); + } + if (new_model) + { + tidy_isotope_ratios(); + tidy_isotope_alphas(); + } +/* + * Duplicate kinetics + */ + if (new_kinetics) + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + n_user = it->second.Get_n_user(); + last = it->second.Get_n_user_end(); + it->second.Set_n_user_end(n_user); + Utilities::Rxn_copies(Rxn_kinetics_map, n_user, last); + } + } + +/* + * Tidy pitzer information + */ + if (pitzer_model && new_model) + { + pitzer_tidy(); + } +/* + * Tidy SIT information + */ + if (sit_model && new_model) + { + sit_tidy(); + } +/* + * Tidy punch information + */ + if (get_input_errors() == 0 && (new_punch || new_model)) + { + tidy_punch(); + } +/* + * Tidy solution information + */ + if (new_solution) + { + tidy_solutions(); + } +/* +* need to update exchange and surface related in case anything has changed +*/ + if (keycount[Keywords::KEY_KINETICS] > 0 || + keycount[Keywords::KEY_KINETICS_RAW] > 0 || + keycount[Keywords::KEY_KINETICS_MODIFY] || + keycount[Keywords::KEY_EXCHANGE] > 0 || + keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || + keycount[Keywords::KEY_EXCHANGE_MODIFY]) + { + update_kin_exchange(); + } + if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY] || + keycount[Keywords::KEY_EXCHANGE] > 0 || + keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || + keycount[Keywords::KEY_EXCHANGE_MODIFY]) + { + update_min_exchange(); + } + if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY] || + keycount[Keywords::KEY_SURFACE] > 0 || + keycount[Keywords::KEY_SURFACE_RAW] > 0 || + keycount[Keywords::KEY_SURFACE_MODIFY] > 0) + { + update_min_surface(); + } + if (keycount[Keywords::KEY_KINETICS] > 0 || + keycount[Keywords::KEY_KINETICS_RAW] > 0 || + keycount[Keywords::KEY_KINETICS_MODIFY] > 0 || + keycount[Keywords::KEY_SURFACE] > 0 || + keycount[Keywords::KEY_SURFACE_RAW] > 0 || + keycount[Keywords::KEY_SURFACE_MODIFY] > 0) + { + update_kin_surface(); + } + /* if (new_model || new_exchange || new_pp_assemblage || new_surface || new_gas_phase || new_kinetics) reset_last_model(); */ + if (new_model) + { + reset_last_model(); + } +/* + * make sure essential species are defined + */ + //if (new_model) + { + if (s_h2o == NULL) + { + input_error++; + //error_msg("H2O not defined.", STOP); + error_msg("H2O not defined.", CONTINUE); + } + else + { + if (s_h2o->primary == NULL) + { + input_error++; + error_msg("H2O, primary master species for O, not defined.", + CONTINUE); + } + if (s_h2o->secondary == NULL) + { + input_error++; + error_msg("H2O, secondary master species for O(-2), not defined.", + CONTINUE); + } + if (s_h2o->type != H2O) + { + input_error++; + error_msg("H2O can only be defined in SOLUTION_SPECIES.", + CONTINUE); + } + } + if (s_hplus == NULL && s_h3oplus == NULL) + { + input_error++; + error_msg("Neither H+ nor H3O+ are defined in solution_species.", + STOP); + } + else if (s_hplus == NULL && s_h3oplus != NULL) + { + s_hplus = s_h3oplus; + s_h3oplus = NULL; + } + else if (s_hplus != NULL && s_h3oplus == NULL) + { + } + else if (s_hplus != NULL && s_h3oplus != NULL) + { + input_error++; + error_msg("Cannot define both H+ and H3O+ in solution_species.", + STOP); + } + if (s_hplus->primary == NULL) + { + input_error++; + error_msg("H3O+, primary master species for H, not defined.", + CONTINUE); + } + if (s_hplus->secondary == NULL) + { + input_error++; + error_msg("H3O+, secondary master species for H(1), not defined.", + CONTINUE); + } + if (s_eminus == NULL) + { + input_error++; + error_msg("e- not defined in solution_species.", CONTINUE); + } + if (s_eminus->primary == NULL) + { + input_error++; + error_msg("e-, primary master species for E-, not defined.", + CONTINUE); + } + if (pitzer_model == FALSE || pitzer_pe == TRUE) + { + if (s_h2 == NULL) + { + input_error++; + error_msg("H2(aq) not defined in solution_species.", CONTINUE); + } + if (s_o2 == NULL) + { + input_error++; + error_msg("O2(aq) not defined in solution_species.", CONTINUE); + } + } + element_h_one = element_store("H(1)"); + if (element_h_one == NULL) + { + input_error++; + error_msg("H(1) not defined in solution_master_species.", CONTINUE); + } + } +/* + * Error check, program termination + */ + if (get_input_errors() > 0 || parse_error > 0) + { + error_msg("Calculations terminating due to input errors.", STOP); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_species_input(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check species data for completeness + */ + int i; + int return_value; + + return_value = OK; + for (i = 0; i < (int)s.size(); i++) + { + if (s[i]->next_elt.size() == 0) + { + input_error++; + return_value = ERROR; + error_string = sformatf( + "Elements in species have not been tabulated, %s.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + if (s[i]->rxn.token.size() == 0) + { + input_error++; + return_value = ERROR; + error_string = sformatf( + "Reaction for species has not been defined, %s.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + else + { + select_log_k_expression(s[i]->logk, s[i]->rxn.logk); + add_other_logk(s[i]->rxn.logk, s[i]->add_logk); + } + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +select_log_k_expression(LDBLE * source_k, LDBLE * target_k) +/* ---------------------------------------------------------------------- */ +{ + int j; + bool analytic; + + analytic = false; + for (j = T_A1; j <= T_A6; j++) + { + if (source_k[j] != 0.0) + { + analytic = true; + break; + } + } + if (analytic) + { + target_k[logK_T0] = 0.0; + target_k[delta_h] = 0.0; + for (j = T_A1; j <= T_A6; j++) + { + target_k[j] = source_k[j]; + } + } + else + { + target_k[logK_T0] = source_k[logK_T0]; + target_k[delta_h] = source_k[delta_h]; + for (j = T_A1; j <= T_A6; j++) + { + target_k[j] = 0.0; + } + } + for (j = delta_v; j < MAX_LOG_K_INDICES; j++) + { + target_k[j] = source_k[j]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_logk(void) +/* ---------------------------------------------------------------------- */ +/* + * Picks log k expression + */ +{ + int i; + for (i = 0; i < (int)logk.size(); i++) + { + select_log_k_expression(logk[i]->log_k_original, logk[i]->log_k); + logk[i]->done = FALSE; + } + for (i = 0; i < (int)logk.size(); i++) + { + if (logk[i]->done == FALSE) + { + add_logks(logk[i], 0); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_other_logk(LDBLE * source_k, std::vector &add_logk) +/* ---------------------------------------------------------------------- */ +{ + int j; + bool analytic; + class logk *logk_ptr; + LDBLE coef; + + for (size_t i = 0; i < add_logk.size(); i++) + { + coef = add_logk[i].coef; + std::string token = add_logk[i].name; + str_tolower(token); + std::map::iterator l_it = logk_map.find(token); + if (l_it == logk_map.end()) + { + input_error++; + error_string = sformatf( + "Could not find named temperature expression, %s\n", + token.c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + logk_ptr = l_it->second; + analytic = false; + for (j = T_A1; j <= T_A6; j++) + { + if (logk_ptr->log_k[j] != 0.0) + { + analytic = true; + break; + } + } + if (analytic) + { + for (j = T_A1; j <= T_A6; j++) + { + source_k[j] += logk_ptr->log_k[j] * coef; + } + } + else + { + source_k[logK_T0] += logk_ptr->log_k[logK_T0] * coef; + source_k[delta_h] += logk_ptr->log_k[delta_h] * coef; + } + for (j = delta_v; j < MAX_LOG_K_INDICES; j++) + { + source_k[j] += logk_ptr->log_k[j] * coef; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_logks(class logk *logk_ptr, int repeats) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + class logk *next_logk_ptr; + LDBLE coef; + /* + * Adds in other named_expressions to get complete log K + * Evaluates others recursively if necessary + */ + if (repeats > 15) + { + input_error++; + error_string = sformatf( "Circular definition of named_logk? %s\n", + logk_ptr->name); + error_msg(error_string, CONTINUE); + return (ERROR); + } + for (i = 0; i < (int)logk_ptr->add_logk.size(); i++) + { + coef = logk_ptr->add_logk[i].coef; + std::string token = logk_ptr->add_logk[i].name; + str_tolower(token); + std::map::iterator l_it = logk_map.find(token); + if (l_it == logk_map.end()) + { + input_error++; + error_string = sformatf( + "Could not find named temperature expression, %s\n", + token.c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + next_logk_ptr = l_it->second; + if (next_logk_ptr->done == FALSE) + { + /*output_msg(sformatf( "Done == FALSE\n", token)); */ + if (add_logks(next_logk_ptr, repeats + 1) == ERROR) + { + return (ERROR); + } + } + for (j = 0; j < MAX_LOG_K_INDICES; j++) + { + logk_ptr->log_k[j] += next_logk_ptr->log_k[j] * coef; + } + } + logk_ptr->done = TRUE; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +coef_in_master(class master * master_ptr) +/* ---------------------------------------------------------------------- */ +{ + int l; + LDBLE coef; + const char* cptr; + std::string elt_name; + const class elt_list *next_elt; + + coef = 0.0; + cptr = master_ptr->elt->name; + get_elt(&cptr, elt_name, &l); + for (next_elt = &master_ptr->s->next_elt[0]; next_elt->elt != NULL; + next_elt++) + { + if (strcmp(elt_name.c_str(), next_elt->elt->name) == 0) + { + coef = next_elt->coef; + break; + } + } + return (coef); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_eqn_to_secondary(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for species in terms of secondary species + * Result is in trxn. + */ + LDBLE coef; + int repeat, i, add_count; + class rxn_token_temp *token_ptr; +/* + * + */ + add_count = 0; + repeat = TRUE; +/* + * Reduce reaction equation to primary and secondary species + */ + while (repeat == TRUE) + { + repeat = FALSE; + /* Check for too many iterations */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not reduce equation to secondary master species, %s.", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + break; + } + + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s == NULL) + { + error_string = sformatf( + "NULL species pointer for species, %s.", + token_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (token_ptr->s->secondary == NULL + && token_ptr->s->primary == NULL) + { + coef = token_ptr->coef; + trxn_add(token_ptr->s->rxn, coef, true); + repeat = TRUE; + break; + } + } + } + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +replace_solids_gases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for species in terms of secondary species + * Result is in trxn. + */ + LDBLE coef; + int n; + int repeat, i, add_count; + class rxn_token_temp *token_ptr; + class phase *phase_ptr; + int replaced; + char token[MAX_LENGTH]; +/* + * + */ + add_count = 0; + repeat = TRUE; + replaced = FALSE; +/* + * Reduce reaction equation to primary and secondary species + */ + while (repeat == TRUE) + { + repeat = FALSE; + /* Check for too many iterations */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not remove all solids and gases from equation, %s.", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + break; + } + + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s == NULL) + { + phase_ptr = phase_bsearch(token_ptr->name, &n, FALSE); + /* try phase name without (g) or (s) */ + if (phase_ptr == NULL) + { + strcpy(token, token_ptr->name); + replace("(g)", "", token); + replace("(s)", "", token); + replace("(G)", "", token); + replace("(S)", "", token); + phase_ptr = phase_bsearch(token, &n, FALSE); + } + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( "Phase not found, %s.", + token_ptr->name); + error_msg(error_string, CONTINUE); + break; + } + coef = token_ptr->coef; + /* add reaction for solid/gas */ + /* debug + output_msg(sformatf( "Reaction to add.\n")); + rxn_print(phase_ptr->rxn); + */ + trxn_add_phase(phase_ptr->rxn, coef, false); + + /* remove solid/gas from trxn list */ + trxn.token[i].name = phase_ptr->rxn.token[0].name; + trxn.token[i].s = phase_ptr->rxn.token[0].s; + trxn.token[i].coef = -coef * phase_ptr->rxn.token[0].coef; + repeat = TRUE; + replaced = TRUE; + /* debug + output_msg(sformatf( "Before combined.\n")); + trxn_print(); + */ + /* combine */ + trxn_combine(); + /* debug + output_msg(sformatf( "Combined.\n")); + trxn_print(); + */ + break; + } + } + } + trxn_combine(); + return (replaced); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_eqn_to_primary(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for secondary master species in terms of primary master species + * Store result in reaction structure for master species + * rewrite if necessary. + * + */ + int repeat, j, add_count; + +/* + * Check secondary master species + */ + repeat = TRUE; + add_count = 0; +/* + * Check if reaction contains only primary master species + */ + while (repeat == TRUE) + { + repeat = FALSE; +/* + * Check for too many iterations + */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not reduce equation to primary master species, %s.", + trxn.token[0].s->name); + error_msg(error_string, CONTINUE); + break; + } +/* + * Go through species in reaction for secondary master species, look for non-primary + * species as reactants, rewrite + */ + for (j = 1; j < count_trxn; j++) + { + if (trxn.token[j].s->primary == NULL) + { + trxn_add(trxn.token[j].s->rxn, trxn.token[j].coef, true); + repeat = TRUE; + break; + } + } + } + trxn_combine(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ + int n_user, last; + LDBLE P, V_m; + bool PR; +/* + * Find all gases for each gas_phase in phase list + */ + for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) + { + std::map::iterator it = Rxn_gas_phase_map.find(*nit); + if (it == Rxn_gas_phase_map.end()) + { + assert(false); + } + cxxGasPhase *gas_phase_ptr = &(it->second); + PR = false; + P = 0.0; + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + int k; + class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Gas not found in PHASES database, %s.", + gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + if (phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + } + } + gas_phase_ptr->Set_pr_in(PR); + + if (gas_phase_ptr->Get_new_def()) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + /* + * Fixed pressure + */ + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_phase_ptr->Get_solution_equilibria()) + { + input_error++; + error_string = sformatf( + "Gas phase %d: cannot use '-equilibrium' option with fixed pressure gas phase.", + gas_phase_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + } + /* calculate moles */ +#ifdef NPP + if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) +#else + if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) +#endif + { + P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); + if (!PR) + { + double moles = gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * gas_phase_ptr->Get_volume() / + R_LITER_ATM / gas_phase_ptr->Get_temperature(); + gas_phase_ptr->Get_gas_comps()[j].Set_moles(moles); + gas_phase_ptr->Get_gas_comps()[j].Set_p(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); + gas_phase_ptr->Get_gas_comps()[j].Set_phi(1.0); + gas_phase_ptr->Get_gas_comps()[j].Set_f(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); + } + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: partial pressure of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + else + { + /* + * Fixed volume + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { +#ifdef NPP + if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) +#else + if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) +#endif + { + P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); + if (!PR) + { + double moles = gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * + gas_phase_ptr->Get_volume() / R_LITER_ATM / + gas_phase_ptr->Get_temperature(); + gas_phase_ptr->Get_gas_comps()[j].Set_moles(moles); + gas_phase_ptr->Get_gas_comps()[j].Set_p(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); + gas_phase_ptr->Get_gas_comps()[j].Set_phi(1.0); + gas_phase_ptr->Get_gas_comps()[j].Set_f(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); + } + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: moles of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), + gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + + if (PR && P > 0) + { + std::vector phase_ptrs; + size_t j_PR; + std::vector &gc = gas_phase_ptr->Get_gas_comps(); + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + { + gc[j_PR].Set_moles(0.0); + gc[j_PR].Set_p(0.0); + gc[j_PR].Set_phi(1.0); + gc[j_PR].Set_f(0.0); + continue; + } + if (phase_ptr) + { + phase_ptr->moles_x = gc[j_PR].Get_p_read() / P; + phase_ptrs.push_back(phase_ptr); + } + } + V_m = calc_PR(phase_ptrs, P, gas_phase_ptr->Get_temperature(), 0); + gas_phase_ptr->Set_v_m(V_m); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_p(P); + } + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + class phase *phase_ptr = phase_bsearch(gc[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + { + gc[j_PR].Set_moles(0.0); + gc[j_PR].Set_p(0.0); + gc[j_PR].Set_phi(1.0); + gc[j_PR].Set_f(0.0); + } else + { + if (phase_ptr) + { + gc[j_PR].Set_moles(phase_ptr->moles_x * gas_phase_ptr->Get_volume() / V_m); + gc[j_PR].Set_p(gc[j_PR].Get_p_read()); + gc[j_PR].Set_phi(phase_ptr->pr_phi); + gc[j_PR].Set_f(gc[j_PR].Get_p_read()* phase_ptr->pr_phi); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gc[j_PR].Get_moles()); + } + } + } + } + /* + * Duplicate gas phase, only if not solution equilibria + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { + gas_phase_ptr->Set_new_def(false); + n_user = gas_phase_ptr->Get_n_user(); + last = gas_phase_ptr->Get_n_user_end(); + gas_phase_ptr->Set_n_user_end(n_user); + for (int j1 = n_user + 1; j1 <= last; j1++) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, n_user, j1); + } + } + else + { + gas_phase_ptr->Set_new_def(true); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_inverse(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * After all of data are read, fill in data for an inverse structure, + * including master species pointers, phase pointers, and uncertainties + * and a list of all elements from phases or -balance input. + */ + int i, j, k, l; + int count_in; + LDBLE value; + class master *master_ptr; + class master *master_alk_ptr; + const class elt_list *elt_list_ptr; + master_alk_ptr = master_bsearch("Alkalinity"); + for (i = 0; i < count_inverse; i++) + { + if (inverse[i].new_def != TRUE) + continue; +/* + * Set default uncertainties for all solutions, if necessary + */ + if (inverse[i].uncertainties.size() < inverse[i].count_solns) + { + size_t count = inverse[i].uncertainties.size(); + double value = (count > 0) ? inverse[i].uncertainties.back() : 0.05; + inverse[i].uncertainties.resize(inverse[i].count_solns); + for (size_t j = count; j < inverse[i].count_solns; j++) + { + inverse[i].uncertainties[j] = value; + } + } +/* + * Set default ph uncertainties for all solutions, if necessary + */ + if (inverse[i].ph_uncertainties.size() < inverse[i].count_solns) + { + size_t count = inverse[i].ph_uncertainties.size(); + double value = (count > 0) ? inverse[i].ph_uncertainties.back() : 0.05; + inverse[i].ph_uncertainties.resize(inverse[i].count_solns); + for (size_t j = count; j < inverse[i].count_solns; j++) + { + inverse[i].ph_uncertainties[j] = value; + } + } +/* + * Set default force for all solutions + */ + if (inverse[i].force_solns.size() < inverse[i].count_solns) + { + size_t count = inverse[i].force_solns.size(); + inverse[i].force_solns.resize(inverse[i].count_solns); + for (size_t j = count; j < inverse[i].count_solns; j++) + { + inverse[i].force_solns[j] = false; + } + } +/* + * Find master species for element, set uncertainties + */ + for (j = 0; j < inverse[i].elts.size(); j++) + { + inverse[i].elts[j].master = + master_bsearch_primary(inverse[i].elts[j].name); + if (inverse[i].elts[j].master == NULL) + { + input_error++; + error_string = sformatf( "No master species for element, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + size_t count_uncertainties = inverse[i].elts[j].uncertainties.size(); + inverse[i].elts[j].uncertainties.resize((size_t)inverse[i].count_solns); + if (count_uncertainties == 0) + { +/* use default uncertainties for element */ + for (k = 0; k < inverse[i].count_solns; k++) + { + inverse[i].elts[j].uncertainties[k] = + inverse[i].uncertainties[k]; + } + } + else if (count_uncertainties < inverse[i].count_solns) + { +/* use input uncertainties, fill in any missing at end */ + value = inverse[i].elts[j].uncertainties[count_uncertainties - 1]; + for (size_t k = count_uncertainties; k < inverse[i].count_solns; k++) + { + inverse[i].elts[j].uncertainties[k] = value; + } + } + } +/* + * Find phase + */ + count_elts = 0; + paren_count = 0; + for (j = 0; j < inverse[i].phases.size(); j++) + { + inverse[i].phases[j].phase = + phase_bsearch(inverse[i].phases[j].name, &k, FALSE); + if (inverse[i].phases[j].phase == NULL) + { + input_error++; + error_string = sformatf( "Could not find phase, %s.", + inverse[i].phases[j].name); + error_msg(error_string, CONTINUE); + continue; + } +/* + * Find isotope elements + */ + if (inverse[i].phases[j].isotopes.size() > 0) + { + for (k = 0; k < inverse[i].phases[j].isotopes.size(); k++) + { + inverse[i].phases[j].isotopes[k].primary = NULL; + inverse[i].phases[j].isotopes[k].master = NULL; + master_ptr = + master_bsearch(inverse[i].phases[j].isotopes[k]. + elt_name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Element not found for isotope calculation: %s.", + inverse[i].phases[j].isotopes[k].elt_name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary != TRUE) + { + input_error++; + error_string = sformatf( + "Isotope ratio may only be used" + " for total element in phase.\n" + "Secondary species not allowed: %s.", + master_ptr->elt->name); + error_msg(error_string, CONTINUE); + continue; + } + inverse[i].phases[j].isotopes[k].primary = master_ptr; + inverse[i].phases[j].isotopes[k].master = master_ptr; + /* find coefficient for element */ + for (elt_list_ptr = &inverse[i].phases[j].phase->next_elt[0]; + elt_list_ptr->elt != NULL; elt_list_ptr++) + { + if (elt_list_ptr->elt == master_ptr->elt) + { + inverse[i].phases[j].isotopes[k].coef = + elt_list_ptr->coef; + break; + } + } + if (elt_list_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Element, %s,for which isotope ratio was defined is not found in phase, %s", + master_ptr->elt->name, + inverse[i].phases[j].phase->name); + error_msg(error_string, CONTINUE); + continue; + } + } + qsort(&inverse[i].phases[j].isotopes[0], + inverse[i].phases[j].isotopes.size(), + sizeof(class isotope), isotope_compare); + } + add_elt_list(inverse[i].phases[j].phase->next_elt, 1.0); + + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Sort elements in reaction and combine + */ + elt_list_combine(); +/* + * Mark master species list + */ + for (j = 0; j < (int)master.size(); j++) + master[j]->in = FALSE; + for (j = 0; j < count_elts; j++) + { + elt_list[j].elt->master->in = TRUE; + } + /* Include all input elements */ + for (j = 0; j < inverse[i].elts.size(); j++) + { + inverse[i].elts[j].master->in = TRUE; + } + s_eminus->primary->in = TRUE; /* Include electrons */ + if (master_alk_ptr) + { + master_alk_ptr->in = TRUE; /* Include alkalinity */ + } + else + { + input_error++; + error_string = sformatf( + "Alkalinity must be defined in SOLUTION_MASTER_SPECIES to be able to use INVERSE_MODELING."); + error_msg(error_string, CONTINUE); + } +/* + * Unmark primary and mark secondary master species for redox elements + */ + count_in = 0; + inverse[i].count_redox_rxns = 0; + for (j = 0; j < (int)master.size(); j++) + { + /* skip all secondary master species in this loop */ + if (master[j]->primary == FALSE || master[j]->in == FALSE) + continue; + count_in++; + if (j + 1 == (int)master.size()) + continue; + /* if next master species is secondary, mark all + secondary master species until a primary is found */ + if (master[(size_t)j + 1]->primary == FALSE) + { + master[j]->in = FALSE; + count_in--; + for (k = j + 1; k < (int)master.size(); k++) + { + if (master[k]->primary == FALSE) + { + count_in++; + master[k]->in = TRUE; + if (master[k]->s->primary == NULL) + { + inverse[i].count_redox_rxns++; + } + } + else + { + break; + } + } + } + } +/* + * Save list of master species in inv_elts structure + */ + std::vector inv_elts; + inv_elts.resize(count_in); + count_in = 0; + for (j = 0; j < (int)master.size(); j++) + { + /* skip H(1) and O(-2) */ + if (master[j]->s == s_hplus || master[j]->s == s_h2o) + continue; + if (master[j]->in == TRUE) + { + /* set master */ + inv_elts[count_in].master = master[j]; + /* alloc uncertainties and set default */ + inv_elts[count_in].uncertainties.resize((size_t)inverse[i].count_solns); + for (k = 0; k < inverse[i].count_solns; k++) + { + inv_elts[count_in].uncertainties[k] = + inverse[i].uncertainties[k]; + } + count_in++; + } + } + if (s_co3->secondary->in == TRUE) + { + inverse[i].carbon = TRUE; + } + else + { + inverse[i].carbon = FALSE; + } +/* + * copy in input uncertainties + */ + /* copy primary redox to all secondary redox */ + for (j = 0; j < inverse[i].elts.size(); j++) + { + master_ptr = master_bsearch(inverse[i].elts[j].name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not found, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary == FALSE + || master_ptr->s->secondary == NULL) + continue; + for (k = 0; k < count_in; k++) + { + if (master_ptr == inv_elts[k].master->elt->primary) + { + for (l = 0; l < inverse[i].count_solns; l++) + { + inv_elts[k].uncertainties[l] = + inverse[i].elts[j].uncertainties[l]; + } + } + } + inverse[i].elts[j].uncertainties.clear(); + } + /* copy masters that are not primary redox */ + for (j = 0; j < inverse[i].elts.size(); j++) + { + master_ptr = master_bsearch(inverse[i].elts[j].name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not found, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary == TRUE + && master_ptr->s->secondary != NULL) + continue; + for (k = 0; k < count_in; k++) + { + if (master_ptr == inv_elts[k].master) + { + for (l = 0; l < inverse[i].count_solns; l++) + { + inv_elts[k].uncertainties[l] = + inverse[i].elts[j].uncertainties[l]; + } + break; + } + } + inverse[i].elts[j].uncertainties.clear(); + } +/* + * replace elts in inverse struct + */ + inverse[i].elts.clear(); + inverse[i].elts = inv_elts; + inverse[i].elts.resize(count_in); + for (j = 0; j < inverse[i].elts.size(); j++) + { +/* debug + output_msg(sformatf( "\t%d\t%s", j, inverse[i].elts[j].master->elt->name)); + for (k = 0; k < inverse[i].count_solns; k++) { + output_msg(sformatf( "\t%f", inverse[i].elts[j].uncertainties[k])); + } + output_msg(sformatf("\n")); + */ + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_phases(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + int replaced; + /* + * Fix log Ks first, so they can possibly be added to other phase equations + */ + for (i = 0; i < (int)phases.size(); i++) + { + select_log_k_expression(phases[i]->logk, phases[i]->rxn.logk); + add_other_logk(phases[i]->rxn.logk, phases[i]->add_logk); + phases[i]->rxn.token[0].name = phases[i]->name; + phases[i]->rxn.token[0].s = NULL; + } + /* + * Rewrite all phases to secondary species + */ + for (i = 0; i < (int)phases.size(); i++) + { + /* + * Rewrite equation + */ + count_trxn = 0; + trxn_add_phase(phases[i]->rxn, 1.0, false); + trxn.token[0].name = phases[i]->name; + /* debug + output_msg(sformatf( "%s PHASE.\n", phases[i]->name)); + trxn_print(); + */ + replaced = replace_solids_gases(); + phases[i]->replaced = replaced; + /* save rxn_s */ + trxn_reverse_k(); + rewrite_eqn_to_secondary(); + trxn_reverse_k(); + trxn_copy(phases[i]->rxn_s); + /* + * Check equation + */ + if (phases[i]->check_equation == TRUE) + { + if (replaced == FALSE) + { + phase_rxn_to_trxn(phases[i], phases[i]->rxn); + } + else + { + phase_rxn_to_trxn(phases[i], phases[i]->rxn_s); + } + if (check_eqn(FALSE) == ERROR) + { + input_error++; + error_string = sformatf( + "Equation for phase %s does not balance.", + phases[i]->name); + error_msg(error_string, CONTINUE); + } + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE coef; + const char* cptr; +/* + * Find pointers for pure phases + */ + //std::map::iterator it; + //it = Rxn_pp_assemblage_map.begin(); + //for ( ; it != Rxn_pp_assemblage_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_pp_assemblage.size(); nn++) + //{ + //std::map::iterator kit = Rxn_pp_assemblage_map.find(Rxn_new_pp_assemblage[nn]); + for (std::set::const_iterator nit = Rxn_new_pp_assemblage.begin(); nit != Rxn_new_pp_assemblage.end(); nit++) + { + std::map::iterator kit = Rxn_pp_assemblage_map.find(*nit); + if (kit == Rxn_pp_assemblage_map.end()) + { + assert(false); + } + //if (!kit->second.Get_new_def()) continue; + cxxPPassemblage *pp_assemblage_ptr = &(kit->second); + count_elts = 0; + paren_count = 0; + coef = 1.0; + pp_assemblage_ptr->Set_new_def(false); + + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int k; + class phase *phase_ptr = phase_bsearch(it->first.c_str(), &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( "Phase not found in database, %s.", + it->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + add_elt_list(phase_ptr->next_elt, coef); + } + if (it->second.Get_add_formula().size() > 0) + { + size_t first = count_elts; + phase_ptr = phase_bsearch(it->second.Get_add_formula().c_str(), &k, FALSE); + if (phase_ptr != NULL) + { + it->second.Set_add_formula(phase_ptr->formula); + } + { + cptr = it->second.Get_add_formula().c_str(); + get_elts_in_species(&cptr, coef); + } + /* check that all elements are in the database */ + for (size_t l = first; l < count_elts; l++) + { + if (elt_list[l].elt->master == NULL) + { + input_error++; + error_string = sformatf( + "Element \"%s\" in alternative phase for \"%s\" in EQUILIBRIUM_PHASES not found in database.", + elt_list[l].elt->name, + it->first.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + +/* + * Store list with all elements in phases and add formulae + */ + cxxNameDouble nd = elt_list_NameDouble(); + pp_assemblage_ptr->Set_eltList(nd); +/* + * Duplicate pure phases if necessary + */ + + int n_user = pp_assemblage_ptr->Get_n_user(); + int n_user_end = pp_assemblage_ptr->Get_n_user_end(); + pp_assemblage_ptr->Set_n_user_end(n_user); + Utilities::Rxn_copies(Rxn_pp_assemblage_map, n_user, n_user_end); + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + class phase *phase_ptr; + LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1; + LDBLE xb2, xb3, xb4, xc2, xc3; + LDBLE moles; +/* + * Find pointers for pure phases + */ + //std::map::iterator it; + //for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_ss_assemblage.size(); nn++) + //{ + //std::map::iterator it = Rxn_ss_assemblage_map.find(Rxn_new_ss_assemblage[nn]); + for (std::set::const_iterator nit = Rxn_new_ss_assemblage.begin(); nit != Rxn_new_ss_assemblage.end(); nit++) + { + std::map::iterator it = Rxn_ss_assemblage_map.find(*nit); + if (it == Rxn_ss_assemblage_map.end()) + { + assert(false); + } + //if (!it->second.Get_new_def()) continue; + count_elts = 0; + paren_count = 0; + cxxSSassemblage *ss_assemblage_ptr = &(it->second); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int k1; + phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &k1, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Phase not found in database, %s, assemblage %d.", + comp_ptr->Get_name().c_str(), + ss_assemblage_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } +#ifdef NPP + if (isnan(comp_ptr->Get_moles())) +#else + if (comp_ptr->Get_moles() == NAN) +#endif + { + input_error++; + error_string = sformatf( + "Moles of solid solution component not defined, %s, assemblage %d.", + comp_ptr->Get_name().c_str(), + ss_assemblage_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + } + + if (ss_assemblage_ptr->Get_new_def()) + { + /* + * Calculate a0 and a1 first + */ + ss_calc_a0_a1(ss_ptr); + + n_tot = 0; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + { + moles = MIN_TOTAL_SS; + comp_ptr->Set_initial_moles(moles); + } + n_tot += moles; + } + + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + { + moles = MIN_TOTAL_SS; + } + comp_ptr->Set_fraction_x(moles / n_tot); + comp_ptr->Set_log10_fraction_x(log10(moles / n_tot)); + } + l_a0 = ss_ptr->Get_a0(); + l_a1 = ss_ptr->Get_a1(); + +/* + * Binary solid solution + */ + if (l_a0 != 0.0 || l_a1 != 0) + { + ss_ptr->Set_dn(1.0 / n_tot); + nc = ss_ptr->Get_ss_comps()[0].Get_moles(); + if (nc == 0) + nc = MIN_TOTAL_SS; + nb = ss_ptr->Get_ss_comps()[1].Get_moles(); + if (nb == 0) + nb = MIN_TOTAL_SS; + xc = nc / n_tot; + xb = nb / n_tot; + + /* lambdas */ + ss_ptr->Get_ss_comps()[0].Set_log10_lambda(xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10); + ss_ptr->Get_ss_comps()[1].Set_log10_lambda(xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10); + + /* derivatives wrt nc and nb */ + xc2 = xc * xc; + xc3 = xc2 * xc; + xb2 = xb * xb; + xb3 = xb2 * xb; + xb4 = xb3 * xb; + + /* component 1 */ + dnb = + -2 * l_a0 * xb * xc2 - 8 * l_a1 * xb2 * xc2 + + 6 * l_a1 * xb * xc2 - 4 * l_a1 * xc * xb4 - + 8 * l_a1 * xb3 * xc2 - 4 * l_a1 * xb2 * xc3 - + 2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + + 6 * l_a1 * xc * xb2 + 1; + ss_ptr->Get_ss_comps()[0].Set_dnb(dnb / n_tot); + dnc = + 2 * l_a0 * xb3 + 2 * l_a0 * xc * xb2 + 8 * l_a1 * xb4 + + 8 * l_a1 * xc * xb3 - 2 * l_a1 * xb3 - 6 * l_a1 * xc * xb2; + ss_ptr->Get_ss_comps()[0].Set_dnc(-xb / nc + dnc / n_tot); + ss_ptr->Get_ss_comps()[0].Set_dn(1.0 / n_tot); + + /* component 2 */ + dnb = + 2 * l_a0 * xb * xc2 + 2 * l_a0 * xc3 + + 8 * l_a1 * xb2 * xc2 + 8 * l_a1 * xb * xc3 - + 2 * l_a1 * xb * xc2 - 6 * l_a1 * xc3; + ss_ptr->Get_ss_comps()[1].Set_dnb(-xc / nb + dnb / n_tot); + dnc = + -2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + + 2 * l_a1 * xc * xb2 - 2 * l_a0 * xb * xc2 - + 8 * l_a1 * xb2 * xc2 + 6 * l_a1 * xb * xc2 + 1; + ss_ptr->Get_ss_comps()[1].Set_dnc(dnc / n_tot); + ss_prep(ss_ptr->Get_tk(), ss_ptr, TRUE); + ss_ptr->Get_ss_comps()[1].Set_dn(1.0 / n_tot); +/* + * Ideal solid solution + */ + } + else + { + ss_ptr->Set_dn(1.0 / n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + comp_ptr->Set_log10_lambda(0); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + moles = MIN_TOTAL_SS; + comp_ptr->Set_dnb((n_tot - moles) / (moles * n_tot)); + comp_ptr->Set_dn(1.0 / n_tot); + } + } + } + } + ss_assemblage_ptr->Set_new_def(false); +/* + * Duplicate ss_assemblage if necessary + */ + int n_user = ss_assemblage_ptr->Get_n_user(); + int n_user_end = ss_assemblage_ptr->Get_n_user_end(); + Utilities::Rxn_copies(Rxn_ss_assemblage_map, n_user, n_user_end); + ss_assemblage_ptr->Set_n_user_end(n_user); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_punch(void) +/* ---------------------------------------------------------------------- */ +{ + //int i, j, l; + int punch_save; + //char token[MAX_LENGTH]; +/* + * tidy punch information + */ + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (current_selected_output == NULL) + continue; + + + /* totals */ + + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_totals()[i]; + std::string noplus = pair_ptr.first; + replace(noplus, "+", ""); + pair_ptr.second = master_bsearch(noplus.c_str()); + } + + /* molalities */ + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_molalities()[i]; + pair_ptr.second = s_search(pair_ptr.first.c_str()); + } + + /* log activities */ + + //for (i = 0; i < punch.count_activities; i++) + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_activities()[i]; + pair_ptr.second = s_search(pair_ptr.first.c_str()); + } + + /* equilibrium phases */ + + //for (i = 0; i < punch.count_pure_phases; i++) + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_pure_phases()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + + /* saturation indices */ + + //for (i = 0; i < punch.count_si; i++) + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_si()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + + /* gases */ + + //for (i = 0; i < punch.count_gases; i++) + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_gases()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + } + /* + * Always write new headings when SELECTED_OUTPUT is read + */ + so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (current_selected_output == NULL || + !current_selected_output->Get_new_def()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + + int l; + if (current_selected_output->Get_high_precision() == false) + { + l = 12; + } + else + { + l = 20; + } + // UserPunch + std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user()); + current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second); + + punch_save = pr.punch; + pr.punch = TRUE; + phrq_io->Set_punch_on(true); + + /* constant stuff, sim, pH, etc. */ + + if (current_selected_output->Get_sim() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "sim")); + } + if (current_selected_output->Get_state() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "state")); + } + if (current_selected_output->Get_soln() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "soln")); + } + if (current_selected_output->Get_dist() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "dist_x")); + } + if (current_selected_output->Get_time() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "time")); + } + if (current_selected_output->Get_step() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "step")); + } + if (current_selected_output->Get_ph() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pH")); + } + if (current_selected_output->Get_pe() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pe")); + } + if (current_selected_output->Get_rxn() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "reaction")); + } + if (current_selected_output->Get_temp() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "temp")); + } + if (current_selected_output->Get_alk() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "Alk")); + } + if (current_selected_output->Get_mu() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mu")); + } + if (current_selected_output->Get_water() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mass_H2O")); + } + if (current_selected_output->Get_charge_balance() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "charge")); + } + if (current_selected_output->Get_percent_error() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pct_err")); + } + /* totals */ + + //for (i = 0; i < punch.count_totals; i++) + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_totals()[i]; + fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find master species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //fpunchf_heading(sformatf("%*s\t", l, punch.totals[i].name)); + //if (punch.totals[i].master == NULL) + //{ + // error_string = sformatf( "Did not find master species," + // " %s.", punch.totals[i].name); + // warning_msg(error_string); + //} + } + + /* molalities */ + + //for (i = 0; i < punch.count_molalities; i++) + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_molalities()[i]; + std::string name = "m_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "m_"); + //strcat(token, punch.molalities[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.molalities[i].s == NULL) + //{ + // error_string = sformatf( "Did not find species," + // " %s.", punch.molalities[i].name); + // warning_msg(error_string); + //} + } + + /* log activities */ + + //for (i = 0; i < punch.count_activities; i++) + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_activities()[i]; + std::string name = "la_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "la_"); + //strcat(token, punch.activities[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.activities[i].s == NULL) + //{ + // error_string = sformatf( "Did not find species, " + // "%s.", punch.activities[i].name); + // warning_msg(error_string); + //} + } + + /* equilibrium phases */ + + //for (i = 0; i < punch.count_pure_phases; i++) + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_pure_phases()[i]; + fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); + std::string name = "d_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "d_"); + //strcat(token, punch.pure_phases[i].name); + //fpunchf_heading(sformatf("%*s\t", l, punch.pure_phases[i].name)); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.pure_phases[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.pure_phases[i].name); + // warning_msg(error_string); + //} + } + + /* saturation indices */ + + //for (i = 0; i < punch.count_si; i++) + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_si()[i]; + std::string name = "si_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "si_"); + //strcat(token, punch.si[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.si[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.si[i].name); + // warning_msg(error_string); + //} + } + + /* gases */ + + //if (punch.count_gases > 0) + if (current_selected_output->Get_gases().size() > 0) + { + fpunchf_heading(sformatf("%*s\t", l, "pressure")); + fpunchf_heading(sformatf("%*s\t", l, "total mol")); + fpunchf_heading(sformatf("%*s\t", l, "volume")); + } + //for (i = 0; i < punch.count_gases; i++) + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_gases()[i]; + std::string name = "g_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "g_"); + //strcat(token, punch.gases[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.gases[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.gases[i].name); + // warning_msg(error_string); + //} + } + + /* kinetics */ + + //for (i = 0; i < punch.count_kinetics; i++) + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_kinetics()[i]; + std::string name = "k_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + name = "dk_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //strcpy(token, "k_"); + //strcat(token, punch.kinetics[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //strcpy(token, "dk_"); + //strcat(token, punch.kinetics[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* solid solutions */ + + //for (i = 0; i < punch.count_s_s; i++) + for (size_t i = 0; i < current_selected_output->Get_s_s().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_s_s()[i]; + std::string name = "s_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //strcpy(token, "s_"); + //strcat(token, punch.s_s[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* isotopes */ + + //for (i = 0; i < punch.count_isotopes; i++) + for (size_t i = 0; i < current_selected_output->Get_isotopes().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_isotopes()[i]; + if (isotope_ratio_search(pair_ref.first.c_str()) == NULL) + { + error_string = sformatf( + "Did not find isotope_ratio definition for " + "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", + pair_ref.first.c_str(), pair_ref.first.c_str()); + warning_msg(error_string); + } + std::string name = "I_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //if (isotope_ratio_search(punch.isotopes[i].name) == NULL) + //{ + // error_string = sformatf( + // "Did not find isotope_ratio definition for " + // "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", + // punch.isotopes[i].name, punch.isotopes[i].name); + // warning_msg(error_string); + //} + //strcpy(token, "I_"); + //strcat(token, punch.isotopes[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* calculate_values */ + + for (size_t i = 0; i < current_selected_output->Get_calculate_values().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_calculate_values()[i]; + if (calculate_value_search(pair_ref.first.c_str()) == NULL) + { + error_string = sformatf( + "Did not find calculate_values definition for " + "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", + pair_ref.first.c_str(), + pair_ref.first.c_str()); + warning_msg(error_string); + } + std::string name = "V_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //if (calculate_value_search(punch.calculate_values[i].name) == NULL) + //{ + // error_string = sformatf( + // "Did not find calculate_values definition for " + // "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", + // punch.calculate_values[i].name, + // punch.calculate_values[i].name); + // warning_msg(error_string); + //} + //strcpy(token, "V_"); + //strcat(token, punch.calculate_values[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* user_punch */ + if (current_user_punch != NULL && current_selected_output->Get_user_punch()) + { + for (size_t i = 0; i < current_user_punch->Get_headings().size(); i++) + { + fpunchf_heading(sformatf("%*s\t", l, current_user_punch->Get_headings()[i].c_str())); + } + } + fpunchf_heading("\n"); + + current_selected_output->Set_new_def(false); + pr.punch = punch_save; + phrq_io->Set_punch_on(pr.punch == TRUE); + + punch_flush(); + } + + current_selected_output = NULL; + current_user_punch = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_species(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + class master *master_ptr; + char c; + const char* cptr; +/* + * Make sure species pointers are ok + */ + if (check_species_input() == ERROR) + { + error_msg("Calculations terminating due to input errors.", STOP); + } +/* + * Set secondary and primary pointers in species structures + */ + for (i = 0; i < (int)s.size(); i++) + { + s[i]->number = i; + s[i]->primary = NULL; + s[i]->secondary = NULL; + if (s[i]->check_equation == TRUE) + { + species_rxn_to_trxn(s[i]); + if (check_eqn(TRUE) == ERROR) + { + input_error++; + error_string = sformatf( + "Equation for species %s does not balance.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + } + } + for (i = 0; i < (int)master.size(); i++) + { + cptr = master[i]->elt->name; + if (cptr[0] != '[') + { + while ((c = (int) *(++cptr)) != '\0') + { + if (isupper((int) c)) + { + input_error++; + error_string = sformatf( + "Element or valence name in SOLUTION_MASTER_SPECIES should include only one element, %s.", + master[i]->elt->name); + error_msg(error_string, CONTINUE); + break; + } + } + } + /* store sequence number in master structure */ + master[i]->number = i; + if (strcmp(master[i]->elt->name, "Alkalinity") != 0) + { + if (master[i]->primary == TRUE) + { + master[i]->s->primary = master[i]; + } + else + { + master[i]->s->secondary = master[i]; + } + } + if (strcmp(master[i]->elt->name, "C") == 0) + { + s_co3 = master[i]->s; + } + if (master[i]->gfw_formula != NULL) + { + if (compute_gfw(master[i]->gfw_formula, &master[i]->gfw) == ERROR) + { + input_error++; + error_string = sformatf( + "Calculating gfw for master species, %s, formula %s.", + master[i]->elt->name, master[i]->gfw_formula); + error_msg(error_string, CONTINUE); + } + } + } +/* + * Write equations for all master species in terms of primary + * master species, set coefficient of element in master species + */ + for (i = 0; i < (int)master.size(); i++) + { + count_trxn = 0; + if (master[i]->s->primary != NULL) + { + trxn_add(master[i]->s->rxn, 1.0, false); + trxn_add(master[i]->s->rxn, -1.0, true); + } + else + { + trxn_add(master[i]->s->rxn, 1.0, false); + rewrite_eqn_to_primary(); + } + trxn_copy(master[i]->rxn_primary); + master[i]->coef = coef_in_master(master[i]); + } +/* + * Rewrite all species to secondary species + */ + for (i = 0; i < (int)s.size(); i++) + { + count_trxn = 0; + if (s[i]->primary != NULL || s[i]->secondary != NULL) + { + trxn_add(s[i]->rxn, 1.0, false); + trxn_add(s[i]->rxn, -1.0, true); + } + else + { + trxn_add(s[i]->rxn, 1.0, false); + rewrite_eqn_to_secondary(); + } + //rxn_free(s[i].rxn_s); + //s[i].rxn_s = rxn_alloc(count_trxn + 1); + trxn_copy(s[i]->rxn_s); + /* calculate alkalinity */ + s[i]->alk = calc_alk(s[i]->rxn_s); + /* set co2 coefficient */ + s[i]->co2 = 0.0; + for (j = 1; j < count_trxn; j++) + { + if (trxn.token[j].s == s_co3) + { + s[i]->co2 = trxn.token[j].coef; + break; + } + } + } +/* + * Set pointer in element to master species + */ + for (i = 0; i < (int)elements.size(); i++) + { + elements[i]->master = master_bsearch(elements[i]->name); + if (elements[i]->master == NULL) + { + input_error++; + error_string = sformatf( "No master species for element %s.", + elements[i]->name); + error_msg(error_string, CONTINUE); + } + elements[i]->primary = master_bsearch_primary(elements[i]->name); + if (elements[i]->primary == NULL) + { + input_error++; + error_string = sformatf( "No master species for element %s.", + elements[i]->name); + error_msg(error_string, CONTINUE); + } + } +/* + * Make sure all primary master species for redox elements + * are also secondary master species + */ + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->primary == FALSE) + { + master_ptr = master[i]->s->secondary->elt->primary; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Every primary master species for a redox element\n" + "\tmust also be a secondary master species.\n" + "\tError in definitions related to %s .\n", + master[i]->s->name); + error_msg(error_string, CONTINUE); + + } + else if (master_ptr->s->secondary == NULL) + { + input_error++; + error_string = sformatf( + "Every primary master species for a redox element\n" + "\tmust also be a secondary master species.\n" + "\t%s is the primary master species for element %s.\n" + "\tAnother entry in SOLUTION_MASTER_SPECIES is needed.\n" + "\tDefine species %s as a secondary master species for a valence state.\n" + "\tFor example: \n" "\t%s(0)\t%s alk gfw", + master_ptr->s->name, master_ptr->elt->name, + master_ptr->s->name, master_ptr->elt->name, + master_ptr->s->name); + error_msg(error_string, CONTINUE); + } + } + } +/* + * Calculate H and O if alternate mass balance is given + */ + for (i = 0; i < (int)s.size(); i++) + { + if (s[i]->next_secondary.size() != 0) + { + s[i]->h = 0.0; + s[i]->o = 0.0; + for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) + { + if (s[i]->next_secondary[j].elt->primary == NULL) + continue; + if (s[i]->next_secondary[j].elt->primary->s == s_hplus || s[i]->next_secondary[j].elt->primary->s == s_h3oplus) + { + s[i]->h += s[i]->next_secondary[j].coef; + } + else if (s[i]->next_secondary[j].elt->primary->s == s_h2o) + { + s[i]->o += s[i]->next_secondary[j].coef; + } + else if (s[i]->mole_balance != NULL) + { + master_ptr = s[i]->next_secondary[j].elt->master; + if (master_ptr != NULL) + { + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } + } + else + { + input_error++; + error_string = sformatf( + "Element in -mole_balance %s not defined for species %s.\n", s[i]->mole_balance, s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->coef != 1) + { + s[i]->next_secondary[j].coef /= master_ptr->coef; + } + } + } + if (s[i]->type == EX) + { + for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) + { + if (s[i]->next_secondary[j].elt->primary->s->type == EX) + { + s[i]->equiv = s[i]->next_secondary[j].coef; + break; + } + } + } + } + if (s[i]->type == EX) + { +/* + * Find valence of cation from coefficients of reaction components + * Changed to be coefficient of exchanger + */ + LDBLE exchange_coef = 0.0; + for (j = 1; s[i]->rxn_s.token[j].s != NULL; j++) + { + if (s[i]->rxn_s.token[j].s->type == EX) + { + exchange_coef = s[i]->rxn_s.token[j].coef; + break; + } + } + if (exchange_coef == 0.0) + { + input_error++; + error_string = sformatf( + "No exchange species found in equation for %s.\n", s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + s[i]->equiv = exchange_coef; + } + if (s[i]->type == SURF) + { + LDBLE surface_coef = 0.0; + /* + * Find coefficient of surface in rxn, store in equiv + */ + for (j = 1; s[i]->rxn_s.token[j].s != NULL; j++) + { + if (s[i]->rxn_s.token[j].s->type == SURF) + { + surface_coef = s[i]->rxn_s.token[j].coef; + break; + } + } + if (surface_coef == 0.0) + { + input_error++; + error_string = sformatf( + "No surface species found in equation for %s.\n", s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + s[i]->equiv = surface_coef; + } + } + + for (i = 0; i < (int)master.size(); i++) + { + if (master[i]->gfw <= 0.0) + { + if (master[i]->type >= EMINUS) continue; + if ((strcmp(master[i]->elt->name, "E") != 0) && + (strcmp(master[i]->elt->name, "e") != 0) && + (strcmp(master[i]->elt->name, "H(1)") != 0) && + (strcmp(master[i]->elt->name, "O(-2)") != 0) + ) + { + input_error++; + error_string = sformatf( + "Gram formula wt in SOLUTION_MASTER_SPECIES should not be <= 0.0, %s.\n", master[i]->elt->name); + error_msg(error_string, CONTINUE); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * After all of data are read, fill in master species for surface comps + * Sort surface + */ + const char* cptr1; + cxxSurface *surface_ptr; + //std::map::iterator kit; + //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + //std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator kit = Rxn_surface_map.find(*nit); + if (kit == Rxn_surface_map.end()) + { + assert(false); + } + //if (!kit->second.Get_new_def()) continue; + surface_ptr = &(kit->second); + if (surface_ptr->Get_tidied()) continue; + surface_ptr->Set_tidied(true); + // ccm incompatible with Donnan or diffuse_layer + if (surface_ptr->Get_type() == cxxSurface::CCM) + { + if (surface_ptr->Get_dl_type() == cxxSurface::BORKOVEK_DL || surface_ptr->Get_dl_type() == cxxSurface::DONNAN_DL) + { + input_error++; + error_string = "Cannot use -diffuse_layer or -donnan calculation with Constant Capacity Model."; + error_msg(error_string, CONTINUE); + continue; + } + } + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); +/* + * Find master species for each surface + */ + cxxNameDouble::iterator jit = comp_ptr->Get_totals().begin(); + for ( ; jit != comp_ptr->Get_totals().end(); jit++ ) + { + class element *elt_ptr = element_store(jit->first.c_str()); + class master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Master species not in database for %s, " + "skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + comp_ptr->Set_master_element(elt_ptr->name); +/* + * Set flags + */ + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + /* + * Calculate moles of sites + */ + if (surface_ptr->Get_new_def() + && surface_ptr->Get_sites_units() == cxxSurface::SITES_DENSITY + && comp_ptr->Get_phase_name().size() == 0) + { + if (charge_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Surface type is incompatible with site units for %s.", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + comp_ptr->Set_moles( + comp_ptr->Get_moles() * 1.0e18 * + charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() / AVOGADRO); + /* + * Calculate totals + */ + count_elts = 0; + paren_count = 0; + { + cptr1 = comp_ptr->Get_formula().c_str(); + get_elts_in_species(&cptr1, comp_ptr->Get_moles()); + } + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + } + if (surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + { + charge_ptr->Set_charge_balance(charge_ptr->Get_charge_balance() + + comp_ptr->Get_moles() * + comp_ptr->Get_formula_z()); + } + break; + } + } + /* + * Check that all surface comps have a corresponding master + */ + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + if (surface_ptr->Get_surface_comps()[i].Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "No surface master species for surface component %s, ", + surface_ptr->Get_surface_comps()[i].Get_formula().c_str()); + error_msg(error_string, CONTINUE); + } + } +/* + * Sort components + */ + std::map comp_map; + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + comp_map[comp_ptr->Get_formula()] = *comp_ptr; + } + std::map::iterator it = comp_map.begin(); + surface_ptr->Get_surface_comps().clear(); + for ( ; it != comp_map.end(); it++) + { + surface_ptr->Get_surface_comps().push_back(it->second); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_solutions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Define n_user for any solutions read by solution_spread that + * don`t have n_user defined + */ + class master *master_ptr; + + /* + * Calculate solution numbers + */ + if (unnumbered_solutions.size() > 0) + { + int last = 0; + std::map::iterator jit; + for (jit = Rxn_solution_map.begin(); jit != Rxn_solution_map.end(); jit++) + { + if (jit->second.Get_n_user() > last) + last = jit->second.Get_n_user(); + if (jit->second.Get_n_user_end() > last) + last = jit->second.Get_n_user_end(); + } + if (save.solution == TRUE) + { + if (save.n_solution_user > last) + last = save.n_solution_user; + if (save.n_solution_user_end > last) + last = save.n_solution_user_end; + } + // put unnumbered solutions in map + for (size_t i = 0; i < unnumbered_solutions.size(); i++) + { + if (use.Get_n_solution_user() < 0) + { + use.Set_n_solution_user(last + 1); + } + unnumbered_solutions[i].Set_n_user_both(++last); + Rxn_solution_map[last] = unnumbered_solutions[i]; + Rxn_new_solution.insert(last); + } + unnumbered_solutions.clear(); + } + /* + * Check that elements are in database + */ + //std::map::iterator it; + //for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + //for(size_t n = 0; n < Rxn_new_solution.size(); n++) + //{ + //std::map::iterator it = Rxn_solution_map.find(Rxn_new_solution[n]); + for (std::set::const_iterator nit = Rxn_new_solution.begin(); nit != Rxn_new_solution.end(); nit++) + { + std::map::iterator it = Rxn_solution_map.find(*nit); + if (it == Rxn_solution_map.end()) + { + assert(false); + continue; + } + cxxSolution &solution_ref = it->second; + //if (solution_ref.Get_new_def()) + { + cxxISolution *initial_data_ptr = solution_ref.Get_initial_data(); + if (initial_data_ptr != NULL) + { + std::map::iterator iit = initial_data_ptr->Get_comps().begin(); + for ( ; iit != initial_data_ptr->Get_comps().end(); iit++) + { + cxxISolutionComp &comp_ref = iit->second; + if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 || + strcmp(comp_ref.Get_description().c_str(), "E") == 0) + { + comp_ref.Set_moles(0.0); + continue; + } + std::string token; + std::string description = comp_ref.Get_description(); + std::string::iterator b = description.begin(); + std::string::iterator e = description.end(); + CParser::copy_token(token, b, e); + + master_ptr = master_bsearch(token.c_str()); + if (master_ptr == NULL) + { + error_string = sformatf( + "Could not find element in database, %s.\n\tConcentration is set to zero.", + comp_ref.Get_description().c_str()); + warning_msg(error_string); + comp_ref.Set_input_conc(0.0); + continue; + } + } + } + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_rxn_to_trxn(class species *s_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copy reaction from reaction structure to + * temp reaction structure. + */ + if (trxn.token.size() <= s_ptr->rxn.token.size()) + { + trxn.token.resize(s_ptr->rxn.token.size()); + } + count_trxn = 0; + for (size_t i = 0; s_ptr->rxn.token[i].s != NULL; i++) + { + trxn.token[i].name = s_ptr->rxn.token[i].s->name; + trxn.token[i].z = s_ptr->rxn.token[i].s->z; + trxn.token[i].s = s_ptr->rxn.token[i].s; + trxn.token[i].unknown = NULL; + trxn.token[i].coef = s_ptr->rxn.token[i].coef; + count_trxn = i + 1; + if (count_trxn + 1 > trxn.token.size()) + trxn.token.resize(count_trxn + 1); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Isotope ratios for each element or element valence state + */ + LDBLE isotope_number; + class master *master_ptr, *primary_ptr; + + size_t primary_number = 0; + primary_ptr = NULL; + std::map::iterator it; + for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + { + std::map new_isotopes; + cxxSolution &solution_ref = it->second; + if (!solution_ref.Get_new_def()) + continue; + if (solution_ref.Get_isotopes().size() == 0) + continue; + + std::map primary_isotopes; +/* + * Make list of primary master species for isotopes + */ + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ref.Get_isotopes().begin(); + for ( ; kit != solution_ref.Get_isotopes().end(); kit++) + { + cxxSolutionIsotope &isotope_ref = kit->second; + master_ptr = master_bsearch_primary(isotope_ref.Get_elt_name().c_str()); + isotope_number = isotope_ref.Get_isotope_number(); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + isotope_ref.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::ostringstream iso_name_str; + iso_name_str << (int) isotope_number << master_ptr->elt->name; + + std::map < std::string, cxxSolutionIsotope >::iterator jit; + jit = primary_isotopes.find(iso_name_str.str().c_str()); + if (jit == primary_isotopes.end()) + { + cxxSolutionIsotope temp_isotope; + temp_isotope.Set_isotope_name(iso_name_str.str().c_str()); + temp_isotope.Set_elt_name(master_ptr->elt->name); + temp_isotope.Set_isotope_number(isotope_number); + primary_isotopes[iso_name_str.str().c_str()] = temp_isotope; + } + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Go through all redox states of the list of primary species and isotope number + */ + for (kit = primary_isotopes.begin(); kit != primary_isotopes.end(); kit++) + { + /* find index number of master species, set flag to FALSE */ + master_ptr = master_bsearch(kit->second.Get_elt_name().c_str()); + isotope_number = kit->second.Get_isotope_number(); + for (int k = 0; k < (int)master.size(); k++) + { + master[k]->isotope = FALSE; + } + primary_number = master_ptr->number; + primary_ptr = master_ptr; + + /* go through isotopes of solution and fill in master species */ + std::map < std::string, cxxSolutionIsotope >::iterator lit = solution_ref.Get_isotopes().begin(); + for ( ; lit != solution_ref.Get_isotopes().end(); lit++) + { + master_ptr = master_bsearch(lit->second.Get_elt_name().c_str()); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* only fill for pertinent isotope */ + if (master_ptr->elt->primary != primary_ptr) + continue; + if (lit->second.Get_isotope_number() != isotope_number) + continue; + + /* for primary, fill in ratio for all secondary species */ + if (master_ptr->primary == TRUE && master_ptr->s->secondary != NULL) + { + for (size_t k = primary_number + 1; k < (int)master.size(); k++) + { + if (master[k]->elt->primary != primary_ptr) + break; + master[k]->isotope_ratio = lit->second.Get_ratio(); + master[k]->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); + if (master[k]->isotope == TRUE) + { + error_string = sformatf( + "In isotope calculation: redefinition of isotope ratio for %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + } + master[k]->isotope = TRUE; + } + } + /* for secondary and non redox, set ratio */ + else + { + master_ptr->isotope_ratio = lit->second.Get_ratio(); + master_ptr->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); + if (master_ptr->isotope == TRUE) + { + error_string = sformatf( + "In isotope calculation: redefinition of isotope ratio for %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + } + master_ptr->isotope = TRUE; + } + } +/* + * Write new isotope structure + */ + for (int k = 0; k < (int)master.size(); k++) + { + /* skip primary master species of redox elements */ + if (master[k]->primary == TRUE && master[k]->s->secondary != NULL) + continue; + if (master[k]->elt->primary == primary_ptr && master[k]->isotope == FALSE) + { + input_error++; + error_string = sformatf( + "Isotopic ratio not defined for element or valence state %g%s, using 0.", + (double) isotope_number, master[k]->elt->name); + warning_msg(error_string); + master[k]->isotope = TRUE; + master[k]->isotope_ratio = 0.0; + master[k]->isotope_ratio_uncertainty = 0.001; + } + if (master[k]->isotope == FALSE) + continue; + cxxSolutionIsotope temp_iso; + temp_iso.Set_isotope_number(isotope_number); + temp_iso.Set_elt_name(master[k]->elt->name); + temp_iso.Set_total(0); + temp_iso.Set_ratio(master[k]->isotope_ratio); + temp_iso.Set_ratio_uncertainty(master[k]->isotope_ratio_uncertainty); +#ifdef NPP + if (!isnan(master[k]->isotope_ratio_uncertainty)) +#else + if (master[k]->isotope_ratio_uncertainty != NAN) +#endif + { + temp_iso.Set_ratio_uncertainty_defined(true); + } + std::string token = sformatf("%d%s", (int) isotope_number, + master[k]->elt->name); + temp_iso.Set_isotope_name(token.c_str()); + new_isotopes[token] = temp_iso; + } + } + solution_ref.Set_isotopes(new_isotopes); + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_kin_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + cxxKinetics *kinetics_ptr; + const char* cptr; + LDBLE conc; + + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + cxxExchange * exchange_ptr = &(it->second); + if (!exchange_ptr->Get_new_def()) + continue; + if (exchange_ptr->Get_n_user() < 0) + continue; + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_rate_name().size() == 0) + continue; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + class element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + found_exchange = true;; + } + if (!found_exchange) + { + input_error++; + error_string = sformatf( + "Exchange formula does not contain an exchange master species, %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find associated kinetic reaction ... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, exchange_ptr->Get_n_user())) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use exchange related to kinetic reaction, %s", + exchange_ptr->Get_n_user(), comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + size_t k; + for (k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) + { + if (strcmp_nocase + (comp_ref.Get_rate_name().c_str(), + kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to exchanger, %s, not found in KINETICS %d", + comp_ref.Get_rate_name().c_str(), comp_ref.Get_formula().c_str(), exchange_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + + /* use database name for phase */ + comp_ref.Set_rate_name(kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()); + + /* make exchanger concentration proportional to mineral ... */ + conc = kinetics_ptr->Get_kinetics_comps()[k].Get_m() * comp_ref.Get_phase_proportion(); + + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, conc); + } + comp_ref.Set_totals(elt_list_NameDouble()); +/* + * No check on availability of exchange elements + */ + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +update_kin_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion. Exchange needs to be updated if the + * amount of kinetic reaction has changed. Corner case of + * zero moles. + */ +{ + cxxKinetics* kinetics_ptr; + const char* cptr; + LDBLE conc; + + std::map::iterator it = Rxn_exchange_map.begin(); + for ( ; it != Rxn_exchange_map.end(); it++) + { + cxxExchange* exchange_ptr = &(it->second); + if (exchange_ptr->Get_n_user() < 0) continue; + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp& comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_rate_name().size() == 0) continue; + double comp_moles = 0.0; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + class element* elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf("Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + { + comp_moles = kit->second; + found_exchange = true; + } + } + //if (!found_exchange) + //{ + // input_error++; + // error_string = sformatf( + // "Exchange formula does not contain an exchange master species, %s", + // comp_ref.Get_formula().c_str()); + // error_msg(error_string, CONTINUE); + // continue; + //} + + /* Now find associated kinetic reaction ... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, exchange_ptr->Get_n_user())) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use exchange related to kinetic reaction, %s", + exchange_ptr->Get_n_user(), comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + size_t k; + for (k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) + { + if (strcmp_nocase + (comp_ref.Get_rate_name().c_str(), + kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to exchanger, %s, not found in KINETICS %d", + comp_ref.Get_rate_name().c_str(), comp_ref.Get_formula().c_str(), exchange_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + + /* use database name for phase */ + comp_ref.Set_rate_name(kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()); + + /* make exchanger concentration proportional to mineral ... */ + conc = kinetics_ptr->Get_kinetics_comps()[k].Get_m() * comp_ref.Get_phase_proportion(); + if (found_exchange && comp_moles > 0.0) + { + /* parse formula */ + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, 1.0); + } + cxxNameDouble nd_formula = elt_list_NameDouble(); + double comp_coef = 0; + for (kit = nd_formula.begin(); kit != nd_formula.end(); kit++) + { + /* Find master species */ + class element* elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr->master->type == EX) + { + comp_coef = kit->second; + } + } + comp_ref.multiply(comp_coef * conc / comp_moles); + } + else /* need to generate totals from scratch */ + { + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, conc); + } + comp_ref.Set_totals(elt_list_NameDouble()); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_min_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + int n, jj; + const char* cptr; + LDBLE conc; + + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + cxxExchange * exchange_ptr = &(it->second); + if (!exchange_ptr->Get_new_def()) + continue; + if (exchange_ptr->Get_n_user() < 0) + continue; + n = exchange_ptr->Get_n_user(); + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_phase_name().size() == 0) + continue; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + class element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + { + found_exchange = true;; + } + } + if (!found_exchange) + { + input_error++; + error_string = sformatf( + "Exchange formula does not contain an exchange master species, %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + /* Now find the mineral on which exchanger depends... */ + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use exchange related to mineral phase, %s", + n, comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(comp_ref.Get_phase_name().c_str(), jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end() ) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + comp_ref.Set_phase_name(jit->first.c_str()); + /* make exchanger concentration proportional to mineral ... */ + conc = jit->second.Get_moles() * comp_ref.Get_phase_proportion(); + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, conc); + } + comp_ref.Set_totals(elt_list_NameDouble()); +/* + * make sure exchange elements are in phase + */ + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, -comp_ref.Get_phase_proportion()); + } + int l; + class phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cptr = phase_ptr->formula; + get_elts_in_species(&cptr, 1.0); + } + else + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + elt_list_combine(); + for (jj = 0; jj < count_elts; jj++) + { + if (elt_list[jj].elt->primary->s->type != EX + && elt_list[jj].coef < 0) + { + input_error++; + error_string = sformatf( + "Stoichiometry of exchanger, %s * %g mol sites/mol phase,\n\tmust be a subset of the related phase %s, %s.", + comp_ref.Get_formula().c_str(), + (double) comp_ref.Get_phase_proportion(), + phase_ptr->name, + phase_ptr->formula); + error_msg(error_string, CONTINUE); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +update_min_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion. Need to check in case exchange or min + * are modified. + */ +{ + int n, jj; + const char* cptr; + LDBLE conc; + + std::map::iterator it = Rxn_exchange_map.begin(); + for ( ; it != Rxn_exchange_map.end(); it++) + { + cxxExchange* exchange_ptr = &(it->second); + if (exchange_ptr->Get_n_user() < 0) continue; + n = exchange_ptr->Get_n_user(); + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + double comp_moles = 0.0; + cxxExchComp& comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_phase_name().size() == 0) continue; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + class element* elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf("Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + { + comp_moles = kit->second; + found_exchange = true; + } + } + //if (!found_exchange) + //{ + // input_error++; + // error_string = sformatf( + // "Exchange formula does not contain an exchange master species, %s", + // comp_ref.Get_formula().c_str()); + // error_msg(error_string, CONTINUE); + // continue; + //} + + cxxPPassemblage* pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + /* Now find the mineral on which exchanger depends... */ + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use exchange related to mineral phase, %s", + n, comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for (; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(comp_ref.Get_phase_name().c_str(), jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + comp_ref.Set_phase_name(jit->first.c_str()); + + /* make exchanger concentration proportional to mineral ... */ + conc = jit->second.Get_moles() * comp_ref.Get_phase_proportion(); + if (found_exchange && comp_moles > 0.0) + { + /* parse formula */ + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, 1.0); + } + cxxNameDouble nd_formula = elt_list_NameDouble(); + double comp_coef = 0; + for (kit = nd_formula.begin(); kit != nd_formula.end(); kit++) + { + /* Find master species */ + class element* elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr->master->type == EX) + { + comp_coef = kit->second; + } + } + comp_ref.multiply(comp_coef * conc / comp_moles); + } + else /* comp_moles is zero, need to redefine totals from scratch */ + { + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, conc); + } + comp_ref.Set_totals(elt_list_NameDouble()); + /* + * make sure exchange elements are in phase + */ + count_elts = 0; + paren_count = 0; + { + cptr = comp_ref.Get_formula().c_str(); + get_elts_in_species(&cptr, -comp_ref.Get_phase_proportion()); + } + int l; + class phase* phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cptr = phase_ptr->formula; + get_elts_in_species(&cptr, 1.0); + } + else + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + elt_list_combine(); + for (jj = 0; jj < count_elts; jj++) + { + if (elt_list[jj].elt->primary->s->type != EX + && elt_list[jj].coef < 0) + { + input_error++; + error_string = sformatf( + "Stoichiometry of exchanger, %s * %g mol sites/mol phase,\n\tmust be a subset of the related phase %s, %s.", + comp_ref.Get_formula().c_str(), + (double)comp_ref.Get_phase_proportion(), + phase_ptr->name, + phase_ptr->formula); + error_msg(error_string, CONTINUE); + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_min_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion + */ +{ + //std::map::iterator kit; + //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + // std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator kit = Rxn_surface_map.find(*nit); + if (kit == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface *surface_ptr = &(kit->second); + if (!surface_ptr->Get_new_def()) + continue; + if (surface_ptr->Get_n_user() < 0) + continue; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + cxxSurfaceCharge *surface_charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); + if (surface_comp_ptr->Get_phase_name().size() == 0) + continue; + int n = surface_ptr->Get_n_user(); + + /* First find surface master species */ + + cxxNameDouble::iterator it; + for (it = surface_comp_ptr->Get_totals().begin(); it != surface_comp_ptr->Get_totals().end(); it++) + { + /* Find master species */ + class element *elt_ptr = element_store(it->first.c_str()); + class master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + surface_comp_ptr->Set_master_element(elt_ptr->name); + break; + } + if (surface_comp_ptr->Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "Surface formula does not contain a surface master species, %s", + surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find the mineral on which surface depends... */ + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use surface related to mineral phase, %s", + n, surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(surface_comp_ptr->Get_phase_name().c_str(), + jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in Equilibrium_Phases %d", + surface_comp_ptr->Get_phase_name().c_str(), surface_comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + int l; + class phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in database.", + jit->first.c_str(), surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + surface_comp_ptr->Set_phase_name(phase_ptr->name); + /* make surface concentration proportional to mineral ... */ + LDBLE conc = jit->second.Get_moles() * surface_comp_ptr->Get_phase_proportion(); +/* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ + { + char * temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); + const char* cptr = temp_formula; + count_elts = 0; + paren_count = 0; + get_elts_in_species(&cptr, conc); + free_check_null(temp_formula); + } + { + if (surface_ptr->Get_new_def()) + { + cxxNameDouble nd = elt_list_NameDouble(); + surface_comp_ptr->Set_totals(nd); + } + else + { + surface_comp_ptr->Get_totals()[surface_comp_ptr->Get_master_element()] = conc; + } + } + + /* area */ + if (surface_charge_ptr) + { + surface_charge_ptr->Set_grams(jit->second.Get_moles()); + } +/* + * make sure surface elements are in phase + * logically necessary for mass balance and to avoid negative concentrations when dissolving phase + */ + count_elts = 0; + paren_count = 0; + { + const char* cptr = phase_ptr->formula; + get_elts_in_species(&cptr, 1.0); + } + // Revise logic for surface related to mineral + for (size_t jj = 0; jj < surface_ptr->Get_surface_comps().size(); jj++) + { + cxxSurfaceComp *comp_jj_ptr = &(surface_ptr->Get_surface_comps()[jj]); + // Use formula for all types of surfaces + { + const char* cptr = comp_jj_ptr->Get_formula().c_str(); + get_elts_in_species(&cptr, -comp_jj_ptr->Get_phase_proportion()); + + if (surface_ptr->Get_type() != cxxSurface::CD_MUSIC) + { + + // Warn if not master species and charge balanced + class element *elt_ptr = element_store(comp_jj_ptr->Get_master_element().c_str()); + if (elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf("Unknown element definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", + surface_ptr->Get_n_user()); + error_msg(error_string); + continue; + } + if (elt_ptr->master->s == NULL || elt_ptr->master->s->name == NULL) + { + input_error++; + error_string = sformatf("Unknown master species definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", + surface_ptr->Get_n_user()); + error_msg(error_string); + continue; + } + //if (strcmp(elt_ptr->master->s->name, temp_formula) != 0) + //{ + // error_string = sformatf("Suggest using master species formula in SURFACE \n\t for surface related to equilibrium_phase: %s.", + // elt_ptr->master->s->name); + // warning_msg(error_string); + //} + if (elt_ptr->master->s->z != 0.0 && surface_ptr->Get_dl_type() != cxxSurface::DONNAN_DL) + { + error_string = sformatf( + "Use the -donnan option when coupling surface %s to an equilibrium_phase, \n\t and note to give the equilibrium_phase the surface charge.", + elt_ptr->master->s->name); + warning_msg(error_string); + } + } + } + } + elt_list_combine(); + /* Makes no sense: sorbed species need not be in mineral structure... */ + /* But elements that can desorb into solution must be in mineral */ + /* If you precipitate Ca-Mont, and make SurfMg (assuming this is the + formula in SURFACE), where does the Mg come from? + Further, if you precipitate Ca-Mont, make SurfCa, desorb + all the Ca, then dissolve the "Ca-Mont", you must remove SurfCa, or you + will end up with Ca in solution. H and O are excluded */ + /* Example that makes montmorillonite a cation exchanger: + PHASES + Summ_Montmorillonite; Al2.33Si3.67O10(OH)2-0.33 + 12 H2O = 2.33 Al(OH)4- + 3.67 H4SiO4 + 2 H+; -log_k -44.4 + SURFACE_MASTER_SPECIES; Summ Summ-; SURFACE_SPECIES; Summ- = Summ- + SOLUTION 1; Na 1e1; Cl 1e1; pH 7 charge; C(4) 1 CO2(g) -2 + EQUILIBRIUM_PHASES 1; Ca-Montmorillonite 0 1e-3 + Summ_Montmorillonite 0 0 + SURFACE 1; Summ Summ_Montmorillonite 0.33 3.11e5; -donnan; -equil 1 + END + */ + for (int jj = 0; jj < count_elts; jj++) + { + if (elt_list[jj].elt->primary == NULL) + { + error_string = sformatf("Primary master species missing for %s", + elt_list[jj].elt->name); + error_msg(error_string, CONTINUE); + break; + } + if (elt_list[jj].elt->primary->s == NULL) + { + error_string = sformatf( + "Species missing for %s", elt_list[jj].elt->name); + error_msg(error_string, CONTINUE); + break; + } + + if (elt_list[jj].elt->primary->s->type != SURF + && elt_list[jj].coef < 0 + //&& elt_list[jj].elt->primary->s != s_hplus + //&& elt_list[jj].elt->primary->s != s_h2o + ) + { + class element *elt_ptr = element_store(surface_comp_ptr->Get_master_element().c_str()); + error_string = sformatf( + "Element %s in sum of surface sites,\n" + "\t including %s * %g mol sites/mol phase,\n" + "\t exceeds stoichiometry in the related phase %s, %s.", + elt_list[jj].elt->name, + elt_ptr->master->s->name, + (double) surface_comp_ptr->Get_phase_proportion(), + phase_ptr->name, + phase_ptr->formula); + warning_msg(error_string); + warning_msg("The mismatch in stoichiometry may cause mass-balance errors or unwanted redox reactions."); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +update_min_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion + */ +{ + std::map::iterator kit; + for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) + { + cxxSurface* surface_ptr = &(kit->second); + if (surface_ptr->Get_n_user() < 0) continue; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + double comp_moles = 0.0; + cxxSurfaceComp* surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_phase_name().size() == 0) continue; + cxxSurfaceCharge* surface_charge_ptr = NULL; + if (surface_ptr->Get_type() != cxxSurface::NO_EDL) + { + surface_charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); + if (surface_charge_ptr == NULL) + { + input_error++; + error_string = sformatf("Data structure for surface charge not found " + "for %s ", + surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + } + int n = surface_ptr->Get_n_user(); + + /* First find surface master species */ + cxxNameDouble::iterator it; + for (it = surface_comp_ptr->Get_totals().begin(); it != surface_comp_ptr->Get_totals().end(); it++) + { + /* Find master species */ + class element* elt_ptr = element_store(it->first.c_str()); + class master* master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf("Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) continue; + comp_moles = it->second; + surface_comp_ptr->Set_master_element(elt_ptr->name); + break; + } + //if (surface_comp_ptr->Get_master_element().size() == 0) + //{ + // input_error++; + // error_string = sformatf( + // "Surface formula does not contain a surface master species, %s", + // surface_comp_ptr->Get_formula().c_str()); + // error_msg(error_string, CONTINUE); + // continue; + //} + + /* Now find the mineral on which surface depends... */ + cxxPPassemblage* pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use surface related to mineral phase, %s", + n, surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for (; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(surface_comp_ptr->Get_phase_name().c_str(), + jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in Equilibrium_Phases %d", + surface_comp_ptr->Get_phase_name().c_str(), surface_comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + int l; + class phase* phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in database.", + jit->first.c_str(), surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + surface_comp_ptr->Set_phase_name(phase_ptr->name); + /* make surface concentration proportional to mineral ... */ + LDBLE conc = jit->second.Get_moles() * surface_comp_ptr->Get_phase_proportion(); + double grams = 0.0; + if (surface_charge_ptr != NULL) + { + grams = surface_charge_ptr->Get_grams(); + } + if (comp_moles > 0.0) + { + surface_comp_ptr->multiply(conc / comp_moles); + } + else /* need to generate from scratch */ + { + char* temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); + const char* cptr = temp_formula; + count_elts = 0; + paren_count = 0; + get_elts_in_species(&cptr, conc); + free_check_null(temp_formula); + + cxxNameDouble nd = elt_list_NameDouble(); + surface_comp_ptr->Set_totals(nd); + } + if (grams > 0.0) + { + surface_charge_ptr->multiply(jit->second.Get_moles() / grams); + } + else if (surface_charge_ptr != NULL) /* need to generate from scratch */ + { + surface_charge_ptr->Set_grams(jit->second.Get_moles()); + surface_charge_ptr->Set_charge_balance(0.0); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_kin_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion + */ +{ + cxxKinetics *kinetics_ptr; + class phase *phase_ptr; + std::vector elt_list_kinetics; + size_t count_elts_kinetics; + + //std::map::iterator it; + //for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + // std::map::iterator it = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator it = Rxn_surface_map.find(*nit); + if (it == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface *surface_ptr = &(it->second); + if (!surface_ptr->Get_new_def()) + continue; + if (surface_ptr->Get_n_user() < 0) + continue; + int n = surface_ptr->Get_n_user(); + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (comp_ptr->Get_rate_name().size() == 0) + continue; + comp_ptr->Set_master_element(""); + + /* First find surface master species */ + int k; + cxxNameDouble::iterator kit; + for (kit = comp_ptr->Get_totals().begin(); kit != comp_ptr->Get_totals().end(); kit++) + { + /* Find master species */ + class element *elt_ptr = element_store(kit->first.c_str()); + class master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + comp_ptr->Set_master_element(elt_ptr->name); + break; + } + if (comp_ptr->Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "Surface formula does not contain a surface master species, %s", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find the kinetic reaction on which surface depends... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n)) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use surface related to kinetic reaction, %s", + n, comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + for (k = 0; k < (int) kinetics_ptr->Get_kinetics_comps().size(); k++) + { + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + if (strcmp_nocase + (comp_ptr->Get_rate_name().c_str(), + kin_comp_ptr->Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == (int) kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to surface, %s, not found in Kinetics %d", + comp_ptr->Get_rate_name().c_str(), comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + /* use database name for phase */ + comp_ptr->Set_rate_name(kin_comp_ptr->Get_rate_name().c_str()); + + /* make surface concentration proportional to mineral ... */ + LDBLE conc = kin_comp_ptr->Get_m() * comp_ptr->Get_phase_proportion(); + +/* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ + { + const char* cptr = comp_ptr->Get_formula().c_str(); + count_elts = 0; + paren_count = 0; + get_elts_in_species(&cptr, conc); + } + { + if (surface_ptr->Get_new_def()) + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + else + { + comp_ptr->Get_totals()[comp_ptr->Get_master_element()] = conc; + } + } + + /* area */ + + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + charge_ptr->Set_grams(kin_comp_ptr->Get_m()); + } +/* + * check on elements + */ + /* Go through each kinetic reaction, add all related surface compositions + * check for negative values + */ + if (!surface_ptr->Get_related_rate()) + continue; + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n); + if (kinetics_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Error in SURFACE related to KINETICS. "); + error_msg(error_string, CONTINUE); + continue; + } + for (size_t k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) + { + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + count_elts = 0; + paren_count = 0; + + /* added in kinetics formula */ + cxxNameDouble::iterator jit = kin_comp_ptr->Get_namecoef().begin(); + for (; jit != kin_comp_ptr->Get_namecoef().end(); jit++) + { + std::string name = jit->first; + LDBLE coef = jit->second; + phase_ptr = NULL; + int jj; + phase_ptr = phase_bsearch(name.c_str(), &jj, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, 1.0); + } + else + { + const char* cptr = name.c_str(); + get_elts_in_species(&cptr, coef); + } + } + /* save kinetics formula */ + if (count_elts > 0) + { + elt_list_combine(); + } + elt_list_kinetics = elt_list_vsave(); + count_elts_kinetics = count_elts; + + /* get surface formulas */ + count_elts = 0; + paren_count = 0; + cxxSurfaceComp *comp_ptr_save = NULL; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (comp_ptr->Get_rate_name().size() == 0) + continue; + comp_ptr_save = comp_ptr; + if (strcmp_nocase + (comp_ptr->Get_rate_name().c_str(), + kin_comp_ptr->Get_rate_name().c_str()) == 0) + { + const char* cptr = comp_ptr->Get_formula().c_str(); + get_elts_in_species(&cptr, -1 * comp_ptr->Get_phase_proportion()); + } + } + elt_list_combine(); + for (int j = 0; j < count_elts; j++) + { + if (elt_list[j].elt == NULL) + { + input_error++; + error_string = sformatf( + "Cannot identify elements in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_list[j].elt->primary == NULL ) + { + input_error++; + error_string = sformatf( + "Cannot identify primary element in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_list[j].elt->primary->s == NULL) + { + input_error++; + error_string = sformatf( + "Cannot identify primary species for an element in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + if (elt_list[j].elt->primary->s->type <= H2O) + { + int l; + for (l = 0; l < count_elts_kinetics; l++) + { + if (elt_list[j].elt == elt_list_kinetics[l].elt) + { + break; + } + } + if (l == count_elts_kinetics) + { + input_error++; + error_string = sformatf( + "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tElement %s is not present in reactant formula.", + comp_ptr_save->Get_formula().c_str(), + (double) comp_ptr_save->Get_phase_proportion(), + comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); + error_msg(error_string, CONTINUE); + } + else if (fabs(elt_list[j].coef) > + fabs(elt_list_kinetics[l].coef)) + { + input_error++; + error_string = sformatf( + "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tCoefficient of element %s in surface exceeds amount present in reactant formula.", + comp_ptr_save->Get_formula().c_str(), + (double) comp_ptr_save->Get_phase_proportion(), + comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); + error_msg(error_string, CONTINUE); + } + } + } + elt_list_kinetics.clear(); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +update_kin_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion. Need to update surface if + * moles of kinetic reaction changes + */ +{ + cxxKinetics* kinetics_ptr; + + std::map::iterator it; + for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) + { + cxxSurface* surface_ptr = &(it->second); + if (surface_ptr->Get_n_user() < 0) continue; + int n = surface_ptr->Get_n_user(); + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + double comp_moles = 0.0; + cxxSurfaceComp* comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (comp_ptr->Get_rate_name().size() == 0) continue; + comp_ptr->Set_master_element(""); + + /* First find surface master species */ + int k; + cxxNameDouble::iterator kit; + for (kit = comp_ptr->Get_totals().begin(); kit != comp_ptr->Get_totals().end(); kit++) + { + /* Find master species */ + class element* elt_ptr = element_store(kit->first.c_str()); + class master* master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf("Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) continue; + comp_ptr->Set_master_element(elt_ptr->name); + comp_moles = kit->second; + break; + } + if (comp_ptr->Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "Surface formula does not contain a surface master species, %s", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find the kinetic reaction on which surface depends... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n)) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use surface related to kinetic reaction, %s", + n, comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + for (k = 0; k < (int)kinetics_ptr->Get_kinetics_comps().size(); k++) + { + cxxKineticsComp* kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + if (strcmp_nocase + (comp_ptr->Get_rate_name().c_str(), + kin_comp_ptr->Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == (int)kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to surface, %s, not found in Kinetics %d", + comp_ptr->Get_rate_name().c_str(), comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + + cxxKineticsComp* kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + /* use database name for rate */ + comp_ptr->Set_rate_name(kin_comp_ptr->Get_rate_name().c_str()); + cxxSurfaceCharge* charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + if (surface_ptr->Get_type() != cxxSurface::NO_EDL) + { + charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + if (charge_ptr == NULL) + { + input_error++; + error_string = sformatf("Data structure for surface charge not found " + "for %s ", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + } + /* make surface concentration proportional to mineral ... */ + LDBLE conc = kin_comp_ptr->Get_m() * comp_ptr->Get_phase_proportion(); + double grams = 0.0; + if (charge_ptr != NULL) charge_ptr->Get_grams(); + if (comp_moles > 0.0) + { + comp_ptr->multiply(conc / comp_moles); + } + else /* need to generate from scratch */ + { + const char* cptr = comp_ptr->Get_formula().c_str(); + count_elts = 0; + paren_count = 0; + get_elts_in_species(&cptr, conc); + + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + + if (grams > 0.0) + { + charge_ptr->multiply(kin_comp_ptr->Get_m() / grams); + } + else if (charge_ptr != NULL) /* need to generate from scratch */ + { + charge_ptr->Set_grams(kin_comp_ptr->Get_m()); + charge_ptr->Set_charge_balance(0.0); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_prep(LDBLE t, cxxSS *ss_ptr, int print) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, converged, divisions; + LDBLE r, rt, ag0, ag1, crit_pt; + LDBLE xc, tc; + LDBLE l_x, x0, x1, xsm1, xsm2, xb1, xb2; + LDBLE xc1, xc2; + LDBLE facb1, faca1, spim1, xblm1, acrae, acrael, xliapt, xliapm; + LDBLE xaly, xaly1, xaly2; + LDBLE faca, facb, spialy, facal, facbl; + LDBLE tol; + + if (pr.ss_assemblage == FALSE) + print = FALSE; + tol = 1e-6; + r = R_KJ_DEG_MOL; + rt = r * t; + a0 = ss_ptr->Get_ag0() / rt; + a1 = ss_ptr->Get_ag1() / rt; + ss_ptr->Set_a0(a0); + ss_ptr->Set_a1(a1); + ag0 = a0 * rt; + ag1 = a1 * rt; + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); + class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); + kc = exp(k_calc(phase0_ptr->rxn.logk, t, REF_PRES_PASCAL) * LOG_10); + kb = exp(k_calc(phase1_ptr->rxn.logk, t, REF_PRES_PASCAL) * LOG_10); + crit_pt = fabs(a0) + fabs(a1); +/* + * Default, no miscibility or spinodal gaps + */ + ss_ptr->Set_miscibility(false); + ss_ptr->Set_spinodal(false); + xsm1 = 0.5; + xsm2 = 0.5; + xb1 = 0.5; + xb2 = 0.5; + xc1 = 0; + xc2 = 0; + + if (crit_pt >= tol) + { +/* + * Miscibility gap information + */ + if (fabs(a1) < tol) + { + xc = 0.5; + tc = ag0 / (2 * r); + } + else + { + xc = 0.5 + (pow((ag0 * ag0 + 27 * ag1 * ag1), (LDBLE) 0.5) - + ag0) / (18 * ag1); + tc = (12 * ag1 * xc - 6 * ag1 + 2 * ag0) * (xc - xc * xc) / r; + } + if (print == TRUE) + { + error_string = sformatf( "Description of Solid Solution %s", + ss_ptr->Get_name().c_str()); + dup_print(error_string, TRUE); + } + if (print == TRUE) + { + output_msg(sformatf( + "\t Temperature: %g kelvin\n", + (double) t)); + output_msg(sformatf( + "\t A0 (dimensionless): %g\n", + (double) a0)); + output_msg(sformatf( + "\t A1 (dimensionless): %g\n", + (double) a1)); + output_msg(sformatf( + "\t A0 (kJ/mol): %g\n", + (double) ag0)); + output_msg(sformatf( + "\t A1 (kJ/mol): %g\n\n", + (double) ag1)); + } + if (xc < 0 || xc > 1) + { + if (print == TRUE) + output_msg(sformatf( + "No miscibility gap above 0 degrees kelvin.\n")); + } + else + { + if (print == TRUE) + { + output_msg(sformatf( + "\t Critical mole-fraction of component 2: %g\n", + (double) xc)); + output_msg(sformatf( + "\t Critical temperature: %g kelvin\n", + (double) tc)); + output_msg(sformatf( + "\n(The critical temperature calculation assumes that the Guggenheim model\ndefined at %g kelvin is valid at the critical temperature.)\n\n\n", + (double) t)); + } + } +/* + * Calculate miscibility and spinodal gaps + */ + if (tc >= t) + { + + /* search for sign changes */ + x0 = 0; + x1 = 1; + if (scan(f_spinodal, &x0, &x1) == TRUE) + { + + /* find first spinodal pt */ + xsm1 = halve(f_spinodal, x0, x1, tol); + xsm1 = halve(f_spinodal, x0, x1, tol); + ss_ptr->Set_spinodal(true); + + /* find second spinodal pt */ + x0 = x1; + x1 = 1; + if (scan(f_spinodal, &x0, &x1) == TRUE) + { + xsm2 = halve(f_spinodal, x0, x1, tol); + } + else + { + error_msg("Failed to find second spinodal point.", STOP); + } + } + } + } +/* + * Now find Miscibility gap + */ + if (ss_ptr->Get_spinodal()) + { + if (print == TRUE) + output_msg(sformatf( + "\t Spinodal-gap mole fractions, component 2: %g\t%g\n", + (double) xsm1, (double) xsm2)); + converged = FALSE; + if (converged == FALSE) + { + for (i = 1; i < 3; i++) + { + divisions = (int) pow(10., i); + for (j = 0; j < divisions; j++) + { + for (k = divisions; k > 0; k--) + { + xc1 = (LDBLE) j / divisions + 0.001; + xc2 = (LDBLE) k / divisions; + converged = solve_misc(&xc1, &xc2, tol); + if (converged == TRUE) + break; + } + if (converged == TRUE) + break; + } + if (converged == TRUE) + break; + } + } + if (converged == FALSE) + { + error_msg("Failed to find miscibility gap.", STOP); + } + ss_ptr->Set_miscibility(true); + if (xc1 < xc2) + { + xb1 = 1 - xc2; + xb2 = 1 - xc1; + xc1 = 1 - xb1; + xc2 = 1 - xb2; + } + else + { + xb1 = 1 - xc1; + xb2 = 1 - xc2; + } + facb1 = kb * xb1 * exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); + faca1 = kc * xc1 * exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); + spim1 = log10(faca1 + facb1); + xblm1 = 1. / (1. + faca1 / facb1); + acrae = facb1 / faca1; + acrael = log10(acrae); + xliapt = log10(facb1); + xliapm = log10(faca1); + + if (print == TRUE) + { + output_msg(sformatf( + "\t Miscibility-gap fractions, component 2: %g\t%g\n", + (double) xb1, (double) xb2)); + output_msg(sformatf( + "\n\t\t\tEutectic Point Calculations\n\n")); + output_msg(sformatf( + "\t Aqueous activity ratio (comp2/comp1): %g\n", + (double) acrae)); + output_msg(sformatf( + "\t Log aqueous activity ratio (comp2/comp1): %g\n", + (double) acrael)); + output_msg(sformatf( + "\t Aqueous activity fraction of component 2: %g\n", + (double) xblm1)); + output_msg(sformatf( + "\t Log IAP (component 2): %g\n", + (double) xliapt)); + output_msg(sformatf( + "\t Log IAP (component 1): %g\n", + (double) xliapm)); + output_msg(sformatf( + "\t Log Sum Pi: %g\n", + (double) spim1)); + } + ss_ptr->Set_tk(t); + ss_ptr->Set_xb1(xb1); + ss_ptr->Set_xb2(xb2); + } +/* + * Alyotropic point calculation + */ + xaly = -1.0; + l_x = a0 * a0 + 3 * a1 * a1 + 6 * a1 * log(kb / kc); + if (l_x > 0) + { + if (fabs(l_x - a0 * a0) >= tol) + { + xaly1 = (-(a0 - 3 * a1) + pow(l_x, (LDBLE) 0.5)) / (6 * a1); + xaly2 = (-(a0 - 3 * a1) - pow(l_x, (LDBLE) 0.5)) / (6 * a1); + if (xaly1 >= 0 && xaly1 <= 1) + { + xaly = xaly1; + } + if (xaly2 >= 0 && xaly2 <= 1) + { + xaly = xaly2; + } + } + else + { + xaly = 0.5 + log(kb / kc) / (2 * a0); + } + if (xaly > 0 && xaly < 1) + { + faca = + kc * (1 - + xaly) * exp(xaly * xaly * (a0 - a1 * (3 - 4 * xaly))); + facb = + kb * xaly * exp((1 - xaly) * (1 - xaly) * + (a0 + a1 * (4 * xaly - 1.0))); + spialy = log10(faca + facb); + facal = log10(faca); + facbl = log10(facb); + if (xaly > xb1 && xaly < xb2) + { + if (print == TRUE) + output_msg(sformatf( + "\nLocal minimum in the solidus curve coresponding to a maximum\nin the minimum stoichiometric saturation curve.\n\n")); + } + else + { + if (print == TRUE) + output_msg(sformatf( + "\n\t\t\tAlyotropic Point\n\n")); + } + if (print == TRUE) + { + output_msg(sformatf( + "\t Solid mole fraction of component 2: %g\n", + (double) xaly)); + output_msg(sformatf( + "\t Log IAP (component 2): %g\n", + (double) facbl)); + output_msg(sformatf( + "\t Log IAP (component 1): %g\n", + (double) facal)); + output_msg(sformatf( + "\t Log Sum Pi: %g\n", + (double) spialy)); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +halve(LDBLE f(LDBLE x, void *), LDBLE x0, LDBLE x1, LDBLE tol) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE l_x, y, y0, dx; + + y0 = f(x0, this); + dx = (x1 - x0); +/* + * Loop for interval halving + */ + for (i = 0; i < 100; i++) + { + dx *= 0.5; + l_x = x0 + dx; + y = f(l_x, this); + if (dx < tol || y == 0) + { + break; + } + if (y0 * y >= 0) + { + x0 = l_x; + y0 = y; + } + } + return (x0 + dx); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +scan(LDBLE f(LDBLE x, void *), LDBLE * xx0, LDBLE * xx1) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + LDBLE fx0, fx1, divisions; + LDBLE x0, x1, diff; + + x0 = *xx0; + x1 = *xx1; + diff = x1 - x0; + for (j = 0; j < 3; j++) + { + fx0 = f(x0, this); + divisions = (int) pow((LDBLE) 10, (LDBLE) j); + for (i = 1; i < divisions; i++) + { + x1 = *xx0 + diff * (LDBLE) i / divisions; + fx1 = f(x1, this); + if (fx0 * fx1 <= 0) + { + *xx0 = x0; + *xx1 = x1; + return (TRUE); + } + x0 = x1; + fx0 = fx1; + } + } + return (FALSE); +} + + +/* ---------------------------------------------------------------------- */ + LDBLE Phreeqc:: +f_spinodal(LDBLE x, void * cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE fx; + Phreeqc * pThis; + pThis = (Phreeqc *) cookie; + fx = -12 * pThis->a1 * x * x * x + (18 * pThis->a1 - + 2 * pThis->a0) * x * x + (2 * pThis->a0 - + 6 * pThis->a1) * x - 1.0; + return (fx); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +slnq(int n, LDBLE * a, LDBLE * l_delta, int ncols, int print) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, m; +/* debug + */ + int row; + + LDBLE b; +/* Debug +*/ + if (print == TRUE) + { + output_msg(sformatf( "\nArray in slnq: \n\n")); + for (i = 0; i < ncols - 1; i++) + { + row = i * (n + 1); + for (j = 0; j < ncols; j++) + { + output_msg(sformatf( "%10.2e", (double) a[row + j])); + } + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "\n")); + } + + if (n == 0) + return (OK); +/* Trivial case */ + if (n == 1) + { + if (fabs(a[0]) < ZERO_TOL) + goto slnq_error; + l_delta[0] = a[1] / a[0]; + return (OK); + } + +/* Reduction loop */ + for (i = 0; i < n - 1; i++) + { + b = fabs(a[i * ncols + i]); + m = i; + +/* Find maximum value in column */ + for (j = i + 1; j < n; j++) + { + if (fabs(a[j * ncols + i]) > b) + { + b = fabs(a[j * ncols + i]); + m = j; + } + } + +/* Check for singularity */ + if (b < ZERO_TOL) + goto slnq_error; + +/* Exchange rows if necessary */ + if (m != i) + { + for (j = i; j <= n; j++) + { + b = a[i * ncols + j]; + a[i * ncols + j] = a[m * ncols + j]; + a[m * ncols + j] = b; + } + } + +/* Make a[i][i]=1.0 */ + for (j = n; j >= i; j--) + { + a[i * ncols + j] /= a[i * ncols + i]; + } + +/* Reduction step */ + for (j = i + 1; j < n; j++) + { + if (a[j * ncols + i] == 0.0) + continue; + b = -a[j * ncols + i]; + for (k = i + 1; k <= n; k++) + { + a[j * ncols + k] += b * a[i * ncols + k]; + } + } + } + +/* Calculation of l_delta[n] */ + if (fabs(a[(n - 1) * ncols + n - 1]) > ZERO_TOL) + { + l_delta[n - 1] = a[(n - 1) * ncols + n] / a[(n - 1) * ncols + n - 1]; + } + else + { + output_msg(sformatf( "Error: Divide by zero in slnq.\n")); + l_delta[n] = 0.0; + goto slnq_error; + } + +/* Back substitution for other delta values */ + for (i = n - 2; i >= 0; i--) + { + l_delta[i] = a[i * ncols + n]; + for (j = i + 1; j < n; j++) + { + l_delta[i] -= a[i * ncols + j] * l_delta[j]; + } + } + if (print == TRUE) + { + output_msg(sformatf( "\nResults from slnq: \n\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%10.2e", (double) l_delta[i])); + } + output_msg(sformatf( "\n")); + } + return (OK); + + slnq_error:{ + error_string = sformatf( + "Error: Singular matrix in subroutine slnq. \n"); + warning_msg(error_string); + } + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +solve_misc(LDBLE * xxc1, LDBLE * xxc2, LDBLE tol) +/* ---------------------------------------------------------------------- */ +{ + int i, repeat, converged, max_iter; + LDBLE x1, x2, xb1, xb2; + LDBLE xc1, xc1_2, xc1_3, xc2, xc2_2, xc2_3; + LDBLE lc1, lc2, lb1, lb2; + LDBLE a[6], d[2]; + LDBLE t; + + d[0] = d[1] = 0; + xc1 = *xxc1; + xc2 = *xxc2; + x1 = 0; + x2 = 0; + converged = TRUE; + max_iter = 25; + for (i = 0; i < max_iter; i++) + { + /* + output_msg(sformatf( "%d xc1: %g\txc2 %g\n", i, xc1, xc2)); + */ + xb1 = 1 - xc1; + xb2 = 1 - xc2; + xc1_2 = xc1 * xc1; + xc1_3 = xc1_2 * xc1; + xc2_2 = xc2 * xc2; + xc2_3 = xc2_2 * xc2; + + lc1 = exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); + lb1 = exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); + lc2 = exp(xb2 * xb2 * (a0 - a1 * (3 - 4 * xb2))); + lb2 = exp(xc2 * xc2 * (a0 + a1 * (4 * xb2 - 1))); + + /* -fb */ + a[2] = -(xb1 * lb1 - xb2 * lb2); + + /* -fc */ + a[5] = -(xc1 * lc1 - xc2 * lc2); + + if (fabs(a[2]) < tol && fabs(a[5]) < tol) + break; + + /* dfb/dxc1 */ + t = exp(a0 * xc1_2 - 4 * a1 * xc1_3 + 3 * a1 * xc1_2); + a[0] = + (2 * a0 * xc1 + 6 * a1 * xc1 - 2 * a0 * xc1_2 + 12 * a1 * xc1_3 - + 18 * a1 * xc1_2 - 1) * t; + + /* dfb/dxc2 */ + t = exp(a0 * xc2_2 - 4 * a1 * xc2_3 + 3 * a1 * xc2_2); + a[1] = + (2 * a0 * xc2_2 - 12 * a1 * xc2_3 - 2 * a0 * xc2 + + 18 * a1 * xc2_2 - 6 * a1 * xc2 + 1) * t; + + + /* dfc/dxc1 */ + t = exp(a0 * xc1_2 - 2 * a0 * xc1 + a0 - 4 * a1 * xc1_3 + + 9 * a1 * xc1_2 - 6 * a1 * xc1 + a1); + a[3] = + (2 * a0 * xc1_2 - 2 * a0 * xc1 - 12 * a1 * xc1_3 + + 18 * a1 * xc1_2 - 6 * a1 * xc1 + 1) * t; + + /* dfc/dxc2 */ + t = exp(a0 * xc2_2 - 2 * a0 * xc2 + a0 - 4 * a1 * xc2_3 + + 9 * a1 * xc2_2 - 6 * a1 * xc2 + a1); + a[4] = + (-2 * a0 * xc2_2 + 2 * a0 * xc2 + 12 * a1 * xc2_3 - + 18 * a1 * xc2_2 + 6 * a1 * xc2 - 1) * t; + + + /* solve for dxc1 and dxc2 */ + slnq(2, a, d, 3, FALSE); + + repeat = TRUE; + while (repeat == TRUE) + { + x1 = xc1 + d[0]; + x2 = xc2 + d[1]; + if (x1 > 1 || x1 < 0 || x2 > 1 || x2 < 0) + { + d[0] *= 0.5; + d[1] *= 0.5; + } + else + { + repeat = FALSE; + } + }; + xc1 = x1; + xc2 = x2; + + if (fabs(xc1 - xc2) < .01) + { + converged = FALSE; + break; + } + } + if (i == max_iter) + converged = FALSE; + *xxc1 = xc1; + *xxc2 = xc2; + return (converged); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +ss_calc_a0_a1(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, done; + LDBLE r, rt; + std::vector p; + LDBLE q1, q2, xbq1, xbq2, xb1, xb2, xc1, xc2; + LDBLE r1, r2, pa1, pb1, pa2, pb2, xsm1, xsm2; + LDBLE pn9, pn10, c5, c6, pl9, pl10, pj9, pj10; + LDBLE xc, tc; + LDBLE spialy, azero, phi1, phi2, test; + LDBLE dq1, dq2, denom, ratio, dr1, dr2, x21, x22, x61, x62; + LDBLE l_a0, l_a1, ag0, ag1; + LDBLE wg2, wg1, alpha2, alpha3; + LDBLE l_kc, l_kb; + LDBLE xaly, xcaly, alpha0, alpha1, fx, fx1; + LDBLE tol; + + tol = 1e-6; + rt = ss_ptr->Get_tk() * R_KJ_DEG_MOL; + if (ss_ptr->Get_ss_comps().size() < 2) + { + input_error++; + error_string = sformatf( + "Two components not defined for solid solution ", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + int k; + class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); + class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); + if (phase0_ptr == NULL || phase1_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Two components were not defined for %s solid solution", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + l_kc = exp(k_calc(phase0_ptr->rxn.logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * + LOG_10); + l_kb = exp(k_calc(phase1_ptr->rxn.logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * + LOG_10); + + p = ss_ptr->Get_p(); + + l_a0 = 0; + l_a1 = 0; + ag0 = 0; + ag1 = 0; + dq2 = 0; + switch (ss_ptr->Get_input_case()) + { + /* + * dimensionless a0 and a1 + */ + case cxxSS::SS_PARM_A0_A1: + l_a0 = p[0]; + l_a1 = p[1]; + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * two activity coefficients + * q1, q2, xbq1, xbq2 + */ + case cxxSS::SS_PARM_GAMMAS: + q1 = p[0]; + q2 = p[1]; + xbq1 = p[2]; + xbq2 = p[3]; + done = FALSE; + if (fabs(1 - xbq1) > 0 && q1 > 0) + { + dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); + if (xbq2 <= 0 || xbq2 > 1) + { + l_a0 = dq1; + l_a1 = 0; + done = TRUE; + } + } + if (done == FALSE) + { + if (fabs(xbq2) < 0 || q2 <= 0) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + done = TRUE; + } + } + if (done == FALSE) + { + dq2 = log(q2) / (xbq2 * xbq2); + if (xbq1 < 0. || xbq2 > 1.) + { + l_a0 = dq2; + l_a1 = 0; + done = TRUE; + } + } + if (done == FALSE) + { + denom = 4 * (xbq1 - xbq2) + 2; + if (fabs(denom) >= tol) + { + if (fabs(1 - xbq1) > 0 && q1 > 0) + { + dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); + l_a0 = (dq1 * (3 - 4 * xbq2) + + dq2 * (4 * xbq1 - 1)) / denom; + l_a1 = (dq1 - dq2) / denom; + done = TRUE; + } + } + } + if (done == FALSE) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * two distribution coefficients + * q1, q2, xbq1, xbq2 + */ + case cxxSS::SS_PARM_DIST_COEF: + q1 = p[0]; + q2 = p[1]; + xbq1 = p[2]; + xbq2 = p[3]; + ratio = l_kc / l_kb; + dr1 = log(q1 / ratio); + x21 = 2 * xbq1 - 1; + if (fabs(xbq1 - xbq2) < tol || xbq2 < 0) + { + l_a0 = dr1 / x21; + l_a1 = 0; + } + else + { + dr2 = log(q2 / ratio); + x22 = 2 * xbq2 - 1; + if (xbq1 < 0.) + { + l_a0 = dr2 / x22; + l_a1 = 0; + } + else + { + x61 = 6 * xbq1 * xbq1 - 6 * xbq1 + 1; + x62 = 6 * xbq2 * xbq2 - 6 * xbq2 + 1; + if (fabs(x22 * x61 - x21 * x62) < tol) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two distribution coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + l_a0 = (x61 * dr2 - x62 * dr1) / (x22 * x61 - x21 * x62); + l_a1 = (x21 * dr2 - x22 * dr1) / (x21 * x62 - x22 * x61); + } + } + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from miscibility gap fractions + * q1, q2 + */ + case cxxSS::SS_PARM_MISCIBILITY: + q1 = p[0]; + q2 = p[1]; + xb1 = q1; + xb2 = q2; + xc1 = 1 - xb1; + xc2 = 1 - xb2; + r1 = log(xb1 / xb2); + r2 = log(xc1 / xc2); + pa1 = xc2 * xc2 - xc1 * xc1; + pb1 = + 3 * (xc2 * xc2 - xc1 * xc1) - 4 * (xc2 * xc2 * xc2 - + xc1 * xc1 * xc1); + pa2 = xb2 * xb2 - xb1 * xb1; + pb2 = + -(3 * (xb2 * xb2 - xb1 * xb1) - + 4 * (xb2 * xb2 * xb2 - xb1 * xb1 * xb1)); + l_a0 = (r1 - pb1 / pb2 * r2) / (pa1 - pa2 * pb1 / pb2); + l_a1 = (r1 - pa1 / pa2 * r2) / (pb1 - pb2 * pa1 / pa2); + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from spinodal gap fractions + * q1, q2 + */ + case cxxSS::SS_PARM_SPINODAL: + q1 = p[0]; + q2 = p[1]; + xsm1 = q1; + xsm2 = q2; + pn9 = 1 / xsm1; + pn10 = 1 / xsm2; + c5 = 1 - xsm1; + c6 = 1 - xsm2; + pl9 = 6 * c5 - 12 * c5 * c5; + pl10 = 6 * c6 - 12 * c6 * c6; + pj9 = 2 * c5; + pj10 = 2 * c6; + l_a0 = (pn9 - pl9 / pl10 * pn10) / (pj9 - pl9 / pl10 * pj10); + l_a1 = (pn9 - pj9 / pj10 * pn10) / (pl9 - pj9 / pj10 * pl10); + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from critical point + * q1, q2 + */ + case cxxSS::SS_PARM_CRITICAL: + xc = p[0]; + tc = p[1]; + r = R_KJ_DEG_MOL; + ag1 = r * tc * (2 * xc - 1) / (12 * xc * xc * (1 - xc) * (1 - xc)); + ag0 = (r * tc / (xc * (1 - xc)) - (12 * xc - 6) * ag1) / 2; + + /* io = 0 */ + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * from alyotropic point + * q1, q2 + */ + case cxxSS::SS_PARM_ALYOTROPIC: + q1 = p[0]; + q2 = p[1]; + xaly = q1; + r = log(l_kb / l_kc); + alpha0 = 2 * xaly - 1; + alpha1 = 6 * xaly * (xaly - 1) + 1; + spialy = pow((LDBLE) 10., q2); + l_a0 = -999.; + l_a1 = -999.; + if (fabs(alpha0) < tol) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + else + { + azero = 1; + if (fabs(alpha0) > tol) + azero = r / alpha0; + xcaly = 1 - xaly; +/* + * Solve for a0 by Newton's method + */ + for (i = 0; i < 50; i++) + { + phi1 = + xcaly * xcaly * (azero + + (r - azero * alpha0) * (4 * xaly - + 1) / alpha1); + phi2 = + xaly * xaly * (azero + + (3 - 4 * xaly) * (azero * alpha0 - + r) / alpha1); + phi1 = xaly * l_kb * exp(phi1); + phi2 = xcaly * l_kc * exp(phi2); + fx = phi1 + phi2 - spialy; + fx1 = + xcaly * xcaly * (1 - + alpha0 * (4 * xaly - + 1) / alpha1) * phi1 + + xaly * xaly * (1 + + alpha0 * (3 - 4 * xaly) / alpha1) * phi2; + if (fabs(fx1) < 1e-10) + { + input_error++; + error_string = sformatf( + "Could not find A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + break; + } + l_a0 = azero - fx / fx1; + test = fabs(l_a0 - azero) + fabs(fx); + azero = l_a0; + if (test < tol) + break; + } + if (i == 50) + { + input_error++; + error_string = sformatf( + "Too many iterations, could not find A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + else + { + l_a1 = (r - l_a0 * alpha0) / alpha1; + + /* io = 0 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + } + + } + break; + /* + * dimensional (kJ/mol) Guggenheim parameters + * ag0, ag1 + */ + case cxxSS::SS_PARM_DIM_GUGG: + ag0 = p[0]; + ag1 = p[1]; + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * Waldbaum-Thompson + * wg2, wg1 + */ + case cxxSS::SS_PARM_WALDBAUM: + wg2 = p[0]; + wg1 = p[1]; + ag0 = (wg2 + wg1) / 2; + ag1 = (wg2 - wg1) / 2; + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * Margules + * alpha2, alpha3 + */ + case cxxSS::SS_PARM_MARGULES: + alpha2 = p[0]; + alpha3 = p[1]; + l_a0 = alpha2 + 3 * alpha3 / 4; + l_a1 = alpha3 / 4; + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + case cxxSS::SS_PARM_NONE: + break; + } + ss_ptr->Set_ag0(ag0); + ss_ptr->Set_ag1(ag1); + ss_ptr->Set_a0(l_a0); + ss_ptr->Set_a1(l_a1); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_master_isotope(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class master *master_ptr; + + for (i = 0; i < (int)master_isotope.size(); i++) + { + /* + * Mark master species list as minor isotope + */ + if (master_isotope[i]->minor_isotope == TRUE) + { + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find master species for isotope, %s", + master_isotope[i]->name); + error_msg(error_string, CONTINUE); + master_isotope[i]->master = NULL; + continue; + } + else + { + master_isotope[i]->master = master_ptr; + } + master_ptr->minor_isotope = TRUE; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class master *master_ptr; + class master_isotope *master_isotope_ptr; + class calculate_value *calculate_value_ptr; + + for (i = 0; i < (int)isotope_ratio.size(); i++) + { + /* + * Mark master species list as minor isotope + */ + master_isotope_ptr = + master_isotope_search(isotope_ratio[i]->isotope_name); + if (master_isotope_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIO %s, did not find ISOTOPE definition for this isotope, %s", + isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); + error_msg(error_string, CONTINUE); + } + master_ptr = master_bsearch(isotope_ratio[i]->isotope_name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIO %s, did not find SOLUTION_MASTER_SPECIES for isotope, %s", + isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); + error_msg(error_string, CONTINUE); + } + calculate_value_ptr = calculate_value_search(isotope_ratio[i]->name); + if (calculate_value_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIOS %s, did not find corresponding CALCULATE_VALUE definition", + isotope_ratio[i]->name); + error_msg(error_string, CONTINUE); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + class calculate_value *calculate_value_ptr; + class logk *logk_ptr; + + for (i = 0; i < (int)isotope_alpha.size(); i++) + { + /* + * Mark master species list as minor isotope + */ + calculate_value_ptr = calculate_value_search(isotope_alpha[i]->name); + if (calculate_value_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_ALPHAS %s, did not find corresponding CALCULATE_VALUE definition", + isotope_alpha[i]->name); + error_msg(error_string, CONTINUE); + } + if (isotope_alpha[i]->named_logk != NULL) + { + logk_ptr = logk_search(isotope_alpha[i]->named_logk); + if (logk_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_ALPHAS %s, did not find corresponding NAMED_EXPRESSION definition %s.", + isotope_alpha[i]->name, isotope_alpha[i]->named_logk); + error_msg(error_string, CONTINUE); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reset_last_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialize model + */ + last_model.force_prep = true; + last_model.gas_phase.clear(); + last_model.ss_assemblage.clear(); + last_model.pp_assemblage.clear(); + last_model.add_formula.clear(); + last_model.si.clear(); + last_model.dl_type = cxxSurface::NO_DL; + last_model.surface_comp.clear(); + last_model.surface_charge.clear(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + // std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + //std::map::iterator it = Rxn_exchange_map.begin(); + cxxExchange * exchange_ptr = &(it->second); + //if (!exchange_ptr->Get_new_def()) + // continue; + //if (exchange_ptr->Get_n_user() < 0) + // continue; + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_phase_name().size() > 0) + continue; + if (comp_ref.Get_rate_name().size() > 0) + continue; + + /* Check elements */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + for (; kit != nd.end(); kit++) + { + /* Find master species */ + class element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + break; + } + } + } + } + return (OK); +} + diff --git a/phreeqcpp/transport.cpp b/phreeqcpp/transport.cpp new file mode 100644 index 00000000..88f33a29 --- /dev/null +++ b/phreeqcpp/transport.cpp @@ -0,0 +1,6183 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include + +LDBLE F_Re3 = F_C_MOL / (R_KJ_DEG_MOL * 1e3); +LDBLE tk_x2; // average tk_x of icell and jcell +LDBLE dV_dcell; // difference in Volt among icell and jcell +int find_current; +char token[MAX_LENGTH]; + +// implicit... +std::set dif_spec_names; +std::set dif_els_names; +std::map > neg_moles; +std::map els; +double *Ct2, *l_tk_x2, **A, **LU, **mixf, **mixf_stag; +int mixf_comp_size = 0; + +struct CURRENT_CELLS +{ + LDBLE dif, ele, R; // diffusive and electric components, relative cell resistance +} *current_cells; +LDBLE sum_R, sum_Rd; // sum of R, sum of (current_cells[0].dif - current_cells[i].dif) * R +struct V_M // For calculating Vinograd and McBain's zero-charge, diffusive tranfer of individual solutes +{ + LDBLE grad, D, z, c, zc, Dz, Dzc; + LDBLE b_ij; // harmonic mean of cell properties, with EDL enrichment +}; +struct CT /* summed parts of V_M and mcd transfer in a timestep for all cells, for free + DL water */ +{ + LDBLE kgw, dl_s, Dz2c, Dz2c_stag, visc1, visc2, J_ij_sum; + LDBLE A_ij_il, Dz2c_il, mixf_il; + int J_ij_count_spec, J_ij_il_count_spec; + struct V_M *v_m, *v_m_il; + class J_ij *J_ij, *J_ij_il; + int count_m_s; + class M_S *m_s; + int v_m_size, J_ij_size, m_s_size; +} *ct = NULL; +std::map > cell_J_ij; +struct MOLES_ADDED /* total moles added to balance negative conc's */ +{ + char *name; + LDBLE moles; +} *moles_added; +int count_moles_added; + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +transport(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, n; + int j_imm; + LDBLE b, f, mix_f_m, mix_f_imm; + LDBLE water_m, water_imm; + int first_c, last_c, b_c; + int max_iter; + char token[MAX_LENGTH]; + LDBLE kin_time, stagkin_time, kin_time_save; + + int punch_boolean = 0; + LDBLE step_fraction; + + state = TRANSPORT; + diffc_tr = diffc; + diffc_max = 0.0; + transp_surf = warn_fixed_Surf = warn_MCD_X = 0; + dV_dcell = current_A = 0.0; + current_cells = NULL; + + /* mass_water_switch = TRUE; */ + /* + * Check existence of solutions + */ + j = -1; + /* check column solutions */ + for (i = 1; i <= count_cells; i++) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "Solution %d is needed for transport, but is not defined.", i); + error_msg(error_string, CONTINUE); + } + else + cell_data[i].temp = use.Get_solution_ptr()->Get_tc(); + } + + if (multi_Dflag) + { + sol_D = (class sol_D *) PHRQ_malloc((size_t) (all_cells)* sizeof(class sol_D)); + if (sol_D == NULL) + malloc_error(); + for (i = 0; i < all_cells; i++) + { + sol_D[i].count_spec = 0; + sol_D[i].count_exch_spec = 0; + sol_D[i].exch_total = 0; + sol_D[i].x_max = 0; + sol_D[i].viscos_f = 1.0; + sol_D[i].tk_x = 298.15; + sol_D[i].spec = NULL; + sol_D[i].spec_size = 0; + } + //sol_D_dbg = sol_D; + + ct = (struct CT *) PHRQ_malloc((size_t) (all_cells)* sizeof(struct CT)); + if (ct == NULL) + malloc_error(); + for (i = 0; i < all_cells; i++) + { + ct[i].kgw = 1.0; + ct[i].dl_s = 0.0; + ct[i].Dz2c = ct[i].Dz2c_stag = 0.0; + ct[i].visc1 = ct[i].visc2 = 0.0; + ct[i].J_ij_sum = 0.0; + ct[i].A_ij_il = 0.0; + ct[i].Dz2c_il = 0.0; + ct[i].mixf_il = 0.0; + ct[i].J_ij_count_spec = 0; + ct[i].J_ij_il_count_spec = 0; + ct[i].v_m = NULL; + ct[i].v_m_il = NULL; + ct[i].J_ij = NULL; + ct[i].J_ij_il = NULL; + ct[i].m_s = NULL; + ct[i].v_m_size = ct[i].J_ij_size = ct[i].m_s_size = 0; + } + count_moles_added = (int)elements.size(); + moles_added = (struct MOLES_ADDED *) PHRQ_malloc((size_t) (count_moles_added)* sizeof(struct MOLES_ADDED)); + if (moles_added == NULL) + malloc_error(); + + for (i = 0; i < count_moles_added; i++) + { + moles_added[i].name = NULL; + moles_added[i].moles = 0; + } + } + try + { + /* check solution 0 */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, 0)); + if (!use.Get_solution_ptr()) + { + if (ishift == 1) + { + input_error++; + error_string = sformatf( + "Solution 0 is needed for transport, but is not defined."); + error_msg(error_string, CONTINUE); + } + else + Utilities::Rxn_copy(Rxn_solution_map, 1, 0); + } + else + { + if ((cell_data[0].potV = use.Get_solution_ptr()->Get_potV())) + dV_dcell = 1; + } + + /* check solution count_cells */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1)); + if (!use.Get_solution_ptr()) + { + if (ishift == -1) + { + input_error++; + error_string = sformatf( + "Solution %d is needed for transport, but is not defined.", + count_cells + 1); + error_msg(error_string, CONTINUE); + } + else + Utilities::Rxn_copy(Rxn_solution_map, count_cells, count_cells + 1); + } + else + { + if ((cell_data[count_cells + 1].potV = use.Get_solution_ptr()->Get_potV())) + dV_dcell = 1; + } + /* + * Initialize temperature in stagnant cells ... + */ + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, k)); + if (use.Get_solution_ptr() != NULL) + cell_data[k].temp = use.Get_solution_ptr()->Get_tc(); + if (n == 1 && implicit && use.Get_solution_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "Stagnant solution %d not found for implicit diffusion with 1 stagnant layer.\n Please define it, or set -implicit false.", k); + error_msg(error_string, CONTINUE); + } + } + } + + if (fix_current && !dV_dcell && !implicit) + { + warning_msg("fix_current (A) was defined, but potential in a boundary cell was not.\n\tUsing 1e-8 V in cell 0 for calculating the potentials."); + cell_data[0].potV = 1e-8; + dV_dcell = 1; + } + if (dV_dcell) + { + if (ishift) + { + input_error++; + error_string = sformatf( + "Electro-diffusion cannot be combined with advective transport."); + error_msg(error_string, CONTINUE); + } + if (!multi_Dflag) + { + input_error++; + error_string = sformatf( + "Electrical Field (potential) was defined, but needs -multi_D."); + error_msg(error_string, CONTINUE); + } + else + { + if (bcon_first != 1) + { + bcon_first = 1; + error_string = sformatf( + "Electrical Field (potential) was defined, assuming constant boundary condition for first cell."); + warning_msg(error_string); + } + if (bcon_last != 1) + { + bcon_last = 1; + error_string = sformatf( + "Electrical Field (potential) was defined, assuming constant boundary condition for last cell."); + warning_msg(error_string); + } + current_cells = (struct CURRENT_CELLS *) PHRQ_malloc( + (count_cells + 1) * sizeof(struct CURRENT_CELLS)); + if (current_cells == NULL) + malloc_error(); + for (int i = 0; i < count_cells + 1; i++) + { + current_cells[i].dif = 0.0; + current_cells[i].ele = 0.0; + current_cells[i].R = 0.0; + } + } + } + if (implicit && interlayer_Dflag) + { + input_error++; + error_string = sformatf( + "Interlayer diffusion cannot be calculated implicitly. Please remove -implicit true."); + error_msg(error_string, CONTINUE); + } + if (implicit && !multi_Dflag) + { + input_error++; + error_string = sformatf( + "Implicit diffusion needs diffusion coefficients for individual species. Please add -multi_d true."); + error_msg(error_string, CONTINUE); + } + if (implicit && !timest) + { + error_string = sformatf( + "Time step not defined for diffusion, using -time_step 1 # seconds"); + warning_msg(error_string); + timest = 1; + } + //if (implicit && stag_data.count_stag > 1) + //{ + // error_string = sformatf( + // "Sorry, implicit diffusion can handle only 1 stagnant layer for now. Please remove -implicit true, or set -stagnant 1."); + // //error_msg(error_string, CONTINUE); + //} + if (implicit && current_cells == NULL) + { + current_cells = (struct CURRENT_CELLS *) PHRQ_malloc( + (count_cells + 1) * sizeof(struct CURRENT_CELLS)); + if (current_cells == NULL) + malloc_error(); + for (int i = 0; i < count_cells + 1; i++) + { + current_cells[i].dif = 0.0; + current_cells[i].ele = 0.0; + current_cells[i].R = 0.0; + } + } + + /* + * First equilibrate solutions + */ + dup_print("Equilibrating initial solutions", TRUE); + transport_step = 0; + for (i = 0; i <= count_cells + 1; i++) + { + if (!implicit && ((bcon_first == 2 && i == 0) || + (bcon_last == 2 && i == count_cells + 1))) + continue; + set_initial_moles(i); + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_transport()) + transp_surf = TRUE; + if (transp_surf && !multi_Dflag) + { + error_string = sformatf( + "-multi_d must be defined for surface transport"); + error_msg(error_string, CONTINUE); + } + if (multi_Dflag == TRUE) + { + fill_spec(cell_no, 0); + } + print_punch(i, true); + + /* if (i > 0 && i <= count_cells)*/ + saver(); + } + /* + * Also stagnant cells + */ + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + cell_no = k; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + set_initial_moles(k); + set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0); + if (multi_Dflag == TRUE) + { + fill_spec(cell_no, i); + } + print_punch(k, true); + saver(); + } + } + } + /* + * Initialize mixing factors, define kinetics times + * for multicomponent diffusion, limit mixing by diffc_max (usually from H+) + */ + if (multi_Dflag) + diffc_tr = diffc_max; + if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1)) + { + /* multi_D calc's are OK if all cells have the same amount of water */ + if (multi_Dflag == TRUE) + { + sprintf(token, "multi_D calc's and stagnant: define MIXing factors explicitly, or \n\t give in -multi_D the Dw used for calculating the mobile-immobile exchange factor."); + warning_msg(token); + } + + Rxn_mix_map.clear(); + } + /* + * mix[] is extended in init_mix(), to accommodate column mix factors + */ + nmix = init_mix(); + heat_nmix = init_heat_mix(nmix); + if (nmix < 2) + stagkin_time = timest; + else + stagkin_time = timest / (double)nmix; + if (ishift != 0) + kin_time = timest / (1 + (double)nmix); + else + kin_time = stagkin_time; + kin_time_save = kin_time; + + /* Reaction defined for a shift... */ + if (!ishift) + { + if (nmix < 2) + step_fraction = 1.0; + else + step_fraction = 1.0 / nmix; + } + else + step_fraction = 1.0 / (1.0 + nmix); + /* + * Set boundary conditions, transport direction + */ + last_model.force_prep = true; + if ((ishift == 0) || (bcon_first == 1) || (bcon_last == 1)) + b_c = 1; + else + b_c = 0; + if (ishift >= 0) + { + last_c = count_cells; + first_c = 1; + } + else + { + last_c = 1; + first_c = count_cells; + } + /* + * Define stagnant/mobile mix structure, if not read explicitly. + * + * With count_stag = 1, mix factors are calculated from exchange factor alpha + * (= exch_f), mobile th_m and immobile th_im porosity. + * These variables are read under keyword TRANSPORT, after stagnant, in + * structure stag_data. + * MIX 'cell_no' in input file can be an alternative for the calculation here. + */ + if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1)) + { + b = stag_data.th_m / (stag_data.th_m + stag_data.th_im); + f = exp(-stag_data.exch_f * stagkin_time / (b * stag_data.th_im)); + mix_f_imm = b - b * f; + mix_f_m = mix_f_imm * stag_data.th_im / stag_data.th_m; + for (j = 1; j <= count_cells; j++) + { + j_imm = j + (1 + count_cells); + if (Utilities::Rxn_find(Rxn_solution_map, j) == NULL) + error_msg + ("Could not find mobile cell solution in TRANSPORT.", + STOP); + if (Utilities::Rxn_find(Rxn_solution_map, j_imm) == NULL) + //error_msg + //("Could not find immobile cell solution in TRANSPORT.", + //STOP); + continue; + water_m = Utilities::Rxn_find(Rxn_solution_map, j)->Get_mass_water(); + water_imm = Utilities::Rxn_find(Rxn_solution_map, j_imm)->Get_mass_water(); + /* + * Define C_m = (1 - mix_f_m) * C_m0 + mix_f_m) * C_im0 + */ + { + cxxMix temp_mix; + temp_mix.Set_n_user(j); + temp_mix.Set_n_user_end(j); + temp_mix.Add(j, 1 - mix_f_m); + temp_mix.Add(j_imm, mix_f_m * water_m / water_imm); + Rxn_mix_map[j] = temp_mix; + } + /* + * Define C_im = mix_f_imm * C_m0 + (1 - mix_f_imm) * C_im0, or... + */ + { + cxxMix temp_mix; + temp_mix.Set_n_user(j_imm); + temp_mix.Set_n_user_end(j_imm); + temp_mix.Add(j_imm, 1 - mix_f_imm); + temp_mix.Add(j, mix_f_imm * water_imm / water_m); + Rxn_mix_map[j_imm] = temp_mix; + } + } + + if (heat_nmix > 0) + { + /* + * Assumption: D_e used for calculating exch_f in input file equals diffc + */ + f = stag_data.exch_f * (heat_diffc - diffc) / diffc / tempr; + f = exp(-f * stagkin_time / (b * stag_data.th_im)); + heat_mix_f_imm = b - b * f; + heat_mix_f_m = + heat_mix_f_imm * stag_data.th_im / stag_data.th_m; + } + } + /* + * Stop if error + */ + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } + /* + * Now transport + */ + if (implicit) + sprintf(token, "\nCalculating implicit transport: %d (mobile) cells, %d shifts, %d mixruns, max. mixf = %g.\n\n", + count_cells, count_shifts - transport_start + 1, nmix, max_mixf); + else + sprintf(token, "\nCalculating transport: %d (mobile) cells, %d shifts, %d mixruns...\n\n", + count_cells, count_shifts - transport_start + 1, nmix); + warning_msg(token); + max_iter = 0; + for (transport_step = transport_start; transport_step <= count_shifts; + transport_step++) + { + /* + * Set initial moles of phases + */ + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && !implicit && (i == 0 || i == count_cells + 1)) + continue; + set_initial_moles(i); + } + /* + * Also stagnant cells + */ + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + cell_no = k; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + set_initial_moles(k); + } + } + /* + * Start diffusing if boundary cond = 1, (fixed c, or closed) + */ + if (b_c == 1) + { + /* For half of mixing steps */ + for (j = 1; j <= floor((LDBLE)nmix / 2); j++) + { + rate_sim_time_start = ((double)transport_step - 1) * + timest + ((double)j - 1) * kin_time; + rate_sim_time = rate_sim_time_start + kin_time; + + mixrun = j; + if (multi_Dflag && j == floor((LDBLE)nmix / 2)) + { + //sprintf(token, + // "Transport step %3d. Multicomponent diffusion run %3d.", + // transport_step, j); + //dup_print(token, FALSE); + } + else if (!multi_Dflag) + { + sprintf(token, "Transport step %3d. Mixrun %3d.", + transport_step, j); + dup_print(token, FALSE); + } + + if (heat_nmix > 0 && !implicit) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i, i - 1); + saver(); + } + } + /* Go through cells */ + if (transp_surf) + { + if (disp_surf(stagkin_time) == ERROR) + error_msg("Error in surface transport, stopping.", + STOP); + } + if (implicit) + diffuse_implicit(stagkin_time, stag_data.count_stag); + else if (multi_Dflag) + multi_D(stagkin_time, 1, FALSE); + + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && (i == 0 || i == count_cells + 1) && !implicit) + { + if (j == nmix && stag_data.count_stag == 0 && + (cell_data[0].print || cell_data[0].punch || + cell_data[count_cells + 1].print || cell_data[count_cells + 1].punch)) + print_punch(i, false); + continue; + } + if (overall_iterations > max_iter) + max_iter = overall_iterations; + cell_no = i; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + status(0, token); + + if (i == 0 || i == count_cells + 1) + { + if (dV_dcell) + run_reactions(i, kin_time, MIX_BS, step_fraction); // nsaver = i + else + run_reactions(i, kin_time, NOMIX, step_fraction); // nsaver = i + } + else + { + run_reactions(i, kin_time, DISP, step_fraction); // n_saver = -2 + } + if (multi_Dflag) + fill_spec(i, 0); + + /* punch and output file */ + if (ishift == 0 && j == nmix && (stag_data.count_stag == 0 || (implicit && stag_data.count_stag == 1))) + print_punch(i, true); + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + saver(); + } + + if (!dV_dcell) + Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells); + /* Stagnant zone mixing after completion of each + diffusive/dispersive step ... */ + rate_sim_time_start = ((double)transport_step - 1) * + timest + ((double)j - 1) * stagkin_time; + rate_sim_time = rate_sim_time_start + stagkin_time; + + if (stag_data.count_stag > 0) + { + if (ishift == 0 && j == nmix) + punch_boolean = TRUE; + else + punch_boolean = FALSE; + for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells + { + mix_stag(i, stagkin_time, punch_boolean, step_fraction); + } + } + if (ishift == 0 && j == nmix && stag_data.count_stag > 0) + { + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + print_punch(k, false); + } + } + } + } + } + /* + * Advective transport + */ + if (ishift != 0) + { + sprintf(token, "Transport step %3d.", transport_step); + dup_print(token, FALSE); + if (b_c == 1) + rate_sim_time_start = ((double)transport_step - 1) * + timest + ((double)j - 1) * kin_time; + else + rate_sim_time_start = ((double)transport_step - 1) * timest; + rate_sim_time = rate_sim_time_start + kin_time; + + /* halftime kinetics for resident water in first cell ... */ + if (Utilities::Rxn_find(Rxn_kinetics_map, first_c) != NULL && count_cells > 1) + { + cell_no = first_c; + kin_time = kin_time_save / 2; + run_reactions(first_c, kin_time, NOMIX, 0.0); + saver(); + kin_time = kin_time_save; + } + + /* for each cell in column */ + for (i = last_c; i != (first_c - ishift); i -= ishift) + Utilities::Rxn_copy(Rxn_solution_map, i - ishift, i); + + /* if boundary_solutions must be flushed by the flux from the column... + if (ishift == 1 && bcon_last == 3) + solution_duplicate (last_c, last_c + 1); + else if (ishift == -1 && bcon_first == 3) + solution_duplicate (last_c, last_c - 1); + */ + if (transp_surf) + { + for (i = last_c + ishift; i != (first_c - ishift); + i -= ishift) + { + if ((ishift == 1 && i == last_c + 1 && bcon_last != 3) || + (ishift == -1 && i == last_c - 1 && bcon_first != 3)) + continue; + cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, i - ishift); + if (surface_ptr == NULL) + { + if ((Utilities::Rxn_find(Rxn_surface_map, i) != NULL) && + ((i == 0 && bcon_first == 3) + || (i == count_cells + 1 && bcon_last == 3))) + { + Rxn_surface_map.erase(i); + } + continue; + } + if (surface_ptr->Get_transport()) + { + cxxSurface * surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + if (surface_ptr1 == NULL) + { + cxxSurface surf; + surf.Set_n_user(i); + surf.Set_n_user_end(i); + Rxn_surface_map[i] = surf; + } + if (i == first_c) + { + Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, false); + } + else + { + Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, true); + } + } + } + } + + /* + * thermal diffusion when nmix = 0... + */ + if (nmix == 0 && heat_nmix > 0 && !implicit) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i, i - 1); + saver(); + } + } + + for (i = 1; i <= count_cells; i++) + { + if (i == first_c && count_cells > 1) + kin_time /= 2; + cell_no = i; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, 0, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, 0, i, max_iter); + status(0, token); + run_reactions(i, kin_time, NOMIX, step_fraction); + if (multi_Dflag == TRUE) + fill_spec(i, i - 1); + if (overall_iterations > max_iter) + max_iter = overall_iterations; + if (nmix == 0 && stag_data.count_stag == 0) + print_punch(i, true); + if (i == first_c && count_cells > 1) + kin_time = kin_time_save; + saver(); + + /* If nmix is zero, stagnant zone mixing after advective step ... */ + if ((nmix == 0) && (stag_data.count_stag > 0)) + { + mix_stag(i, stagkin_time, TRUE, step_fraction); + } + } + if (nmix == 0 && stag_data.count_stag > 0) + { + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + print_punch(k, false); + } + } + } + } + /* + * Further dispersive and diffusive transport + */ + if (b_c != 1) + j = 1; + for (; j <= nmix; j++) // loop on j + { + mixrun = j; + if (multi_Dflag && j == nmix && (transport_step % print_modulus == 0)) + { + sprintf(token, + "Transport step %3d. Multicomponent diffusion run %3d.", + transport_step, j); + dup_print(token, FALSE); + } + else if (!multi_Dflag) + { + sprintf(token, "Transport step %3d. Mixrun %3d.", + transport_step, j); + dup_print(token, FALSE); + } + rate_sim_time_start = ((double)transport_step - 1) * + timest + ((double)j - 1) * kin_time; + if (ishift != 0) + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start + kin_time; + + if (heat_nmix > 0 && !implicit) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i, i - 1); + saver(); + } + } + if (transp_surf) + { + if (disp_surf(stagkin_time) == ERROR) + error_msg("Error in surface transport, stopping.", STOP); + } + if (implicit) + diffuse_implicit(stagkin_time, stag_data.count_stag); + else if (multi_Dflag) + multi_D(stagkin_time, 1, FALSE); + + /* for each cell in column */ + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && (i == 0 || i == count_cells + 1) && !implicit) + { + if (j == nmix && stag_data.count_stag == 0 && + (cell_data[0].print || cell_data[0].punch || + cell_data[count_cells + 1].print || cell_data[count_cells + 1].punch)) + print_punch(i, false); + continue; + } + if (overall_iterations > max_iter) + max_iter = overall_iterations; + cell_no = i; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + status(0, token); + + if (i == 0 || i == count_cells + 1) + { + if (dV_dcell) + run_reactions(i, kin_time, MIX_BS, step_fraction); // nsaver = i + else + run_reactions(i, kin_time, NOMIX, step_fraction); // nsaver = i + } + else + { + run_reactions(i, kin_time, DISP, step_fraction); + } + if (multi_Dflag == TRUE) + fill_spec(i, 0); + if (j == nmix && (stag_data.count_stag == 0 || (implicit && stag_data.count_stag == 1))) + print_punch(i, true); + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + saver(); + } + if (!dV_dcell) + Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells); + /* Stagnant zone mixing after completion of each diffusive/dispersive step ... */ + rate_sim_time_start = ((double)transport_step - 1) * + timest + ((double)j - 1) * stagkin_time; + rate_sim_time = rate_sim_time_start + stagkin_time; + + if (stag_data.count_stag > 0) + { + if (j == nmix) + punch_boolean = TRUE; + else + punch_boolean = FALSE; + for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells + { + mix_stag(i, stagkin_time, punch_boolean, step_fraction); + } + } + if (j == nmix && ((stag_data.count_stag > 0/* && !implicit) || (implicit && stag_data.count_stag == 1*/))) + { + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + cell_no = k; + print_punch(k, false); + } + } + } + } + if (dump_modulus != 0 && (transport_step % dump_modulus) == 0) + dump(); + } + screen_msg("\n"); + + if (implicit && neg_moles.size()) + { + std::map > ::iterator it1; + std::map ::iterator it2; + for (i = 0; i <= all_cells; i++) + { + if ((it1 = neg_moles.find(i)) != neg_moles.end() && (els = it1->second).size()) + { + for (it2 = els.begin(); it2 != els.end(); it2++) + { + for (int i1 = 0; i1 < count_moles_added; i1++) + { + if (moles_added[i1].name && !strcmp(moles_added[i1].name, it2->first.c_str())) + { + moles_added[i1].moles -= it2->second; + break; + } + else if (!moles_added[i1].moles) + { + moles_added[i1].name = string_duplicate(it2->first.c_str()); + moles_added[i1].moles -= it2->second; + break; + } + } + } + } + } + } + + if (multi_Dflag && moles_added[0].moles > 0) + { + sprintf(token, + "\nFor balancing negative concentrations in MCD, added in total to the system:"); + warning_msg(token); + for (i = 0; i < count_moles_added; i++) + { + if (!moles_added[i].moles) + break; + sprintf(token, + "\t %.4e moles %s.", + (double)moles_added[i].moles, moles_added[i].name); + warning_msg(token); + } + } + } + catch (...) + { + transport_cleanup(); + throw; + } + transport_cleanup(); + initial_total_time += rate_sim_time; + rate_sim_time = 0; + mass_water_switch = FALSE; + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +transport_cleanup(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + /* + * free mix structures + */ + Dispersion_mix_map.clear(); + if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1)) + { + Rxn_mix_map.clear(); + } + + if (heat_nmix > 0) + { + heat_mix_array = (LDBLE *)free_check_null(heat_mix_array); + temp1 = (LDBLE *)free_check_null(temp1); + temp2 = (LDBLE *)free_check_null(temp2); + } + if (multi_Dflag) + { + for (i = 0; i < all_cells; i++) + { + sol_D[i].spec = (class spec *) free_check_null(sol_D[i].spec); + } + sol_D = (class sol_D *) free_check_null(sol_D); + for (int i = 0; i < all_cells; i++) + { + ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m); + ct[i].v_m_il = (struct V_M *) free_check_null(ct[i].v_m_il); + ct[i].J_ij = (class J_ij *) free_check_null(ct[i].J_ij); + ct[i].J_ij_il = (class J_ij *) free_check_null(ct[i].J_ij_il); + ct[i].m_s = (class M_S *) free_check_null(ct[i].m_s); + } + ct = (struct CT *) free_check_null(ct); + for (int i = 0; i < count_moles_added; i++) + { + moles_added[i].name = (char *)free_check_null(moles_added[i].name); + } + moles_added = (struct MOLES_ADDED *) free_check_null(moles_added); + } + if (implicit) + { + int l_stag = (stag_data.count_stag < 2 ? stag_data.count_stag : 0); + Ct2 = (LDBLE *)free_check_null(Ct2); + l_tk_x2 = (LDBLE *)free_check_null(l_tk_x2); + if (A) + { + for (i = 0; i < count_cells + 2 + l_stag * count_cells; i++) + { + A[i] = (LDBLE *)free_check_null(A[i]); + LU[i] = (LDBLE *)free_check_null(LU[i]); + } + } + if (mixf) + { + for (i = 0; i < count_cells + 2; i++) + { + mixf[i] = (LDBLE *)free_check_null(mixf[i]); + if (l_stag) + mixf_stag[i] = (LDBLE *)free_check_null(mixf_stag[i]); + if (!dV_dcell && !fix_current) + { + cell_data[i].potV = 0.0; + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + use.Get_solution_ptr()->Set_potV(0); + } + } + } + A = (LDBLE **)free_check_null(A); + LU = (LDBLE **)free_check_null(LU); + mixf = (LDBLE **)free_check_null(mixf); + mixf_stag = (LDBLE **)free_check_null(mixf_stag); + dif_spec_names.clear(); + mixf_comp_size = 0; + } + current_cells = (struct CURRENT_CELLS *) free_check_null(current_cells); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_punch(int i, boolean active) +/* ---------------------------------------------------------------------- */ +{ + if ((!(cell_data[i].punch && (transport_step % punch_modulus == 0)) && + !(cell_data[i].print && (transport_step % print_modulus == 0))) || + (bcon_first == 2 && i == 0) || + (bcon_last == 2 && i == count_cells + 1)) + return; + if (!active) + run_reactions(i, 0, NOMIX, 0); + cell_no = i; + if (dV_dcell || fix_current) + { + use.Set_n_solution_user(i); + use.Get_solution_ptr()->Set_potV(cell_data[i].potV); + potV_x = cell_data[i].potV; + } + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + } + if (cell_data[i].punch && (transport_step % punch_modulus == 0)) + punch_all(); + if (cell_data[i].print && (transport_step % print_modulus == 0)) + print_all(); + + /* maybe sorb a surface component... */ + if (change_surf_count > 0) + { + for (int k = 0; k < change_surf_count; k++) + { + if (change_surf[k].cell_no != i) + break; + reformat_surf(change_surf[k].comp_name, + change_surf[k].fraction, + change_surf[k].new_comp_name, + change_surf[k].new_Dw, + change_surf[k].cell_no); + change_surf[k].cell_no = -99; + } + change_surf_count = 0; + save.n_surface_user = save.n_solution_user; + save.n_surface_user_end = save.n_solution_user_end; + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +init_mix(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lav, dav = 0.0, mf12, maxmix, corr_disp, diffc_here, mD; + bool warning = false; + int i, l_nmix; + LDBLE *m, *m1; + m = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE)); + if (m == NULL) + malloc_error(); + m1 = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE)); + if (m1 == NULL) + malloc_error(); + for (i = 0; i < count_cells + 1; i++) + { + m[i] = 0; + m1[i] = 0; + } + corr_disp = 1.; + if (correct_disp == TRUE && ishift != 0) + { + if (bcon_first == 3) + corr_disp += 1. / count_cells; + if (bcon_last == 3) + corr_disp += 1. / count_cells; + } + maxmix = 0.0; + if (multi_Dflag) + { + if (dV_dcell) + dV_dcell = (cell_data[count_cells + 1].potV - cell_data[0].potV) / count_cells; + for (i = 1; i <= count_cells; i++) + { + // estimate the number of mixes + if (i < count_cells) + { + lav = (cell_data[i + 1].length + cell_data[i].length) / 2; + mD = diffc_max * timest / (lav * lav); + if (mD > maxmix) + maxmix = mD; + } + if (ishift != 0) + { + /* define dispersive mixing (diffusion is done in multi_D), + with harmonic mean of cell properties (manual eqn 49): + M1 = -a1 * (v1 * timest / 2) * A1 * (c12 - c1) / (h1 / 2) + a1 is dispersivity in cell 1, A1 = V1 / h1, v1 * timest / 2 = h1 / 2. + All cells have equal V in advective transport, delta(c) = M1 / V1. + */ + if (i < count_cells) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i + 1].disp) + dav += cell_data[i + 1].length / cell_data[i + 1].disp; + if (dav) + m1[i] = 2 * corr_disp / dav; /* m1[i] has mixf with higher cell */ + } + if (i > 1) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i - 1].disp) + dav += cell_data[i - 1].length / cell_data[i - 1].disp; + if (dav) + m[i] = 2 * corr_disp / dav; /* m[i] has mixf with lower cell */ + } + mf12 = m[i] + m1[i]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + mD = 2 * diffc_max * timest / (cell_data[1].length * cell_data[1].length); + if (mD > maxmix) + maxmix = mD; + if (ishift != 0) + { + m[1] = 2 * cell_data[1].disp / cell_data[1].length * corr_disp; + mf12 = m[1] + m1[1]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + if (bcon_last == 1) + { + mD = 2 * diffc_max * timest / (cell_data[count_cells].length * cell_data[count_cells].length); + if (mD > maxmix) + maxmix = mD; + if (ishift != 0) + { + m1[count_cells] = 2 * cell_data[count_cells].disp / cell_data[count_cells].length * corr_disp; + mf12 = m[count_cells] + m1[count_cells]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + /* + * Find number of mixes + */ + if (maxmix == 0) + { + l_nmix = 0; + if (mcd_substeps > 1 && stag_data.count_stag > 0) + l_nmix = (int) ceil(mcd_substeps); + } + else if (implicit) + { + l_nmix = 1; + if (maxmix > max_mixf) + l_nmix = 1 + (int)floor(maxmix / max_mixf); + if (ishift != 0 && (bcon_first == 1 || bcon_last == 1)) + { + if (l_nmix < 2) + l_nmix = 2; + } + if (mcd_substeps > 1) + l_nmix = (int)ceil(l_nmix * mcd_substeps); + } + else + { + if (2.25 * maxmix + 1.0 > (double)INT_MAX) + { + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + char token[MAX_LENGTH]; + sprintf(token, "Calculated number of mixes %g, is beyond program limit,\nERROR: please set implicit true, or decrease time_step, or increase cell-lengths.", 2.25 * maxmix); + error_msg(token, STOP); + } + if (bcon_first == 1 || bcon_last == 1) + l_nmix = 1 + (int)floor(2.25 * maxmix); + else + l_nmix = 1 + (int)floor(1.5 * maxmix); + + if (ishift != 0 && (bcon_first == 1 || bcon_last == 1)) + { + if (l_nmix < 2) + l_nmix = 2; + } + if (mcd_substeps > 1) + l_nmix = (int)ceil(l_nmix * mcd_substeps); + } + + for (i = 1; i <= count_cells; i++) + { + m[i] /= l_nmix; + m1[i] /= l_nmix; + /* + * Fill mix structure + */ + cxxMix temp_mix; + temp_mix.Set_n_user(i); + temp_mix.Set_n_user_end(i); + + temp_mix.Add(i - 1, m[i]); + temp_mix.Add(i + 1, m1[i]); + temp_mix.Add(i, 1.0 - m[i] - m1[i]); + Dispersion_mix_map[i] = temp_mix; + } + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + return l_nmix; + } + else // multi_D false + { + diffc_here = 2 * diffc_tr * timest; + /* + * Define mixing factors among inner cells + */ + for (i = 1; i <= count_cells; i++) + { + if (i < count_cells) + { + // find mix with higher numbered cell... + if (ishift != 0) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i + 1].disp) + dav += cell_data[i + 1].length / cell_data[i + 1].disp; + if (dav) + m1[i] = 2 / dav; + } + // add diffusive mixf... + m1[i] += diffc_here / + (cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i + 1].length); + m1[i] *= corr_disp; /* m1[i] has mixf with higher cell */ + } + if (i > 1) + { + // and with lower numbered cell... + if (ishift != 0) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i - 1].disp) + dav += cell_data[i - 1].length / cell_data[i - 1].disp; + if (dav) + m[i] = 2 / dav; + } + // add diffusive mixf... + m[i] += diffc_here / + (cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i - 1].length); + m[i] *= corr_disp; /* m[i] has mixf with lower numbered cell */ + if (m[i] != m1[i - 1] && !warning && (!dav || m[i] / (2 / dav) > 1.00001)) + { + warning_msg("Unequal cell-lengths may give mass-balance error, consider using -multi_D"); + warning = true; + } + } + mf12 = m[i] + m1[i]; + if (mf12 > maxmix) + maxmix = mf12; + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + m[1] = diffc_here / (cell_data[1].length * cell_data[1].length); + if (ishift != 0) + m[1] += cell_data[1].disp / cell_data[1].length; + mf12 = m[1] + m1[1]; + if (mf12 > maxmix) + maxmix = mf12; + } + if (bcon_last == 1) + { + m1[count_cells] = diffc_here / (cell_data[count_cells].length * cell_data[count_cells].length); + if (ishift != 0) + m1[count_cells] += cell_data[count_cells].disp / cell_data[count_cells].length; + mf12 = m[count_cells] + m1[count_cells]; + if (mf12 > maxmix) + maxmix = mf12; + } + /* + * Find number of mixes + */ + if (maxmix == 0) + l_nmix = 0; + else + { + if (1.5 * maxmix > (double)INT_MAX) + { + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + char token[MAX_LENGTH]; + sprintf(token, "Calculated number of mixes %g, is beyond program limit,\nERROR: please set implicit true, or decrease time_step, or increase cell-lengths.", 1.5 * maxmix); + error_msg(token, STOP); + } + l_nmix = 1 + (int) floor(1.5 * maxmix); + + if ((ishift != 0) && ((bcon_first == 1) || (bcon_last == 1))) + { + if (l_nmix < 2) + l_nmix = 2; + } + + for (i = 1; i <= count_cells; i++) + { + m[i] /= l_nmix; + m1[i] /= l_nmix; + /* + * Fill mix structure + */ + cxxMix temp_mix; + temp_mix.Set_n_user(i); + temp_mix.Set_n_user_end(i); + + temp_mix.Add(i - 1, m[i]); + temp_mix.Add(i + 1, m1[i]); + temp_mix.Add(i, 1.0 - m[i] - m1[i]); + Dispersion_mix_map[i] = temp_mix; + } + } + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + return (l_nmix); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mix_stag(int i, LDBLE kin_time, int l_punch, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ + int n, k; + LDBLE t_imm; + cxxSolution *ptr_imm, *ptr_m; + k = -1000; // compiler says k may be undefined + ptr_imm = NULL; + boolean done_mixing = false; + /* + * Kinetics in transport cell is done while transporting + */ + for (n = 1; n <= stag_data.count_stag; n++) + { + if (i == 0 || i == count_cells + 1) + { + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr()) + { + for (std::map < int, LDBLE >::const_iterator it = use.Get_mix_ptr()->Get_mixComps().begin(); + it != use.Get_mix_ptr()->Get_mixComps().end(); it++) + { + if (it->first > i && it->first < all_cells && it->first != count_cells + 1) + { + k = it->first; + ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k); + break; + } + } + } + } + else + { + k = i + 1 + n * count_cells; + if (k < all_cells) + ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k); + } + if (ptr_imm != NULL) + { + if (n == 1) + { + if (heat_nmix > 0 && (!implicit || (implicit && stag_data.count_stag > 1))) + { + ptr_m = Utilities::Rxn_find(Rxn_solution_map, i); + t_imm = + heat_mix_f_imm * ptr_m->Get_tc() + (1 - heat_mix_f_imm) * ptr_imm->Get_tc(); + ptr_m->Set_tc(heat_mix_f_m * ptr_imm->Get_tc() + (1 - heat_mix_f_m) * ptr_m->Get_tc()); + cell_data[i].temp = ptr_m->Get_tc(); + cell_data[k].temp = t_imm = ptr_imm->Get_tc(); + /* equilibrate again ... */ + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no, 0); + saver(); + cell_no = k; + set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no, i); + saver(); + } + /* + * Mobile cell, kinetics already done ... + */ + cell_no = i; + if (transp_surf) + { + if (diff_stag_surf(i) == ERROR) + error_msg("Error in surface transport, stopping.", + STOP); + } + if (!implicit || (implicit && stag_data.count_stag > 1)) + { + if (multi_Dflag == TRUE) + multi_D(1.0, i, 2); + set_and_run_wrapper(i, STAG, FALSE, -2, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no, 0); + if (l_punch) + print_punch(i, true); + saver(); // save solution i in -2, original can be used in other stagnant mixes + } + } + + cell_no = k; + if (implicit) + run_reactions(k, kin_time, NOMIX, step_fraction); + else + run_reactions(k, kin_time, STAG, step_fraction); + if (multi_Dflag == TRUE) + fill_spec(cell_no, i); + saver(); // save solution k in -2 - k, original k can be used in other stagnant mixes + + done_mixing = true; + } + else if (n == 1 && l_punch && !implicit) + print_punch(i, false); + } + + if (done_mixing) // after all mixing is done, the temporal solution becomes the original for the next timestep + { + for (n = 1; n <= stag_data.count_stag; n++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + Utilities::Rxn_copy(Rxn_solution_map, -2 - k, k); + if (n == 1 && !implicit) + Utilities::Rxn_copy(Rxn_solution_map, -2, i); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +init_heat_mix(int l_nmix) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lav, mixf, maxmix, corr_disp, l_diffc; + int i, k, n; + int l_heat_nmix; + LDBLE t0; + /* + * Check for need to model thermal diffusion... + */ + if (heat_diffc <= diffc && !implicit) + return (0); + if (count_cells < 2) + return (0); + + l_heat_nmix = 0; + if (implicit) + l_diffc = heat_diffc; + else + l_diffc = heat_diffc - diffc_tr; + t0 = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc(); + for (i = 1; i <= count_cells; i++) + { + if (fabs(cell_data[i].temp - t0) > 1.0) + { + l_heat_nmix = 1; + break; + } + } + if (l_heat_nmix == 0) + { + if (fabs(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1)->Get_tc() - t0) > 1.0) + l_heat_nmix = 1; + for (n = 1; n <= stag_data.count_stag; n++) + { + for (i = 1; i < count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + if (fabs(cell_data[k].temp - t0) > 1.0) + { + l_heat_nmix = 1; + break; + } + } + } + } + } + if (l_heat_nmix == 0) + return (0); + /* + * Initialize arrays... + */ + heat_mix_array = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (heat_mix_array == NULL) + malloc_error(); + + temp1 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (temp1 == NULL) + malloc_error(); + + temp2 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (temp2 == NULL) + malloc_error(); + /* + * Define mixing factors among inner cells... + */ + corr_disp = 1.; + if (correct_disp == TRUE && ishift != 0) + { + mixf = (l_nmix > 1 ? l_nmix : 1); + if (bcon_first == 3) + corr_disp += 1. / count_cells / mixf; + if (bcon_last == 3) + corr_disp += 1. / count_cells / mixf; + } + maxmix = 0.0; + for (i = 1; i < count_cells; i++) + { + lav = (cell_data[i + 1].length + cell_data[i].length) / 2; + mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav); + if (mixf > maxmix) + maxmix = mixf; + heat_mix_array[i + 1] = mixf; /* m[i] has mixf with lower cell */ + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + lav = cell_data[1].length; + mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav); + if (2 * mixf > maxmix) + maxmix = 2 * mixf; + heat_mix_array[1] = 2 * mixf; + } + else + heat_mix_array[1] = 0; + + if (bcon_last == 1) + { + lav = cell_data[count_cells].length; + mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav); + if (2 * mixf > maxmix) + maxmix = 2 * mixf; + heat_mix_array[count_cells + 1] = 2 * mixf; + } + else + heat_mix_array[count_cells + 1] = 0; + /* + * Find number of mixes + */ + if (maxmix == 0) + l_heat_nmix = 0; + else + { + if (implicit) + { + LDBLE viscos_f; + l_heat_nmix = l_nmix; + for (i = 1; i <= count_cells + 1; i++) + { + heat_mix_array[i - 1] = heat_mix_array[i] / l_heat_nmix; /* for implicit, m[i] has mixf with higher cell */ + viscos_f = sol_D[i - 1].viscos_f * exp(heat_diffc / sol_D[i - 1].tk_x - heat_diffc / 298.15); + viscos_f += sol_D[i].viscos_f * exp(heat_diffc / sol_D[i].tk_x - heat_diffc / 298.15); + heat_mix_array[i - 1] *= (viscos_f / 2); + } + } + else + { + l_heat_nmix = 1 + (int)floor(3.0 * maxmix); + for (i = 1; i <= count_cells + 1; i++) + heat_mix_array[i] /= l_heat_nmix; + } + } + + return (l_heat_nmix); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +heat_mix(int l_heat_nmix) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + + for (i = 1; i <= count_cells; i++) + temp1[i] = Utilities::Rxn_find(Rxn_solution_map, i)->Get_tc(); + temp1[0] = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc(); + temp1[count_cells + 1] = + Utilities::Rxn_find(Rxn_solution_map, (count_cells + 1))->Get_tc(); + + for (i = 1; i <= l_heat_nmix; i++) + { + for (j = 1; j <= count_cells; j++) + temp2[j] = + heat_mix_array[j] * temp1[j - 1] + heat_mix_array[j + 1] * temp1[j + 1] + + (1 - heat_mix_array[j] - heat_mix_array[j + 1]) * temp1[j]; + for (j = 1; j <= count_cells; j++) + temp1[j] = temp2[j]; + } + + for (i = 1; i <= count_cells; i++) + { + cell_data[i].temp = temp1[i]; + Utilities::Rxn_find(Rxn_solution_map, i)->Set_tc(temp1[i]); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_initial_moles(int i) +/* ---------------------------------------------------------------------- */ +{ + cxxKinetics *kinetics_ptr; + char token[MAX_LENGTH]; + const char* cptr; + int j, k, l; + /* + * Pure phase assemblage + */ + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i); + if (pp_assemblage_ptr != NULL) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for (; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + it->second.Set_initial_moles(it->second.Get_moles()); + if (it->second.Get_initial_moles() < 0) + it->second.Set_initial_moles(0.0); + } + } + } + /* + * Gas phase + */ + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, i); + if (gas_phase_ptr != NULL) + { + std::vector gc = gas_phase_ptr->Get_gas_comps(); + for (size_t l = 0; l < gc.size(); l++) + { + gc[l].Set_initial_moles(gc[l].Get_moles()); + } + gas_phase_ptr->Set_gas_comps(gc); + } + } + /* + * Kinetics + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kinetics_ptr != NULL) + { + for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_initial_moles(kinetics_comp_ptr->Get_m()); + } + } + /* + * Solid solutions + */ + { + cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i); + if (ss_assemblage_ptr != NULL) + { + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (k = 0; k < (int) ss_ptrs.size(); k++) + { + cxxSS * ss_ptr = ss_ptrs[k]; + for (j = 0; j < (int) ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_init_moles(comp_ptr->Get_moles()); + } + } + } + } + /* + * For interlayer diffusion: add tiny bit of exchanger if absent + */ + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, i); + if (interlayer_Dflag && exchange_ptr == NULL) + { + cxxExchange temp_exchange; + temp_exchange.Set_n_user_both(i); + temp_exchange.Set_description("Interlayer diffusion: added 2e-10 moles X-"); + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + + temp_exchange.Set_new_def(true); + temp_exchange.Set_solution_equilibria(true); + temp_exchange.Set_n_solution(i); + + cxxExchComp comp; + count_elts = 0; + paren_count = 0; + strcpy(token, "X"); + cptr = token; + get_elts_in_species(&cptr, 2e-10); + cptr = token; + LDBLE z; + { + std::string token1; + get_token(&cptr, token1, &z, &l); + comp.Set_formula(token1.c_str()); + } + comp.Set_formula_z(z); + comp.Set_totals(elt_list_NameDouble()); + comp.Set_charge_balance(0.0); + temp_exchange.Get_exchange_comps().push_back(comp); + Rxn_exchange_map[i] = temp_exchange; + + state = INITIAL_EXCHANGE; + initial_exchangers(TRUE); + state = TRANSPORT; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_spec(int l_cell_no, int ref_cell) +/* ---------------------------------------------------------------------- */ +{ + /* copy species activities into sol_D.spec... */ + + int i, i1, i2, i3, count_spec, count_exch_spec, size_xt; + char token[MAX_LENGTH]; + const char * name; + class species *s_ptr, *s_ptr2; + class master *master_ptr; + LDBLE dum, dum2; + LDBLE lm; + LDBLE por, por_il, viscos_f, viscos_il_f, viscos; + bool x_max_done = false; + std::set loc_spec_names; + + s_ptr2 = NULL; + + // for implicit + std::set ::iterator it; + std::pair ::iterator, bool> name_ret; + if (implicit && !l_cell_no) + dif_spec_names.clear(); + size_xt = 5; + + //sol_D[l_cell_no].spec = (class spec *) free_check_null(sol_D[l_cell_no].spec); + if (sol_D[l_cell_no].spec == NULL) + { + sol_D[l_cell_no].spec = (class spec *) PHRQ_malloc((species_list.size() + (size_t)size_xt) * sizeof(class spec)); + sol_D[l_cell_no].spec_size = (int)species_list.size() + size_xt; + } + else if ((int)species_list.size() + size_xt > sol_D[l_cell_no].spec_size) + { + sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec, (species_list.size() + (size_t)size_xt) * sizeof(class spec)); + sol_D[l_cell_no].spec_size = (int)species_list.size() + size_xt; + } + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + + for (i = 0; i < sol_D[l_cell_no].spec_size; i++) + { + sol_D[l_cell_no].spec[i].name = NULL; + sol_D[l_cell_no].spec[i].aq_name = NULL; + sol_D[l_cell_no].spec[i].type = -1; + sol_D[l_cell_no].spec[i].a = 0.0; + sol_D[l_cell_no].spec[i].lm = min_dif_LM; + sol_D[l_cell_no].spec[i].lg = -0.04; + sol_D[l_cell_no].spec[i].c = 0.0; + sol_D[l_cell_no].spec[i].z = 0.0; + sol_D[l_cell_no].spec[i].Dwt = 0.0; + sol_D[l_cell_no].spec[i].dw_t = 0.0; + sol_D[l_cell_no].spec[i].erm_ddl = 0.0; + sol_D[l_cell_no].count_exch_spec = sol_D[l_cell_no].count_spec = 0; + } + + sol_D[l_cell_no].tk_x = tk_x; + + viscos_f = viscos_il_f = 1.0; + if (l_cell_no == 0) + { + por = cell_data[1].por; + por_il = cell_data[1].por_il; + } + else if (l_cell_no == count_cells + 1) + { + por = cell_data[count_cells].por; + por_il = cell_data[count_cells].por_il; + } + else + { + por = cell_data[l_cell_no].por; + por_il = cell_data[l_cell_no].por_il; + } + if (por < multi_Dpor_lim) + por = viscos_f = 0.0; + + if (por_il < interlayer_Dpor_lim) + por_il = viscos_il_f = 0.0; + /* + * correct diffusion coefficient for temperature and viscosity, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + * modify viscosity effect: Dw(TK) = Dw(298.15) * exp(dw_t / TK - dw_t / 298.15), SC data from Robinson and Stokes, 1959 + */ + viscos = viscos_0; + /* + * put temperature factor in por_factor which corrects for porous medium... + */ + viscos_f *= tk_x * viscos_0_25 / (298.15 * viscos); + viscos_il_f *= tk_x * viscos_0_25 / (298.15 * viscos); + sol_D[l_cell_no].viscos_f = tk_x * viscos_0_25 / (298.15 * viscos); + + count_spec = count_exch_spec = 0; + /* + * sort species by name... + */ + if (species_list.size() > 1) + { + qsort(&species_list[0], species_list.size(), + sizeof(class species_list), sort_species_name); + } + + for (i = 0; i < (int)species_list.size(); i++) + { + /* + * copy species data + */ + s_ptr = species_list[i].s; + if (s_ptr->type > HPLUS && + !(s_ptr->type == EX && interlayer_Dflag)) + continue; + //if (s_ptr->type == EX && !interlayer_Dflag) + // continue; + //if (s_ptr->type == SURF) + // continue; + if (i > 0 && strcmp(s_ptr->name, species_list[(size_t)i - 1].s->name) == 0) + continue; + //if (s_ptr == s_h2o) + // continue; + + if (s_ptr->type == EX) + { + if (s_ptr->lm > min_dif_LM) + { + /* find exchanger's name, use only master exchanger 'X' */ + if (species_list[i].master_s->secondary != NULL) + master_ptr = species_list[i].master_s->secondary; + else + master_ptr = species_list[i].master_s->primary; + if (s_ptr->equiv != 0.0) + dum = fabs(s_ptr->equiv) / master_ptr->total; + else + { + if (species_list[i].master_s->z == 0) + dum = 1 / master_ptr->total; + else + dum = 1; + } + name = master_ptr->elt->name; + if (strcmp(name, "X") != 0) + { + if (!warn_MCD_X) + { + sprintf(token, + "MCD found more than 1 exchanger, uses X for interlayer diffusion."); + warning_msg(token); + warn_MCD_X = 1; + } + continue; + } + dum2 = s_ptr->moles * dum; /* equivalent fraction */ + sol_D[l_cell_no].spec[count_spec].name = s_ptr->name; + //string_hsave(s_ptr->name); + sol_D[l_cell_no].spec[count_spec].type = EX; + sol_D[l_cell_no].spec[count_spec].c = dum2; + sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg - log10(dum); + sol_D[l_cell_no].spec[count_spec].a = dum2 * pow(10, sol_D[l_cell_no].spec[count_spec].lg); + sol_D[l_cell_no].exch_total = master_ptr->total; + if (transport_step == 0 && !x_max_done) + { + x_max_done = true; + dum = master_ptr->total / Utilities::Rxn_find(Rxn_solution_map, l_cell_no)->Get_mass_water(); + if (dum > sol_D[1].x_max) + sol_D[1].x_max = dum; + } + + /* find the aqueous species in the exchange reaction... */ + for (i2 = 0; (s_ptr->rxn.token[i2].s != NULL); i2++) + { + if ((s_ptr2 = s_ptr->rxn.token[i2].s)->type == AQ) + break; + } + /* copy its name and Dw and charge... */ + sol_D[l_cell_no].spec[count_spec].aq_name = s_ptr2->name; + //string_hsave(s_ptr2->name); + sol_D[l_cell_no].spec[count_spec].z = s_ptr2->z; + if (s_ptr2->dw == 0) + sol_D[l_cell_no].spec[count_spec].Dwt = default_Dw * viscos_il_f; + else + { + if (s_ptr2->dw_t) + { + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw * + exp(s_ptr2->dw_t / 298.15 - s_ptr2->dw_t / tk_x) * viscos_il_f; + sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr2->dw_t; + } + else + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw * viscos_il_f; + } + + //if (implicit) // && name_ret.second && (l_cell_no > 1 || (l_cell_no == 1 && bcon_first != 2))) + //{ + // // name_ret = dif_spec_names.insert(s_ptr->name); // but not in implicit now... + // // must fill in the spec in previous cells, order the names, see below for aqueous species... + //} + count_exch_spec++; + count_spec++; + } + continue; + } + + lm = s_ptr->lm; + + if (lm > min_dif_LM) + { + if (implicit/* && l_cell_no < count_cells + 2 */) + { + name_ret = dif_spec_names.insert(s_ptr->name); + loc_spec_names.insert(s_ptr->name); + i2 = 0; + for (it = dif_spec_names.begin(); it != dif_spec_names.end(); it++) + { + if (*it == s_ptr->name) + break; + i2++; + } + if (i2 > count_spec) + { + if (l_cell_no == 0) + { + for (i1 = i2; i1 > count_spec; i1--) + { + it = dif_spec_names.find(s_ptr->name); + dif_spec_names.erase(--it); + } + } + else + { + // there are species before s_ptr->name that must be included first + i1 = 0; + for (it = loc_spec_names.begin(); it != loc_spec_names.end(); it++) + { + if (*it == s_ptr->name) + break; + i1++; + } + i3 = i2 - i1; + if (i3 + count_spec + 1 > sol_D[l_cell_no].spec_size) + { + sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec, + ((size_t)i3 + count_spec + 1 + (size_t)size_xt) * sizeof(class spec)); + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + sol_D[l_cell_no].spec_size = i3 + count_spec + 1 + size_xt; + } + for (; i1 < i2; i1++) // i1 is loop variable + { + // memmove(&sol_D[l_cell_no].spec[i1], &sol_D[ref_cell].spec[i1], sizeof(class spec)); + sol_D[l_cell_no].spec[i1] = sol_D[ref_cell].spec[i1]; + sol_D[l_cell_no].spec[i1].c = 0.0; + sol_D[l_cell_no].spec[i1].a = 0.0; + sol_D[l_cell_no].spec[i1].lm = min_dif_LM; + sol_D[l_cell_no].spec[i1].lg = -0.04; + + loc_spec_names.insert(sol_D[l_cell_no].spec[i1].name); + count_spec++; + } + } + } + } + if (count_spec >= sol_D[l_cell_no].spec_size) + { + sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec, + (count_spec + (size_t)size_xt) * sizeof(class spec)); + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + sol_D[l_cell_no].spec_size = count_spec + size_xt; + } + sol_D[l_cell_no].spec[count_spec].name = s_ptr->name; + sol_D[l_cell_no].spec[count_spec].type = AQ; + sol_D[l_cell_no].spec[count_spec].c = s_ptr->moles / mass_water_aq_x; + sol_D[l_cell_no].spec[count_spec].a = under(lm + s_ptr->lg); + sol_D[l_cell_no].spec[count_spec].lm = lm; + sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg; + sol_D[l_cell_no].spec[count_spec].z = s_ptr->z; + if (s_ptr->dw == 0) + sol_D[l_cell_no].spec[count_spec].Dwt = default_Dw * viscos_f; + else + { + if (s_ptr->dw_t) + { + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw * + exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15) * viscos_f; + sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr->dw_t; + } + else + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw * viscos_f; + } + if (correct_Dw) + { + calc_SC(); // note that neutral species are corrected as if z = 1, but is viscosity-dependent + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw_corr * viscos_f; + } + if (l_cell_no <= count_cells + 1 && sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn) > diffc_max) + diffc_max = sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn); + sol_D[l_cell_no].spec[count_spec].erm_ddl = s_ptr->erm_ddl; + + if (implicit/* && l_cell_no < count_cells + 2 */) + { + if (name_ret.second && l_cell_no) + { + // must fill in the spec in previous cells, order the names... + i3 = 0; + for (it = dif_spec_names.begin(); it != dif_spec_names.end(); ++it) + { + if (it == name_ret.first) + break; + i3++; + } + for (i1 = 0; i1 < l_cell_no; i1++) + { + i2 = sol_D[i1].count_spec + 1; + if (i2 > sol_D[i1].spec_size) + { + sol_D[i1].spec = (class spec *) PHRQ_realloc(sol_D[i1].spec, + ((size_t)i2 + (size_t)size_xt) * sizeof(class spec)); + if (sol_D[i1].spec == NULL) + malloc_error(); + sol_D[i1].spec_size = i2 + size_xt; + } + i2--; + for (; i2 > i3; i2--) // i2 is loop variable + sol_D[i1].spec[i2] = sol_D[i1].spec[i2 - 1]; + + // memmove(&sol_D[i1].spec[i2], &sol_D[l_cell_no].spec[i2], sizeof(class spec)); + sol_D[i1].spec[i2] = sol_D[l_cell_no].spec[i2]; + sol_D[i1].spec[i2].a = 0.0; + sol_D[i1].spec[i2].lm = min_dif_LM; + sol_D[i1].spec[i2].lg = -0.04; + sol_D[i1].spec[i2].c = 0.0; + sol_D[i1].count_spec += 1; + } + } + } + count_spec++; + } + } + sol_D[l_cell_no].count_spec = count_spec; + sol_D[l_cell_no].count_exch_spec = count_exch_spec; + if (implicit/* && l_cell_no < count_cells + 2 */ && loc_spec_names.size() < dif_spec_names.size()) + { + i3 = (int) dif_spec_names.size(); + if (i3 > sol_D[l_cell_no].spec_size) + { + sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec, + ((size_t)i3 + (size_t)size_xt) * sizeof(class spec)); + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + sol_D[l_cell_no].spec_size = i3 + size_xt; + } + for (i1 = count_spec; i1 < i3; i1++) + { + // memmove(&sol_D[l_cell_no].spec[i1], &sol_D[ref_cell].spec[i1], sizeof(class spec)); + sol_D[l_cell_no].spec[i1] = sol_D[ref_cell].spec[i1]; + sol_D[l_cell_no].spec[i1].c = 0.0; + sol_D[l_cell_no].spec[i1].a = 0.0; + sol_D[l_cell_no].spec[i1].lm = min_dif_LM; + sol_D[l_cell_no].spec[i1].lg = -0.04; + sol_D[l_cell_no].count_spec += 1; + + loc_spec_names.insert(sol_D[l_cell_no].spec[i1].name); + count_spec++; + } + //if (loc_spec_names != dif_spec_names) + //{ + // error_string = sformatf( + // "Species in implicit diffusion in cell %d are %; different from previous cells with %d species.", + // l_cell_no, loc_spec_names.size(), dif_spec_names.size()); + // error_msg(error_string, CONTINUE); + //} + } + if (implicit && l_cell_no/* && l_cell_no < count_cells + 2 */) + { + for (i = 0; i < count_spec; i++) + { + //name = sol_D[l_cell_no].spec[i].name; + if (strcmp(sol_D[l_cell_no].spec[i].name, sol_D[ref_cell].spec[i].name)) + { + error_string = sformatf( + "Implicit diffusion: species %s in cell %d differs from species %s in previous cells.", + sol_D[l_cell_no].spec[i].name, l_cell_no, sol_D[ref_cell].spec[i].name); + error_msg(error_string, CONTINUE); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sort_species_name(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const class species_list *nptr1, *nptr2; + + nptr1 = (const class species_list *) ptr1; + nptr2 = (const class species_list *) ptr2; + + return (strcmp(nptr1->s->name, nptr2->s->name)); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +diffuse_implicit(LDBLE DDt, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + // The structure comes from example 11.3 in A&P and Appelo 2017, CCR 101, 102: + // Ct2 is implicitly solved for the individual species, corrected for electro-neutrality, this always gives Ct2 > 0. + // Version 3.5.2 includes diffusion in 1 stagnant layer in the implicit calc'n. + // Transport of aqueous species is summarized into master species. + // With electro-migration, transport of anions and cations is calculated in opposite directions since (sign) J = - z * dV. + // Only available moles are transported, thus are > 0, but if concentrations oscillate, + // change max_mixf in input file: -implicit true 1 # max_mixf = 1 (default). + int i, icell, cp, comp; + // ifirst = (bcon_first == 2 ? 1 : 0); ilast = (bcon_last == 2 ? count_cells - 1 : count_cells); + int ifirst, ilast; + int i_1, i0, i1, i2 = 0; + //double mfr, mfr1, max_b = 0, b, grad, dVc, j_0e, min_dif_M = pow(10, min_dif_LM); + double mfr, mfr1, grad, dVc, j_0e, min_dif_M = pow(10, min_dif_LM); + LDBLE dum1, dum2, dum_stag = 0.0, min_mol; + + LDBLE dum = 0; + //cxxSurfaceCharge * charge_ptr = NULL; + if (stagnant > 1) + stagnant = 0; + cxxSolution *sptr1, *sptr2, *sptr_stag; + std::vector n_solution; + std::vector fraction; + std::map > ::iterator it1; + std::map ::iterator it2; + // c= count_cells, c1=(c+1)= end boundary-cell, c_1=(c-1), c2=(c+2)= first stagnant cell, cc1=(c+c+1)= last stagnant cell + int c = count_cells, c1 = c + 1, c2 = c + 2, c_1 = c - 1, cc = c + stagnant * c, cc1 = cc + 1; + + comp = sol_D[1].count_spec - sol_D[1].count_exch_spec; + cell_J_ij.clear(); + for (i = 0; i <= count_cells; i++) + { + std::map J_map; + for (cp = 0; cp < comp; cp++) + { + class J_ij_save J_save; + J_map[sol_D[1].spec[cp].name] = J_save; + } + cell_J_ij[i] = J_map; + } + if (heat_nmix) + comp += 1; + + if (Ct2 == NULL) + Ct2 = (LDBLE *) PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE)); + if (Ct2 == NULL) malloc_error(); + if (l_tk_x2 == NULL) + l_tk_x2 = (LDBLE *) PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE)); + if (l_tk_x2 == NULL) malloc_error(); + + if (A == NULL) + { + A = (LDBLE **)PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE *)); + if (A == NULL) malloc_error(); + for (i = 0; i < count_cells + 2 + stagnant * count_cells; i++) + { + if (stagnant) + A[i] = (LDBLE *)PHRQ_calloc((2 * count_cells + 2), sizeof(LDBLE)); + else + A[i] = (LDBLE *)PHRQ_malloc(3 * sizeof(LDBLE)); + if (A[i] == NULL) malloc_error(); + } + } + if (LU == NULL) + { + LU = (LDBLE **)PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE *)); + if (LU == NULL) malloc_error(); + for (i = 0; i < count_cells + 2 + stagnant * count_cells; i++) + { + if (stagnant) + LU[i] = (LDBLE *)PHRQ_calloc((2 * count_cells + 2), sizeof(LDBLE)); + else + LU[i] = (LDBLE *)PHRQ_malloc(3 * sizeof(LDBLE)); + if (LU[i] == NULL) malloc_error(); + } + } + if (mixf == NULL) + { + mixf = (LDBLE **)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE *)); + if (mixf == NULL) malloc_error(); + for (i = 0; i < count_cells + 2; i++) + { + mixf[i] = NULL; + } + } + if (stagnant) + { + if (mixf_stag == NULL) + { + mixf_stag = (LDBLE **)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE *)); + if (mixf_stag == NULL) malloc_error(); + for (i = 0; i < count_cells + 2; i++) + { + mixf_stag[i] = NULL; + } + } + } + if (comp + 2 > mixf_comp_size) + { + for (i = 0; i < count_cells + 2; i++) + { + if (mixf[i] == NULL) + mixf[i] = (LDBLE *)PHRQ_malloc(((size_t)comp + 2) * sizeof(LDBLE)); + else + mixf[i] = (LDBLE *)PHRQ_realloc(mixf[i], ((size_t)comp + 2) * sizeof(LDBLE)); + if (mixf[i] == NULL) malloc_error(); + if (stagnant) + { + if (mixf_stag[i] == NULL) + mixf_stag[i] = (LDBLE *)PHRQ_malloc(((size_t)comp + 2) * sizeof(LDBLE)); + else + mixf_stag[i] = (LDBLE *)PHRQ_realloc(mixf_stag[i], ((size_t)comp + 2) * sizeof(LDBLE)); + if (mixf_stag[i] == NULL) malloc_error(); + for (cp = 0; cp < comp; cp++) + mixf_stag[i][cp] = 0; + } + } + mixf_comp_size = comp + 2; + } + + if (dV_dcell) + find_current = 1; + // obtain b_ij... + dummy = default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor; + for (i = 0; i <= count_cells + 1; i++) + { + if (!heat_nmix) + { + if (i <= count_cells) + l_tk_x2[i] = (sol_D[i].tk_x + sol_D[i + 1].tk_x) / 2; + //if (stagnant && i > 0 && i <= c) + // l_tk_x2[i + c1] = (sol_D[i].tk_x + sol_D[i + c1].tk_x) / 2; + } + if (stagnant) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() == NULL) + { + i1 = 0; + if (i == 0 && (sptr1 = Utilities::Rxn_find(Rxn_solution_map, c2)) != NULL) + { + // the 1st boundary solution diffuses into 1st stagnant cell (=c2) by the ratio immobile_kgw in cell c2 / mobile_kgw in cell 1 + find_J(0, 1, 1, 1, 0); + ct[c2].kgw = sptr1->Get_mass_water(); + for (cp = 0; cp < comp; cp++) + { + if (!heat_nmix || cp < comp - 1) + mixf_stag[i][cp] = DDt * ct[i].v_m[cp].b_ij * ct[c2].kgw / ct[1].kgw; + else if (heat_nmix && cp == comp - 1) + mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[i].spec[0].Dwt; + } + i1 = c2; + } + else if (i == c1 && (sptr1 = Utilities::Rxn_find(Rxn_solution_map, cc1)) != NULL) + { + // the end boundary solution diffuses into stagnant cell cc1 + find_J(c, c1, 1, 1, 0); + ct[cc1].kgw = sptr1->Get_mass_water(); + for (cp = 0; cp < comp; cp++) + { + if (!heat_nmix || cp < comp - 1) + mixf_stag[i][cp] = DDt * ct[c].v_m[cp].b_ij * ct[cc1].kgw / ct[c].kgw; + else if (heat_nmix && cp == comp - 1) + mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[c].spec[0].Dwt; + } + i1 = cc1; + } + if (i1) + { + find_J(i, i1, 1, 1, 1); + } + } + else + { + use.Get_mix_ptr()->Vectorize(n_solution, fraction); + for (i1 = 0; i1 < (int) use.Get_mix_ptr()->Get_mixComps().size(); i1++) + { + if (n_solution[i1] > count_cells && n_solution[i1] <= cc1) + { + mfr = fraction[i1] / nmix; + find_J(i, n_solution[i1], mfr, 1, 1); + mfr *= (2.0 / dummy); + for (cp = 0; cp < comp; cp++) + { + if (!heat_nmix || cp < comp - 1) + mixf_stag[i][cp] = ct[i].v_m[cp].b_ij * mfr; + else if (heat_nmix && cp == comp - 1) + mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[i].spec[0].Dwt; + } + } + } + } + } + if (i <= count_cells) + find_J(i, i + 1, 1, 1, 0); + } + if (dV_dcell) + find_current = 0; + + ifirst = (bcon_first == 2 ? 1 : 0); + ilast = (bcon_last == 2 ? count_cells - 1 : count_cells); + + for (cp = 0; cp < comp; cp++) + { + for (i = 0; i <= cc1; i++) + { + if (heat_nmix && cp == comp - 1) + Ct2[i] = sol_D[i].tk_x; + else + Ct2[i] = sol_D[i].spec[cp].c; + } + // fill coefficient matrix A ... + // boundary cells ... + if (heat_nmix && cp == comp - 1) + { + mfr = mixf[0][cp] = heat_mix_array[0]; + mfr1 = mixf[1][cp] = heat_mix_array[1]; + } + else + { + mfr = mixf[0][cp] = DDt * ct[0].v_m[cp].b_ij; + mfr1 = mixf[1][cp] = DDt * ct[1].v_m[cp].b_ij; + } + if (bcon_first == 2) + { + A[1][0] = 0; A[1][1] = 1 + mfr1; A[1][2] = -mfr1; + if (stagnant) + { + A[0][0] = A[c2][c2] = 1; + if (mixf_stag[1][cp]) + { + A[1][1] += mixf_stag[1][cp]; + A[1][c2] = A[c2][1] = -mixf_stag[1][cp]; + A[c2][c2] += mixf_stag[1][cp]; + } + } + else + { + A[0][1] = 1; A[0][2] = 0; + } + } + else + { + A[1][0] = -mfr; A[1][1] = 1 + mfr + mfr1; A[1][2] = -mfr1; + if (stagnant) + { + if (dV_dcell) + { + A[0][0] = 1 + mfr; A[0][1] = -mfr; + } + else + A[0][0] = 1; + A[c2][c2] = 1; + if (mixf_stag[0][cp]) + { + if (dV_dcell) + { + A[0][0] += mixf_stag[0][cp]; A[0][c2] = -mixf_stag[0][cp]; + } + A[c2][0] = -mixf_stag[0][cp]; + A[c2][c2] += mixf_stag[0][cp]; + } + if (mixf_stag[1][cp]) + { + A[1][1] += mixf_stag[1][cp]; + A[1][c2] = A[c2][1] = -mixf_stag[1][cp]; + A[c2][c2] += mixf_stag[1][cp]; + } + } + else + { + if (dV_dcell) + { + A[0][1] = 1 + mfr; A[0][2] = -mfr; + } + else + { + A[0][1] = 1; A[0][2] = 0; + } + } + } + if (heat_nmix && cp == comp - 1) + { + mfr = mixf[c_1][cp] = heat_mix_array[c_1]; + mfr1 = mixf[count_cells][cp] = heat_mix_array[count_cells]; + } + else + { + mfr = mixf[c_1][cp] = DDt * ct[c_1].v_m[cp].b_ij; + mfr1 = mixf[count_cells][cp] = DDt * ct[count_cells].v_m[cp].b_ij; + } + if (bcon_last == 2) + { + if (stagnant) + { + A[count_cells][c_1] = -mfr; A[count_cells][count_cells] = 1 + mfr; + A[c1][c1] = A[cc1][cc1] = 1; + if (mixf_stag[count_cells][cp]) + { + A[count_cells][count_cells] += mixf_stag[count_cells][cp]; + A[count_cells][cc1] = A[cc1][count_cells] = -mixf_stag[count_cells][cp]; + A[cc1][cc1] += mixf_stag[count_cells][cp]; + } + } + else + { + A[count_cells][0] = -mfr; A[count_cells][1] = 1 + mfr; A[count_cells][2] = 0; + A[c1][0] = 0; A[c1][1] = 1; + } + } + else + { + if (stagnant) + { + A[count_cells][c_1] = -mfr; A[count_cells][count_cells] = 1 + mfr + mfr1; + A[count_cells][c1] = -mfr1; + if (dV_dcell) + { + A[c1][count_cells] = -mfr1; A[c1][c1] = 1 + mfr1; + } + else + A[c1][c1] = 1; + A[cc1][cc1] = 1; + if (mixf_stag[count_cells][cp]) + { + A[count_cells][count_cells] += mixf_stag[count_cells][cp]; + A[count_cells][cc1] = A[cc1][count_cells] = -mixf_stag[count_cells][cp]; + A[cc1][cc1] += mixf_stag[count_cells][cp]; + } + if (mixf_stag[c1][cp]) + { + if (dV_dcell) + { + A[c1][c1] += mixf_stag[c1][cp]; A[c1][cc1] = -mixf_stag[c1][cp]; + } + A[cc1][c1] = -mixf_stag[c1][cp]; + A[cc1][cc1] += mixf_stag[c1][cp]; + } + } + else + { + A[count_cells][0] = -mfr; A[count_cells][1] = 1 + mfr + mfr1; A[count_cells][2] = -mfr1; + if (dV_dcell) + { + A[c1][0] = -mfr1; A[c1][1] = 1 + mfr1; + } + else + { + A[c1][0] = 0; A[c1][1] = 1; + } + } + } + // inner cells ... + for (i = 2; i < count_cells; i++) + { + if (heat_nmix && cp == comp - 1) + { + mfr = mixf[i - 1][cp] = heat_mix_array[i - 1]; + mfr1 = mixf[i][cp] = heat_mix_array[i]; + } + else + { + mfr = mixf[i - 1][cp] = DDt * ct[i - 1].v_m[cp].b_ij; + mfr1 = mixf[i][cp] = DDt * ct[i].v_m[cp].b_ij; + } + if (stagnant && mixf_stag[i][cp]) + { + A[i][i - 1] = -mfr; A[i][i] = 1 + mfr + mfr1 + mixf_stag[i][cp]; A[i][i + 1] = -mfr1; + A[i + c1][i + c1] = 1 + mixf_stag[i][cp]; + A[i][i + c1] = A[i + c1][i] = -mixf_stag[i][cp]; + } + else + { + A[i][0] = -mfr; A[i][1] = 1 + mfr + mfr1; A[i][2] = -mfr1; + } + } + if (stagnant) + // decompose full A in LU, bypass A[][] = 0... + { + LDBLE s; + LU[0][0] = A[0][0]; LU[1][0] = A[1][0]; LU[c2][0] = A[c2][0]; + LU[0][1] = A[0][1] / LU[0][0]; LU[0][c2] = A[0][c2] / LU[0][0]; + for (i = 1; i <= cc1; i++) + { + i_1 = i - 1; + if (i < c2) + { + LU[i][i] = A[i][i] - LU[i][i_1] * LU[i_1][i]; + LU[i + 1][i] = A[i + 1][i]; + LU[i][i + 1] = A[i][i + 1] / LU[i][i]; + if (i == 1) + { + LU[c2][1] = A[c2][1] - LU[c2][0] * LU[0][1]; + LU[1][c2] = (A[1][c2] - LU[1][0] * LU[0][c2]) / LU[1][1]; + } + else + { + for (i0 = 0; i0 <= i_1; i0++) + { + if (i0 == i_1 && i0 < c) + { + LU[c2 + i0][i] = A[c2 + i0][i]; + LU[i][c2 + i0] = A[i][c2 + i0] / LU[i][i]; + } + else if (i0 < c) + { + LU[c2 + i0][i] = -LU[c2 + i0][i_1] * LU[i_1][i]; + LU[i][c2 + i0] = -LU[i][i_1] * LU[i_1][c2 + i0] / LU[i][i]; + } + if (i > c && i0 == c_1) + { + LU[c2 + i0][i] += A[c2 + i0][i]; + LU[i][c2 + i0] += A[i][c2 + i0] / LU[i][i]; + } + } + } + } + else + { + // the i = count_cells + 2 and higher rows are filled from i0 onwards, need to subtract the L*U terms for the ith column of L. + for (i0 = i; i0 <= cc1; i0++) + { + s = 0; + if (i0 == i) + { + i2 = (i == c2 ? 0 : i - c1); + for (i1 = i2; i1 <= i_1; i1++) + s += LU[i0][i1] * LU[i1][i]; + LU[i][i] = A[i][i] - s; + i2 += (i2 == 0 ? 2 : 1); + } + else + { + for (i1 = i2; i1 <= i_1; i1++) + s += LU[i0][i1] * LU[i1][i]; + LU[i0][i] = -s; + } + } + // and the ith row of U... + if (i == cc1) + break; + for (i0 = i + 1; i0 <= cc1; i0++) + { + s = 0; + if (i0 == i + 1) + i2 = i - c; + for (i1 = i2; i1 <= i_1; i1++) + s += LU[i][i1] * LU[i1][i0]; + LU[i][i0] = -s / LU[i][i]; + i2++; + } + } + } + Ct2[0] /= LU[0][0]; + for (i = 1; i <= cc1; i++) + { + i_1 = i - 1; + if (i < c2) + s = LU[i][i_1] * Ct2[i_1]; + else + { + i2 = 0; + s = 0; + for (i1 = i2; i1 <= i_1; i1++) + { + s += LU[i][i1] * Ct2[i1]; + i2 += (i2 == 0 ? 2 : 1); + } + } + Ct2[i] = (Ct2[i] - s) / LU[i][i]; + } + for (i = cc; i >= 0; i--) + { + s = 0; + for (i1 = i + 1; i1 <= cc1; i1++) + s += LU[i][i1] * Ct2[i1]; + Ct2[i] -= s; + } + } + else + { + // decompose A in LU : store L in A[..][0..1] and U in A[..][2] ... + for (i = 1; i <= count_cells + 1; i++) + { + A[i - 1][2] /= A[i - 1][1]; + A[i][1] -= A[i][0] * A[i - 1][2]; + } + // solve Ct2 in A.Ct2 = L.U.Ct2 = Ct1, Ct1 was put in Ct2 ... + // First, find y in L.y = Ct1, put y in Ct2 + Ct2[0] /= A[0][1]; + for (i = 1; i <= count_cells + 1; i++) + Ct2[i] = (Ct2[i] - A[i][0] * Ct2[i - 1]) / A[i][1]; + // Now obtain Ct2 in U.Ct2 = 'y' ... + for (i = count_cells; i >= 0; i--) + Ct2[i] -= A[i][2] * Ct2[i + 1]; + } + // Moles transported by concentration gradient from cell [i] to [i + 1] go in tot1, + // moles by stagnant exchange from cell [i] to [i + c1] go in tot_stag + // Correct for electro-neutrality... + for (i = ifirst; i <= ilast + 1; i++) + { + if (heat_nmix && cp == comp - 1) + { + l_tk_x2[i] = (Ct2[i + 1] + Ct2[i]) / 2; + cell_data[i].temp = Ct2[i] - 273.15; + sptr1 = Utilities::Rxn_find(Rxn_solution_map, i); + sptr1->Set_tc(Ct2[i] - 273.15); + if (stagnant && i > 0 && i < c1 && mixf_stag[i][cp]) + { + i1 = i + c1; + cell_data[i1].temp = Ct2[i1] - 273.15; + sptr2 = Utilities::Rxn_find(Rxn_solution_map, i1); + sptr2->Set_tc(Ct2[i1] - 273.15); + } + continue; + } + else if (i == ilast + 1 && (!stagnant || !mixf_stag[i][cp])) + continue; + + if (!cp) + { + ct[i].J_ij_sum = 0; + ct[i].Dz2c_stag = 0; + if (i < ilast + 1) + current_cells[i].ele = current_cells[i].dif = 0; + } + for (i0 = stagnant; i0 >= 0; i0--) + { + i1 = (i0 == 0 ? i + 1 : i == 0 ? c2 : i == c1 ? cc1 : i + c1); + if (Utilities::Rxn_find(Rxn_solution_map, i1) == NULL) + continue; + ct[i].J_ij[cp].name = sol_D[i].spec[cp].name; + ct[i].J_ij[cp].charge = ct[i].v_m[cp].z; + grad = (Ct2[i1] - Ct2[i])/* * ct[i].v_m[cp].D*/; // .D has the d{lg(gamma)} / d{lg(molal)} correction + if (!i0) + { + ct[i].J_ij[cp].tot1 = -mixf[i][cp] * grad; + + std::map >::iterator + cell_iter = cell_J_ij.find(i); + std::map< std::string, class J_ij_save >::iterator + s_iter = cell_iter->second.find(sol_D[1].spec[cp].name); + s_iter->second.flux_c = ct[i].J_ij[cp].tot1; + s_iter->second.flux_t = ct[i].J_ij[cp].tot1; + } + else + ct[i].J_ij[cp].tot_stag = -mixf_stag[i][cp] * grad; + if (ct[i].v_m[cp].z) + { + if (i == 0) + ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i1]; + else if (i == ilast) + { + if (!i0) + ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i]; + else + ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i1]; + } + else + ct[i].v_m[cp].zc = ct[i].v_m[cp].z * (Ct2[i] + Ct2[i1]) / 2; + if (i0) + { + mixf_stag[i][cp] *= ct[i].v_m[cp].zc; + ct[i].J_ij_sum += ct[i].v_m[cp].z * ct[i].J_ij[cp].tot_stag; + ct[i].Dz2c_stag -= ct[i].v_m[cp].z * mixf_stag[i][cp]; + } + else if (i < ilast + 1) + { + mixf[i][cp] *= ct[i].v_m[cp].zc; + current_cells[i].dif += ct[i].v_m[cp].z * ct[i].J_ij[cp].tot1; + current_cells[i].ele -= ct[i].v_m[cp].z * mixf[i][cp]; + } + } + } + } + // define element list + if (cp == 0) + dif_els_names.clear(); + if (heat_nmix && cp == comp - 1) + { + comp -= 1; + break; + } + char * temp_name = string_duplicate(ct[1].J_ij[cp].name); + const char* cptr = temp_name; + count_elts = 0; + get_elts_in_species(&cptr, 1); + free_check_null(temp_name); + for (int k = 0; k < count_elts; k++) + { + if (!strcmp(elt_list[k].elt->name, "X")) continue; + dif_els_names.insert(elt_list[k].elt->name); + } + } + count_m_s = (int) dif_els_names.size(); + sum_R = sum_Rd = 0; + for (i = ifirst; i <= ilast; i++) + { + ct[i].J_ij_count_spec = comp; + current_cells[i].R = l_tk_x2[i] / (current_cells[i].ele * F_Re3); + if (dV_dcell && !fix_current) + { + sum_R += current_cells[i].R; + if (i > 1) + sum_Rd += (current_cells[0].dif - current_cells[i].dif) * current_cells[i].R; + } + } + if (dV_dcell || fix_current) + { + if (fix_current) + { + int sign = (current_x >= 0 ? 1 : -1); + current_x = sign * fix_current * DDt / F_C_MOL; + j_0e = current_x - current_cells[ifirst].dif; + } + else + { + j_0e = (dV_dcell * count_cells - sum_Rd) / sum_R; + current_x = j_0e + current_cells[ifirst].dif; + } + } + else + { + current_x = 1e-10 / F_C_MOL; + cell_data[ifirst].potV = 0e-8; + j_0e = current_x - current_cells[ifirst].dif; + } + dVc = j_0e * current_cells[ifirst].R; + cell_data[ifirst + 1].potV = cell_data[ifirst].potV + dVc; + for (i = ifirst + 1; i <= ilast; i++) + { + dVc = current_cells[i].R * (current_x - current_cells[i].dif); + //if (((dV_dcell && (dVc * j_0e > 0)) || + // (dV_dcell > 0 && (cell_data[i].potV + dVc) > (cell_data[count_cells + 1].potV)) || + // (dV_dcell < 0 && (cell_data[i].potV + dVc) < (cell_data[count_cells + 1].potV)))) + if ((dV_dcell && (dVc * j_0e > 0)) || + ((dV_dcell > 0) && ((cell_data[i].potV + dVc) > cell_data[count_cells + 1].potV)) || + ((dV_dcell < 0) && ((cell_data[i].potV + dVc) < cell_data[count_cells + 1].potV))) + { + dVc = (cell_data[count_cells + 1].potV - cell_data[i].potV) / ((double)count_cells + 1 - (double)i); + } + cell_data[i + 1].potV = cell_data[i].potV + dVc; + } + //if (!dV_dcell || fix_current) + //{ + // dVc = current_cells[i].R * (current_x - current_cells[i].dif); + // cell_data[i + 1].potV = cell_data[i].potV + dVc; + //} + + for (cp = 0; cp < comp; cp++) + { + if (!ct[ifirst].v_m[cp].z) + continue; + for (i = ifirst; i <= ilast + stagnant; i++) + { + if (i < ilast + 1) + { + dVc = (cell_data[i + 1].potV - cell_data[i].potV) * F_Re3 / l_tk_x2[i]; + ct[i].J_ij[cp].tot1 -= mixf[i][cp] * dVc; + + std::map >::iterator + cell_iter = cell_J_ij.find(i); + std::map< std::string, class J_ij_save >::iterator + s_iter = cell_iter->second.find(sol_D[1].spec[cp].name); + s_iter->second.flux_t = ct[i].J_ij[cp].tot1; + } + if (stagnant && ct[i].Dz2c_stag) + { + ct[i].J_ij[cp].tot_stag += (mixf_stag[i][cp] * ct[i].J_ij_sum / ct[i].Dz2c_stag); + } + } + } + current_A = current_x / DDt * F_C_MOL; + + for (i = ifirst; i <= ilast + stagnant + (bcon_last == 2 ? 1 : 0); i++) + { + if (i <= ilast + 1) + { + // preserve the potentials... + sptr1 = Utilities::Rxn_find(Rxn_solution_map, i); + sptr1->Set_potV(cell_data[i].potV); + } + // Translate transport of the solute species into master species... + ct[i].count_m_s = count_m_s; + if (ct[i].m_s_size == 0 && ct[i].m_s != NULL) + ct[i].m_s = (class M_S *) free_check_null(ct[i].m_s); + if (ct[i].m_s == NULL) + { + ct[i].m_s = (class M_S *) PHRQ_malloc((count_m_s + 5) * sizeof(class M_S)); + ct[i].m_s_size = count_m_s + 5; + } + else if (count_m_s > ct[i].m_s_size) + { + ct[i].m_s = (class M_S *) PHRQ_realloc(ct[i].m_s, (count_m_s + 5) * sizeof(class M_S)); + ct[i].m_s_size = count_m_s + 5; + } + if (ct[i].m_s == NULL) + malloc_error(); + std::set ::iterator it = dif_els_names.begin(); + for (i1 = 0; i1 < count_m_s; i1++) + { + ct[i].m_s[i1].tot1 = ct[i].m_s[i1].tot2 = ct[i].m_s[i1].tot_stag = 0.0; + ct[i].m_s[i1].charge = 0.0; + ct[i].m_s[i1].name = (*it).c_str(); + it++; + } + fill_m_s(ct[i].J_ij, ct[i].J_ij_count_spec, i, stagnant); + } + /* + * 3. find the solutions in the column, add or subtract the moles... + */ + cxxNameDouble::iterator kit; + int if1, il1, incr; + for (cp = 0; cp < count_m_s; cp++) + { + if (!dV_dcell || dV_dcell * ct[1].m_s[cp].charge <= 0) + { + if1 = ifirst; il1 = ilast + 1; incr = 1; + } + else + { + if1 = ilast; il1 = ifirst - 1; incr = -1; + } + for (icell = if1; icell != il1; icell += incr) + { + min_mol = min_dif_M * ct[icell].kgw; + if (min_mol < 1e-13) + min_mol = 1e-13; + dum1 = dum2 = 0; + sptr1 = Utilities::Rxn_find(Rxn_solution_map, icell); + sptr2 = Utilities::Rxn_find(Rxn_solution_map, icell + 1); + if (stagnant && mixf_stag[icell][cp]) + { + i1 = (icell == 0 ? c1 + 1 : icell == c1 ? cc1 : icell + c1); + sptr_stag = Utilities::Rxn_find(Rxn_solution_map, i1); + } + else + sptr_stag = NULL; + + //if (!cp) + //{ + // ct[icell].J_ij_sum = ct[icell + 1].J_ij_sum = 0.0; + // if (sptr_stag) + // ct[i1].J_ij_sum = 0.0; + //} + + if (!strcmp(ct[icell].m_s[cp].name, "H")) + { + dummy = ct[icell].m_s[cp].tot1; + if (dV_dcell || (icell > 0 && icell <= ilast)) + sptr1->Set_total_h(sptr1->Get_total_h() - dummy); + if (dV_dcell || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2)) + sptr2->Set_total_h(sptr2->Get_total_h() + dummy); + if (sptr_stag) + { + dummy = ct[icell].m_s[cp].tot_stag; + if (dV_dcell || (icell > 0 && icell <= ilast)) + sptr1->Set_total_h(sptr1->Get_total_h() - dummy); + if (icell == c) + { + // mix the boundary solution with the stagnant column end-cell + dummy += ct[icell + 1].m_s[cp].tot_stag; + if (dV_dcell) + sptr2->Set_total_h(sptr2->Get_total_h() - ct[icell + 1].m_s[cp].tot_stag); + } + sptr_stag->Set_total_h(sptr_stag->Get_total_h() + dummy); + } + continue; + } + if (!strcmp(ct[icell].m_s[cp].name, "O")) + { + dummy = ct[icell].m_s[cp].tot1; + if (dV_dcell || (icell > 0 && icell <= ilast)) + sptr1->Set_total_o(sptr1->Get_total_o() - dummy); + if (dV_dcell || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2)) + sptr2->Set_total_o(sptr2->Get_total_o() + dummy); + if (sptr_stag) + { + dummy = ct[icell].m_s[cp].tot_stag; + if (dV_dcell || (icell > 0 && icell <= ilast)) + sptr1->Set_total_o(sptr1->Get_total_o() - dummy); + if (icell == c) + { + dummy += ct[icell + 1].m_s[cp].tot_stag; + if (dV_dcell) + sptr2->Set_total_o(sptr2->Get_total_o() - ct[icell + 1].m_s[cp].tot_stag); + } + sptr_stag->Set_total_o(sptr_stag->Get_total_o() + dummy); + } + //if (cp == count_m_s - 1) // transport the charge imbalance + //{ + // sptr1->Set_cb(sptr1->Get_cb() + ct[icell].J_ij_sum); + // sptr2->Set_cb(sptr2->Get_cb() + ct[icell + 1].J_ij_sum); + // if (sptr_stag) + // sptr_stag->Set_cb(sptr_stag->Get_cb() + ct[i1].J_ij_sum); + //} + continue; + } + dum1 = sptr1->Get_totals()[ct[icell].m_s[cp].name]; + dum2 = sptr2->Get_totals()[ct[icell].m_s[cp].name]; + if (sptr_stag) + dum_stag = sptr_stag->Get_totals()[ct[icell].m_s[cp].name]; + + // check for negative moles, add moles from other redox states and the donnan layer when necessary and available... + if (dum1 - ct[icell].m_s[cp].tot1 - ct[icell].m_s[cp].tot_stag < min_mol && + (dV_dcell || (icell > 0 && icell <= ilast))) + { + dum1 = moles_from_redox_states(sptr1, ct[icell].m_s[cp].name); + if (ct[icell].dl_s > 1e-8) + { + cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, icell); + if (s_ptr) + { + dum1 += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, ct[icell].m_s[cp].tot1 + ct[icell].m_s[cp].tot_stag - dum1 + min_mol); + } + } + sptr1->Get_totals()[ct[icell].m_s[cp].name] = dum1; + if (dum1 - ct[icell].m_s[cp].tot1 - ct[icell].m_s[cp].tot_stag < min_mol) + ct[icell].m_s[cp].tot1 = dum1 - ct[icell].m_s[cp].tot_stag - min_mol; + } + // check for negative moles, in the other cell... + ct[icell].m_s[cp].tot2 = ct[icell].m_s[cp].tot1; + dum = 0; + if (icell == c && sptr_stag && ct[c1].m_s[cp].tot_stag) + dum = ct[c1].m_s[cp].tot_stag; + if (dum2 + ct[icell].m_s[cp].tot2 - dum < min_mol && + (dV_dcell || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2))) + { + dum2 = moles_from_redox_states(sptr2, ct[icell].m_s[cp].name); + if (ct[icell + 1].dl_s > 1e-8) + { + cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, icell + 1); + if (s_ptr) + { + dum2 += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, -(ct[icell].m_s[cp].tot2 + dum2 - min_mol)); + } + } + sptr2->Get_totals()[ct[icell].m_s[cp].name] = dum2; + if (dum2 + ct[icell].m_s[cp].tot2 - dum < min_mol) + ct[icell].m_s[cp].tot2 = -dum2 + min_mol + dum; + } + if (fabs(ct[icell].m_s[cp].tot2) < fabs(ct[icell].m_s[cp].tot1)) + ct[icell].m_s[cp].tot1 = ct[icell].m_s[cp].tot2; + + if (dV_dcell || (icell > 0 && icell <= ilast)) + { + dum = ct[icell].m_s[cp].tot1; + if (stagnant) + dum += ct[icell].m_s[cp].tot_stag; + dum1 -= dum; + sptr1->Get_totals()[ct[icell].m_s[cp].name] = (dum1 > 0 ? dum1 : min_mol); + if (dum1 < 0) + { + dum += dum1 - min_mol; + if ((it1 = neg_moles.find(icell)) != neg_moles.end() + && (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end()) + dum1 += it2->second; + els.clear(); + els.insert(std::make_pair(ct[icell].m_s[cp].name, dum1)); + neg_moles.erase(icell); + neg_moles.insert(std::make_pair(icell, els)); + } + //ct[icell].J_ij_sum -= dum * ct[icell].m_s[cp].charge; + } + + if (dV_dcell || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2)) + { + dum = ct[icell].m_s[cp].tot1; + if (stagnant && icell == c && dV_dcell) + dum -= ct[c1].m_s[cp].tot_stag; + dum2 += dum; + sptr2->Get_totals()[ct[icell].m_s[cp].name] = (dum2 > 0 ? dum2 : min_mol); + if (dum2 < 0) + { + dum -= dum2 - min_mol; + if ((it1 = neg_moles.find(icell + 1)) != neg_moles.end() + && (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end()) + dum2 += it2->second; + els.clear(); + els.insert(std::make_pair(ct[icell].m_s[cp].name, dum2)); + neg_moles.erase(icell + 1); + neg_moles.insert(std::make_pair(icell + 1, els)); + } + //ct[icell + 1].J_ij_sum += dum * ct[icell].m_s[cp].charge; + } + + if (sptr_stag) + { + dum = ct[icell].m_s[cp].tot_stag; + if (icell == c) + dum += ct[c1].m_s[cp].tot_stag; + if (dum_stag + dum < 0) + { + dum_stag = moles_from_redox_states(sptr_stag, ct[icell].m_s[cp].name); + if (ct[i1].dl_s) + { + cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, i1); + if (s_ptr) + { + dum_stag += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, -(dum + dum_stag - min_mol)); + } + sptr_stag->Get_totals()[ct[icell].m_s[cp].name] = dum_stag; + } + } + dum_stag += dum; + sptr_stag->Get_totals()[ct[icell].m_s[cp].name] = (dum_stag > 0 ? dum_stag : min_mol); + if (dum_stag < 0) + { + dum -= dum_stag - min_mol; + if ((it1 = neg_moles.find(i1)) != neg_moles.end() + && (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end()) + dum_stag += it2->second; + els.clear(); + els.insert(std::make_pair(ct[icell].m_s[cp].name, dum_stag)); + neg_moles.erase(i1); + neg_moles.insert(std::make_pair(i1, els)); + } + //ct[i1].J_ij_sum += dum * ct[icell].m_s[cp].charge; + } + + // reduce oscillations in the column-boundary cells, but not for H and O, and current_A is not adjusted... + if (dV_dcell && icell == il1 - incr && dV_dcell * ct[0].m_s[cp].charge < 0 && strcmp(ct[0].m_s[cp].name, "H") && strcmp(ct[0].m_s[cp].name, "O") && c > 3 && mixrun > 1) + { + dummy = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_totals()[ct[0].m_s[cp].name] / ct[0].kgw * (1 - ct[0].dl_s); + if (dummy > 1e-6) + { + sptr1 = Utilities::Rxn_find(Rxn_solution_map, 1); + sptr2 = Utilities::Rxn_find(Rxn_solution_map, 2); + dum1 = sptr1->Get_totals()[ct[0].m_s[cp].name] / ct[1].kgw * (1 - ct[1].dl_s) - dummy; + dum2 = sptr2->Get_totals()[ct[0].m_s[cp].name] / ct[2].kgw * (1 - ct[2].dl_s) - dummy; + if (dum1 / dum2 < 0 || dum1 / dum2 > 1) + { + dum = cell_data[1].mid_cell_x / cell_data[2].mid_cell_x; + //ct[1].J_ij_sum -= sptr1->Get_totals()[ct[0].m_s[cp].name] * ct[1].m_s[cp].charge; + dum1 = (dummy + dum * dum2) * ct[1].kgw / (1 - ct[1].dl_s); + sptr1->Get_totals()[ct[0].m_s[cp].name] = dum1; + //ct[1].J_ij_sum += dum1 * ct[1].m_s[cp].charge; + } + } + dummy = Utilities::Rxn_find(Rxn_solution_map, c1)->Get_totals()[ct[0].m_s[cp].name] / ct[c1].kgw * (1 - ct[c1].dl_s); + if (dummy > 1e-6) + { + sptr1 = Utilities::Rxn_find(Rxn_solution_map, c); + sptr2 = Utilities::Rxn_find(Rxn_solution_map, c_1); + dum1 = sptr1->Get_totals()[ct[0].m_s[cp].name] / ct[c].kgw * (1 - ct[c].dl_s) - dummy; + dum2 = sptr2->Get_totals()[ct[0].m_s[cp].name] / ct[c_1].kgw * (1 - ct[c_1].dl_s) - dummy; + if (dum1 / dum2 < 0 || dum1 / dum2 > 1) + { + dum = (cell_data[c].mid_cell_x - cell_data[c_1].mid_cell_x) / + (cell_data[c1].mid_cell_x - cell_data[c_1].mid_cell_x); + //ct[c].J_ij_sum -= sptr1->Get_totals()[ct[0].m_s[cp].name] * ct[c].m_s[cp].charge; + dum1 = (dummy + (1 - dum) * dum2) * ct[c].kgw / (1 - ct[c].dl_s); + sptr1->Get_totals()[ct[0].m_s[cp].name] = dum1; + //ct[c].J_ij_sum += dum1 * ct[c].m_s[cp].charge; + } + } + } +//if (cp == count_m_s - 1) + //{ + // sptr1->Set_cb(sptr1->Get_cb() + ct[icell].J_ij_sum); + // sptr2->Set_cb(sptr2->Get_cb() + ct[icell + 1].J_ij_sum); + // if (sptr_stag) + // sptr_stag->Set_cb(sptr_stag->Get_cb() + ct[i1].J_ij_sum); + //} + } + } + return; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +moles_from_redox_states(cxxSolution *sptr, const char *name) +/* ---------------------------------------------------------------------- */ +{ + int length, length2; + cxxNameDouble::iterator kit; + LDBLE dum = 0; + + length = (int)strlen(name); + for (kit = sptr->Get_totals().begin(); kit != sptr->Get_totals().end(); kit++) + { + length2 = (int)(size_t)strcspn(kit->first.c_str(), "("); + if (length == length2 && !strncmp(name, kit->first.c_str(), length2)) + { + dum += kit->second; kit->second = 0; + } + } + return(dum); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +add_MCD_moles(LDBLE moles, LDBLE min_mol, int i, cxxSolution *sptr, const char *name) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::iterator kit; + std::map > ::iterator it1; + std::map ::iterator it2; + LDBLE dum = sptr->Get_totals()[name]; + + if (!dum) + dum = moles_from_redox_states(sptr, name); + if ((it1 = neg_moles.find(i)) != neg_moles.end() + && (it2 = (els = it1->second).find(name)) != els.end()) + { + dum += it2->second; + neg_moles.erase(it1); + els.erase(it2); + neg_moles.insert(std::make_pair(i, els)); + } + dum += moles; + if (dum < -min_mol) + { + if (ct[i].dl_s) + { + cxxSurface *su_ptr = Utilities::Rxn_find(Rxn_surface_map, i); + if (su_ptr != NULL) + dum += moles_from_donnan_layer(su_ptr, name, -dum + min_mol); + } + } + sptr->Get_totals()[name] = (dum > 0 ? dum : 0e-16); + if (dum < -min_mol) + { + els.insert(std::make_pair(name, dum)); + if ((it1 = neg_moles.find(i)) != neg_moles.end()) + neg_moles.erase(it1); + neg_moles.insert(std::make_pair(i, els)); + } + return(dum); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +moles_from_donnan_layer(cxxSurface *sptr, const char *name, LDBLE moles_needed) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::iterator kit; + LDBLE dum = 0; + cxxSurfaceCharge * charge_ptr = NULL; + + for (size_t j = 0; j < sptr->Get_surface_charges().size(); j++) + { + if (sptr->Get_dl_type() == cxxSurface::DONNAN_DL) + { + charge_ptr = &(sptr->Get_surface_charges()[j]); + for (kit = charge_ptr->Get_diffuse_layer_totals().begin(); kit != charge_ptr->Get_diffuse_layer_totals().end(); kit++) + { + if (strcmp(kit->first.c_str(), "H") == 0 || strcmp(kit->first.c_str(), "O") == 0) + continue; + if (strcmp(kit->first.c_str(), name) == 0) + { + if (kit->second > moles_needed) + { + dum += moles_needed; kit->second -= moles_needed; + } + else + { + dum += kit->second; kit->second = 0; + } + } + } + } + } + return(dum); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +multi_D(LDBLE DDt, int mobile_cell, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + /* + * 1. determine mole transfer (mol/s) of solute species for the interface between 2 cells. + * 2. sum up as mole transfer of master_species + * 3. add moles of master_species to the 2 cells + * NOTE. Define the water content of stagnant cells relative to the + * mobile cell (with, for example, 1 kg water) + * Define properties of each interface only 1 time with MIX. + * If an electrical field is applied (dV_dcell != 0), the currents j_i = current_cells[i].ele + dif (C * s) + are calculated for all cells. Then the ele part from cell 0 -> 1 is calculated: + current_x = (j_0d + j_0e) = j_0 = j_1 = ... = j_i + j_0e * (R0 + R1 + ...) + (j_0d - j_1d) * R1 + ... + (j_0d - j_id) * Ri = Vtot + or + j_0e * Sum_R + Sum_Rd = Vtot. + Ri = dV_dcell / j_ie, the relative cell resistance. + Solve j_0e, find (V1 - V0) = j_0e * R0. j_1e = current_x - j_1d, find (V2 - V1) = j_1e * R1, etc. + */ + int icell, jcell, i, l, n, length, length2, il_calcs; + int i1, loop_f_c; + int first_c, last_c, last_c2 = 0; + char token[MAX_LENGTH]; + LDBLE mixf, temp; + LDBLE dVtemp = 0.0; + if (dV_dcell && stagnant) + { + dVtemp = dV_dcell; + dV_dcell = 0; + } + cell_J_ij.clear(); + icell = jcell = -1; + first_c = last_c = -1; + il_calcs = -1; + + current_x = sum_R = sum_Rd = 0.0; + if (dV_dcell) + find_current = loop_f_c = 1; // calculate J_ij once for all cells, loop to dV_dcell2. + else + find_current = loop_f_c = 0; + + for (int f_c = 0; f_c <= loop_f_c; f_c++) + { + for (n = 0; n <= (stagnant ? stag_data.count_stag : 0); n++) // allow for stagnant cell mixing with higher cells in the layer + { + icell = mobile_cell + 1 + n * count_cells; + if (stagnant) + { + if (n == 0) + icell -= 1; + else if (mobile_cell == 0 || mobile_cell == count_cells + 1) + continue; + /* + * find the mix ptr for icell and go along the cells that mix with it + */ + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, icell)); + if (use.Get_mix_ptr() == NULL) + { + if (first_c < 0) + first_c = icell; + if (last_c2 < icell) + last_c2 = icell; + continue; + } + first_c = 0; + last_c = (int) (use.Get_mix_ptr()->Get_mixComps().size() - 1); + } + else + { /* regular column... */ + if (bcon_first == 1) + first_c = 0; + else + first_c = 1; + if (bcon_last == 1) + last_c = count_cells; + else + last_c = count_cells - 1; + last_c2 = (dV_dcell ? count_cells + 1: count_cells); + } + + for (i = first_c; i <= last_c; i++) + { + if (stagnant) + { + std::vector n_solution; + std::vector fraction; + (use.Get_mix_ptr())->Vectorize(n_solution, fraction); + jcell = n_solution[i]; + if (jcell <= icell) + continue; + if (jcell >= all_cells || jcell < 0) + { + error_string = sformatf( + "Multi_D asked for diffusion from cell %d to %d, %d is beyond the number of cells", icell, jcell, jcell); + error_msg(error_string, CONTINUE); + } + if (jcell > last_c2) + last_c2 = jcell; + mixf = (nmix ? fraction[i] / nmix : fraction[i]); + } + else + { /* regular column... */ + icell = i; + jcell = i + 1; + mixf = 1.0; + } + if (dV_dcell) + { + tk_x2 = (sol_D[icell].tk_x + sol_D[jcell].tk_x) / 2; + } + /* + * 1. obtain J_ij... + */ + il_calcs = (int) find_J(icell, jcell, mixf, DDt, stagnant); + + if (find_current) + { + if (i < last_c) + continue; + else + { + LDBLE dVc, j_0e; + // distribute dV_dcell according to relative resistance, calculate current_x if not fixed + j_0e = (dV_dcell * count_cells - sum_Rd) / sum_R; + current_x = j_0e + current_cells[0].dif; + if (fix_current) + { + int sign = (current_x >= 0 ? 1 : -1); + current_x = sign * fix_current / F_C_MOL; + j_0e = current_x - current_cells[0].dif; + } + dVc = j_0e * current_cells[0].R; + cell_data[1].potV = cell_data[0].potV + dVc; + for (i1 = 1; i1 < count_cells; i1++) + { + dVc = current_cells[i1].R * (current_x - current_cells[i1].dif); + cell_data[i1 + 1].potV = cell_data[i1].potV + dVc; + } + if (fix_current) + { + dVc = current_cells[i1].R * (current_x - current_cells[i1].dif); + cell_data[i1 + 1].potV = cell_data[i1].potV + dVc; + } + find_current = 0; + continue; + } + } + if (!ct[icell].J_ij_count_spec) + continue; + + /* + * 2. sum up the primary or secondary master_species + */ + if (!il_calcs) + { + tot1_h = tot1_o = tot2_h = tot2_o = 0.0; + m_s = (class M_S *) free_check_null(m_s); + count_m_s = (ct[icell].J_ij_count_spec < count_moles_added ? + ct[icell].J_ij_count_spec : count_moles_added); + m_s = (class M_S *) PHRQ_malloc((size_t) count_m_s * + sizeof(class M_S)); + if (m_s == NULL) + malloc_error(); + for (i1 = 0; i1 < count_m_s; i1++) + { + m_s[i1].name = NULL; + m_s[i1].tot1 = 0; + m_s[i1].tot2 = 0; + } + count_m_s = 0; + } + fill_m_s(ct[icell].J_ij, ct[icell].J_ij_count_spec, icell, stagnant); + + /* + * 3. find the solutions, add or subtract the moles... + */ + if (dV_dcell || (icell != 0 && icell != count_cells + 1)) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, icell)); + use.Get_solution_ptr()->Set_total_h(use.Get_solution_ptr()->Get_total_h() - tot1_h); + use.Get_solution_ptr()->Set_total_o(use.Get_solution_ptr()->Get_total_o() - tot1_o); + if (dV_dcell && (icell > 0 || fix_current)) + { + use.Get_solution_ptr()->Set_potV(cell_data[icell].potV); + } + for (l = 0; l < count_m_s; l++) + { + length = (int) strlen(m_s[l].name); + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + length2 = + (int) (size_t) strcspn(it->first.c_str(), "("); + if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2) + { + it->second -= m_s[l].tot1; + break; + } + } + if (it == use.Get_solution_ptr()->Get_totals().end()) + { + use.Get_solution_ptr()->Get_totals()[m_s[l].name] = -m_s[l].tot1; + } + } + } + if (dV_dcell || jcell != count_cells + 1) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, jcell)); + dummy = use.Get_solution_ptr()->Get_total_h(); + use.Get_solution_ptr()->Set_total_h(dummy + tot2_h); + dummy = use.Get_solution_ptr()->Get_total_o(); + use.Get_solution_ptr()->Set_total_o(dummy + tot2_o); + if (icell == count_cells && fix_current && !stagnant) + { + use.Get_solution_ptr()->Set_potV(cell_data[jcell].potV); + } + for (l = 0; l < count_m_s; l++) + { + length = (int) strlen(m_s[l].name); + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + length2 = (int) (size_t) strcspn(it->first.c_str(), "("); + if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2) + { + it->second += m_s[l].tot2; + break; + } + } + if (it == use.Get_solution_ptr()->Get_totals().end()) + { + use.Get_solution_ptr()->Get_totals()[m_s[l].name] = m_s[l].tot2; + } + } + } + } + } + } + // check for negative conc's... + //if (stagnant) + // first_c = mobile_cell; // allow for stagnant cell mixing with boundary cell 0 + for (i = first_c; i <= last_c2; i++) + { + //if (stagnant && i > first_c && i <= count_cells + first_c) + // continue; + + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (!use.Get_solution_ptr()) + continue; + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + if (strcmp(it->first.c_str(), "H(0)") == 0) + continue; + if (strcmp(it->first.c_str(), "O(0)") == 0) + continue; + LDBLE moles = it->second; + if (moles < 0 && ct[i].dl_s) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + cxxSurface * s_ptr = use.Get_surface_ptr(); + cxxSurfaceCharge * charge_ptr = NULL; + cxxNameDouble::iterator jit; + for (size_t j = 0; j < s_ptr->Get_surface_charges().size(); j++) + { + if (s_ptr->Get_dl_type() == cxxSurface::DONNAN_DL) + { + charge_ptr = &(s_ptr->Get_surface_charges()[j]); + for (jit = charge_ptr->Get_diffuse_layer_totals().begin(); jit != charge_ptr->Get_diffuse_layer_totals().end(); jit++) + { + if (strcmp(jit->first.c_str(), "H") == 0 || strcmp(jit->first.c_str(), "O") == 0) + continue; + if (strcmp(jit->first.c_str(), it->first.c_str()) == 0) + { + moles += jit->second; + it->second += jit->second; + jit->second = 0; + } + } + } + } + } + if (moles < 0) + { + temp = moles; + it->second = 0; + /* see if other redox states have more moles... */ + length = (int) strlen(it->first.c_str()); + cxxNameDouble::iterator kit; + for (kit = use.Get_solution_ptr()->Get_totals().begin(); + kit != use.Get_solution_ptr()->Get_totals().end(); kit++) + { + length2 = (int) (size_t) strcspn(kit->first.c_str(), "("); + if (!strncmp(it->first.c_str(), kit->first.c_str(), length2)) + { + temp += kit->second; + if (temp < 0) + { + kit->second = 0; + } + else + { + kit->second = temp; + break; + } + } + } + if (temp < -1e-12) + { + sprintf(token, + "Negative concentration in MCD: added %.4e moles %s in cell %d", + (double)-temp, it->first.c_str(), i); + warning_msg(token); + for (i1 = 0; i1 < count_moles_added; i1++) + { + if (moles_added[i1].name && !strcmp(moles_added[i1].name, it->first.c_str())) + { + moles_added[i1].moles -= temp; + break; + } + else if (!moles_added[i1].moles) + { + moles_added[i1].name = string_duplicate(it->first.c_str()); + moles_added[i1].moles -= temp; + break; + } + } + } + } + } + } + + m_s = (class M_S *) free_check_null(m_s); + + for (i = first_c; i < last_c2; i++) + { + if (stagnant && i > first_c && i <= count_cells + first_c) + continue; + ct[i].J_ij = (class J_ij *) free_check_null(ct[i].J_ij); + if (il_calcs) + ct[i].J_ij_il = (class J_ij *) free_check_null(ct[i].J_ij_il); + ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m); + } + if (dVtemp && stagnant) + { + dV_dcell = dVtemp; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_m_s(class J_ij *l_J_ij, int l_J_ij_count_spec, int icell, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + /* sum up the primary or secondary master_species from solute species + * H and O go in tot1&2_h and tot1&2_o + */ + int j, k, l; + LDBLE fraction; + const char* cptr; + + for (j = 0; j < l_J_ij_count_spec; j++) + { + { + char * temp_name = string_duplicate(l_J_ij[j].name); + cptr = temp_name; + count_elts = 0; + get_elts_in_species(&cptr, 1); + free_check_null(temp_name); + } + if (implicit && stagnant < 2) + { + for (k = 0; k < count_elts; k++) + { + for (l = 0; l < count_m_s; l++) + { + if (strcmp(ct[icell].m_s[l].name, elt_list[k].elt->name) == 0) + { + fraction = fabs((double)elt_list[k].coef * l_J_ij[j].tot1) + fabs(ct[icell].m_s[l].tot1); + if (fraction) + fraction = fabs((double)elt_list[k].coef * l_J_ij[j].tot1) / fraction; + else + fraction = 1; + ct[icell].m_s[l].tot1 += elt_list[k].coef * l_J_ij[j].tot1; + ct[icell].m_s[l].charge *= (1 - fraction); + ct[icell].m_s[l].charge += fraction * l_J_ij[j].charge; + if (stagnant) + ct[icell].m_s[l].tot_stag += elt_list[k].coef * l_J_ij[j].tot_stag; + break; + } + } + } + } + else + { + for (k = 0; k < count_elts; k++) + { + if (strcmp(elt_list[k].elt->name, "X") == 0) + continue; + if (strcmp(elt_list[k].elt->name, "H") == 0) + { + tot1_h += elt_list[k].coef * l_J_ij[j].tot1; + tot2_h += elt_list[k].coef * l_J_ij[j].tot2; + } + else if (strcmp(elt_list[k].elt->name, "O") == 0) + { + tot1_o += elt_list[k].coef * l_J_ij[j].tot1; + tot2_o += elt_list[k].coef * l_J_ij[j].tot2; + } + else + { + for (l = 0; l < count_m_s; l++) + { + if (strcmp(m_s[l].name, elt_list[k].elt->name) == 0) + { + m_s[l].tot1 += elt_list[k].coef * l_J_ij[j].tot1; + m_s[l].tot2 += elt_list[k].coef * l_J_ij[j].tot2; + break; + } + } + if (l == count_m_s) + { + m_s[l].name = elt_list[k].elt->name; + m_s[l].tot1 = elt_list[k].coef * l_J_ij[j].tot1; + m_s[l].tot2 = elt_list[k].coef * l_J_ij[j].tot2; + count_m_s++; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +calc_b_ij(int icell, int jcell, int k, LDBLE b_i, LDBLE b_j, LDBLE g_i, LDBLE g_j, LDBLE free_i, LDBLE free_j, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + ct[icell].v_m[k].b_ij = b_i * (free_i + g_i) * b_j * (free_j + g_j) / (b_i * (free_i + g_i) + b_j * (free_j + g_j)); + // At filterends, concentrations of ions change step-wise to the DL. + // We take the harmonic mean for f_free, the average for the DL. + if (ct[icell].v_m[k].z) + { + if (!g_i && g_j) + { + ct[icell].v_m[k].b_ij = free_j * b_i * b_j / (b_i + b_j) + + b_i * (1 - free_j) / 4 + b_j * g_j / 4; + } + else if (g_i && !g_j) + ct[icell].v_m[k].b_ij = free_i * b_i * b_j / (b_i + b_j) + + b_j * (1 - free_i) / 4 + b_i * g_i / 4; + } + // for boundary cells... + if (stagnant > 1) + { /* for a diffusion experiment with well-mixed reservoir in cell 3 and the last stagnant cell, + and with the mixf * 2 for the boundary cells in the input... */ + if (icell == 3 && !g_i && g_j) + ct[icell].v_m[k].b_ij = b_j * (free_j + g_j) / 2; + else if (jcell == all_cells - 1 && !g_j && g_i) + ct[icell].v_m[k].b_ij = b_i * (free_i + g_i) / 2; + } + else + { + if (icell == 0 || (icell == count_cells + 1 && jcell == count_cells + count_cells + 1)) + ct[icell].v_m[k].b_ij = b_j * (free_j + g_j); + else if (icell == count_cells && jcell == count_cells + 1) + ct[icell].v_m[k].b_ij = b_i * (free_i + g_i); + } + if (ct[icell].v_m[k].z) + ct[icell].Dz2c += ct[icell].v_m[k].b_ij * ct[icell].v_m[k].zc * ct[icell].v_m[k].z; + return; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_J(int icell, int jcell, LDBLE mixf, LDBLE DDt, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + /* mole transfer of the individual master_species: + * Eqn 1: + * J_ij = DDt * (A_ij / lav) * (-D_i*grad(c) + D_i*z_i*c_i * SUM(D_i*z_i*grad(c)) / SUM(D_i*(z_i)^2*c_i)) + * regular column, stagnant FALSE: + * D_i = temperature-corrected Dw + * A_ij = A_icell * A_jcell + * A_icell = (L porewater in i_cell / length_icell) / tort_f_icell / + * (length_icell / 2) + * lav = A_icell + A_jcell + * grad(c) is concentration difference in icell and jcell (dx is in lav), + for activity corrections see Appelo & Wersin, 2007. + ** dec. 28, 2015** + included aq_dl in the harmonic mean: + J_ij = - b_i * b_j / (b_i + b_j) * (c_j - c_i) in mol/s (see ex 21 in the manual 3). + b_i = A1 / (G_i * h_i / 2) * Dw for a pore without EDL. A1 = aq1 / h_i (m^2). + with EDL (no aq_il_i in A1, for now): + t_aq1 = aq1 + aq_dl_i. A1 = t_aq1 / h_i. f_free_i = aq1 / t_aq1. + b_i_cat = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) * Bm}. Bm = Boltzmann enrichment in EDL = g_dl. + b_i_ani = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) / Bm)}. + 22/2/18: now calculates diffusion through EDL's of multiple, differently charged surfaces + * stagnant TRUE: + * same eqn for J_ij, but multplies with 2 * mixf. (times 2, because mixf = A / (G_i * h_i)) + * mixf_ij = mixf / (Dw / init_tort_f) / new_tort_f * new_por / init_por + * mixf is defined in MIX; Dw is default multicomponent diffusion coefficient; + * init_tort_f equals multi_Dpor^(-multi_Dn); new_pf = new tortuosity factor. + * Interlayer diffusion (IL) takes the gradient in the equivalent concentrations on X-. + surface area A for IL: + stagnant: ct[icell].mixf_il is mixf * por_il / por. + por_il = interlayer porosity, from -interlayer_D true 'por_il'. + por = free + DL porewater porosity, from -multi_D true 'multi_Dpor'. + in regular column, A is calc'd from (free + DL porewater) and cell-length. + for IL: A * por_il / por. + + por_il should be entered for the cell with the maximal cec. + IL water is related to X-, thus the cec (eq/L IL water) is the same for all cells if X is difined. + IL-water = (free + DL porewater) * por_il / por. + for IL: A * aq_il / t_aq. + */ + int i, i_max, j, j_max, k, k_il, only_counter, il_calcs; + int i1; + LDBLE A1 = 0.0, A2 = 0.0, ddlm, aq1, aq2, t_aq1, t_aq2, f_free_i, f_free_j; + LDBLE dl_aq1, dl_aq2, dum, dum1, dum2, tort1, tort2, b_i, b_j; + LDBLE Sum_zM, aq_il1, aq_il2; + LDBLE por_il1, por_il2, por_il12 = 0.0; + LDBLE cec1, cec2, cec12 = 0.0, rc1 = 0.0, rc2 = 0.0; + LDBLE dV, c1, c2; + LDBLE max_b = 0.0; + + il_calcs = (interlayer_Dflag ? 1 : 0); + cxxSurface *s_ptr1, *s_ptr2; + LDBLE g_i, g_j; + + std::vector s_charge_p; + std::vector s_charge_p1; + std::vector s_charge_p2; + std::vector::iterator it_sc; + std::vector s_com_p; + + ct[icell].dl_s = ct[jcell].dl_s = dl_aq1 = dl_aq2 = 0.0; + + if (dV_dcell && !find_current) + goto dV_dcell2; + + /* check for immediate return and interlayer diffusion calcs... */ + //ct[icell].J_ij_sum = 0.0; + ct[icell].J_ij_count_spec = 0; + if (!il_calcs) + { + if (stagnant) + { + if (cell_data[icell].por < multi_Dpor_lim || cell_data[jcell].por < multi_Dpor_lim) + { + if (!implicit) + return (il_calcs); + } + } + else + { /* regular column... */ + if ((icell == 0 && cell_data[1].por < multi_Dpor_lim) + || (icell == count_cells && cell_data[count_cells].por < multi_Dpor_lim) + || (icell != 0 && icell != count_cells && (cell_data[icell].por < multi_Dpor_lim + || cell_data[jcell].por < multi_Dpor_lim))) + { + if (dV_dcell) + { + current_cells[icell].R = -1e15; + current_cells[icell].ele = dV_dcell / current_cells[icell].R; + current_cells[icell].dif = 0; + sum_R += current_cells[icell].R; + sum_Rd += current_cells[0].dif * current_cells[icell].R; + } + if (!implicit) + return (il_calcs); + } + } + } + + /* do the calcs */ + aq1 = ct[icell].kgw = Utilities::Rxn_find(Rxn_solution_map, icell)->Get_mass_water(); + aq2 = ct[jcell].kgw = Utilities::Rxn_find(Rxn_solution_map, jcell)->Get_mass_water(); + /* + * check if DL calculations must be made, find amounts of water... + */ + s_ptr1 = s_ptr2 = NULL; + ct[icell].visc1 = ct[icell].visc2 = 1.0; + only_counter = FALSE; + + s_ptr1 = Utilities::Rxn_find(Rxn_surface_map, icell); + if (s_ptr1 != NULL) + { + if (s_ptr1->Get_dl_type() != cxxSurface::NO_DL) + { + s_charge_p.assign(s_ptr1->Get_surface_charges().begin(), s_ptr1->Get_surface_charges().end()); + s_com_p.assign(s_ptr1->Get_surface_comps().begin(), s_ptr1->Get_surface_comps().end()); + + if (s_ptr1->Get_only_counter_ions()) + only_counter = TRUE; + + ct[icell].visc1 = s_ptr1->Get_DDL_viscosity(); + /* find the immobile surface charges with DL... */ + for (i = 0; i < (int)s_charge_p.size(); i++) + { + for (i1 = 0; i1 < (int)s_com_p.size(); i1++) + { + if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw()) + { + dl_aq1 += s_charge_p[i].Get_mass_water(); + s_charge_p1.push_back(s_charge_p[i]); + break; + } + } + } + } + } + s_ptr2 = Utilities::Rxn_find(Rxn_surface_map, jcell); + if (s_ptr2 != NULL) + { + if (s_ptr2->Get_dl_type() != cxxSurface::NO_DL) + { + s_charge_p.assign(s_ptr2->Get_surface_charges().begin(), s_ptr2->Get_surface_charges().end()); + s_com_p.assign(s_ptr2->Get_surface_comps().begin(), s_ptr2->Get_surface_comps().end()); + + if (s_ptr2->Get_only_counter_ions()) + only_counter = TRUE; + + ct[icell].visc2 = s_ptr2->Get_DDL_viscosity(); + + for (i = 0; i < (int)s_charge_p.size(); i++) + { + for (i1 = 0; i1 < (int)s_com_p.size(); i1++) + { + if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw()) + { + dl_aq2 += s_charge_p[i].Get_mass_water(); + s_charge_p2.push_back(s_charge_p[i]); + break; + } + } + } + } + } + if (!stagnant) + { + if (icell == 0) + ct[icell].visc1 = ct[icell].visc2; + else if (icell == count_cells) + ct[icell].visc2 = ct[icell].visc1; + } + + /* in each cell: DL surface = mass_water_DL / (cell_length) + free pore surface = mass_water_free / (cell_length) + determine DL surface as a fraction of the total pore surface... */ + t_aq1 = aq1 + dl_aq1; + t_aq2 = aq2 + dl_aq2; + f_free_i = aq1 / t_aq1; + f_free_j = aq2 / t_aq2; + if (dl_aq1 > 0) + ct[icell].dl_s = dl_aq1 / t_aq1; + if (dl_aq2 > 0) + { + ct[jcell].dl_s = dl_aq2 / t_aq2; + if (!ct[icell].dl_s) + ct[icell].dl_s = 1e-8; // used in implicit appt + } + + if (il_calcs) + { + /* find interlayer porosity por_il, + make it relative to exchange capacity (mol X/L), highest X in sol_D[1].x_max (mol X / L). + Find amounts of IL water and cec. */ + por_il1 = por_il2 = por_il12 = 0.0; + cec1 = cec2 = cec12 = rc1 = rc2 = 0.0; + if (icell == 0) + { + por_il1 = sol_D[0].exch_total / aq1 / sol_D[1].x_max * + cell_data[1].por_il; + por_il2 = sol_D[1].exch_total / aq2 / sol_D[1].x_max * + cell_data[1].por_il; + if (sol_D[0].exch_total > 3e-10 && sol_D[1].exch_total > 3e-10) + /* take the harmonic mean... */ + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + /* at column ends, take the clay... */ + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il2 = t_aq2 * por_il12 / cell_data[1].por; + aq_il1 = t_aq2 * por_il12 / cell_data[1].por; + } + else if (icell == count_cells) + { + por_il1 = sol_D[count_cells].exch_total / aq1 / sol_D[1].x_max * + cell_data[count_cells].por_il; + por_il2 = sol_D[count_cells + 1].exch_total / aq2 / sol_D[1].x_max * + cell_data[count_cells].por_il; + if (sol_D[count_cells].exch_total > 3e-10 && sol_D[count_cells + 1].exch_total > 3e-10) + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il1 = t_aq1 * por_il12 / cell_data[count_cells].por; + aq_il2 = t_aq2 * por_il12 / cell_data[count_cells].por; + } + else + { + por_il1 = sol_D[icell].exch_total / aq1 / sol_D[1].x_max * + cell_data[icell].por_il; + por_il2 = sol_D[jcell].exch_total / aq2 / sol_D[1].x_max * + cell_data[jcell].por_il; + + if (sol_D[icell].exch_total > 3e-10 && sol_D[jcell].exch_total > 3e-10) + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il1 = t_aq1 * por_il12 / cell_data[icell].por; + aq_il2 = t_aq2 * por_il12 / cell_data[jcell].por; + } + if (por_il12 < interlayer_Dpor_lim) + il_calcs = 0; + else + { + dum = sol_D[icell].exch_total; + dum2 = sol_D[jcell].exch_total; + // the rc's are for distributing the mole transfer (later on) over X and solution. + rc1 = (dum2 > dum ? dum / dum2 : 1); + rc2 = (dum > dum2 ? dum2 / dum : 1); + if (sol_D[icell].exch_total > 3e-10) + cec1 = dum / aq_il1; + else + cec1 = 2e-10; + if (sol_D[jcell].exch_total > 3e-10) + cec2 = dum2 / aq_il2; + else + cec2 = 2e-10; + /* and the largest for calculating the mass transfer since it is usually at the boundary... */ + cec12 = (cec1 > cec2 ? cec1 : cec2); + } + } + + /* Find ct[icell].mixf_il for IL diffusion. + In stagnant calc's, correct mixf by default values, A = por / tort. + In regular column, A = surface area / (0.5 * x * tort)*/ + + tort1 = tort2 = 1.0; + ct[icell].A_ij_il = ct[icell].mixf_il = 0.0; + if (stagnant) + { + mixf /= (default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor); + if (il_calcs) + ct[icell].mixf_il = mixf * por_il12 / interlayer_tortf; + } + if (icell == 0) + { + tort1 = tort2 = pow(cell_data[1].por, -multi_Dn); + if (stagnant) + A2 = cell_data[1].por / tort2; + else + { + A2 = t_aq2 / (cell_data[1].length * 0.5 * cell_data[1].length); + if (il_calcs && !stagnant) + ct[icell].A_ij_il = A2 * por_il12 / (cell_data[1].por * interlayer_tortf); + A2 /= tort2; + } + A1 = A2; + } + else if (icell == count_cells) + { + tort1 = tort2 = pow(cell_data[count_cells].por, -multi_Dn); + if (stagnant) + A1 = cell_data[count_cells].por / tort1; + else + { + A1 = t_aq1 / (cell_data[count_cells].length * 0.5 * cell_data[count_cells].length); + if (il_calcs && !stagnant) + ct[icell].A_ij_il = A1 * por_il12 / (cell_data[count_cells].por * interlayer_tortf); + A1 /= tort1; + } + A2 = A1; + } + else + { + tort1 = pow(cell_data[icell].por, -multi_Dn); + tort2 = pow(cell_data[jcell].por, -multi_Dn); + if (stagnant) + { + A1 = cell_data[icell].por / tort1; + A2 = cell_data[jcell].por / tort2; + } + else + { + A1 = t_aq1 / (cell_data[icell].length * 0.5 * cell_data[icell].length); + A2 = t_aq2 / (cell_data[jcell].length * 0.5 * cell_data[jcell].length); + if (il_calcs && !stagnant) + { + dum = A1 * por_il12 / (cell_data[icell].por * interlayer_tortf); + dum2 = A2 * por_il12 / (cell_data[jcell].por * interlayer_tortf); + ct[icell].A_ij_il = dum * dum2 / (dum + dum2); + } + A1 /= tort1; + A2 /= tort2; + } + } + /* diffuse... */ + /* + * malloc sufficient space... + */ + k = sol_D[icell].count_spec + sol_D[jcell].count_spec; + + if (ct[icell].J_ij == NULL) + { + ct[icell].J_ij = (class J_ij *) PHRQ_malloc((size_t)(k) * sizeof(class J_ij)); + ct[icell].J_ij_size = k; + } + else if (k > ct[icell].J_ij_size) + { + ct[icell].J_ij = (class J_ij *) PHRQ_realloc(ct[icell].J_ij, (size_t)(k) * sizeof(class J_ij)); + ct[icell].J_ij_size = k; + } + if (ct[icell].J_ij == NULL) + malloc_error(); + + if (ct[icell].v_m == NULL) + { + ct[icell].v_m = (struct V_M *) PHRQ_malloc((size_t)(k) * sizeof(struct V_M)); + ct[icell].v_m_size = k; + } + else if (k > ct[icell].v_m_size) + { + ct[icell].v_m = (struct V_M *) PHRQ_realloc(ct[icell].v_m, (size_t)(k) * sizeof(struct V_M)); + ct[icell].v_m_size = k; + } + if (ct[icell].v_m == NULL) + malloc_error(); + + for (i = 0; i < k; i++) + { + ct[icell].J_ij[i].tot1 = ct[icell].J_ij[i].tot_stag = 0.0; + ct[icell].v_m[i].grad = 0.0; + ct[icell].v_m[i].D = 1.0; // used for gamma correction + ct[icell].v_m[i].z = 0.0; + ct[icell].v_m[i].c = 0.0; + ct[icell].v_m[i].zc = 0.0; + //ct[icell].v_m[i].Dz = 0.0; + //ct[icell].v_m[i].Dzc = 0.0; + ct[icell].v_m[i].b_ij = 0.0; + } + ct[icell].Dz2c = ct[icell].Dz2c_il = 0.0; + + if (il_calcs) + { + /* also for interlayer cations */ + k = sol_D[icell].count_exch_spec + sol_D[jcell].count_exch_spec; + + ct[icell].J_ij_il = (class J_ij *) free_check_null(ct[icell].J_ij_il); + ct[icell].J_ij_il = (class J_ij *) PHRQ_malloc((size_t) k * sizeof(class J_ij)); + if (ct[icell].J_ij_il == NULL) + malloc_error(); + + ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il); + ct[icell].v_m_il = (struct V_M *) PHRQ_malloc((size_t) k * sizeof(struct V_M)); + if (ct[icell].v_m_il == NULL) + malloc_error(); + + for (i = 0; i < k; i++) + { + ct[icell].J_ij_il[i].tot1 = 0.0; + ct[icell].v_m_il[i].grad = 0.0; + ct[icell].v_m_il[i].D = 0.0; + ct[icell].v_m_il[i].z = 0.0; + ct[icell].v_m_il[i].c = 0.0; + ct[icell].v_m_il[i].zc = 0.0; + ct[icell].v_m_il[i].Dz = 0.0; + ct[icell].v_m_il[i].Dzc = 0.0; + ct[icell].v_m_il[i].b_ij = 0.0; + } + } + /* + * coefficients in Eqn (1)... + */ + i = j = k = k_il = 0; + i_max = sol_D[icell].count_spec; + j_max = sol_D[jcell].count_spec; + + while (i < i_max || j < j_max) + { + if (j == j_max + || (i < i_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) < 0)) + { + /* species 'name' is only in icell */ + if (il_calcs && sol_D[icell].spec[i].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name; + ct[icell].v_m_il[k_il].D = sol_D[icell].spec[i].Dwt; + ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + dum = sol_D[icell].spec[i].c * cec12 / (2 * ct[icell].v_m_il[k_il].z); + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * dum; + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = -sol_D[icell].spec[i].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[icell].spec[i].name; + ct[icell].v_m[k].z = sol_D[icell].spec[i].z; + ct[icell].v_m[k].grad = -sol_D[icell].spec[i].c; /* assume d log(gamma) / d log(c) = 0 */ + c1 = sol_D[icell].spec[i].c / 2; + ct[icell].v_m[k].c = c1; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c1; + + if (dV_dcell && ct[icell].v_m[k].z && !fix_current) + { + // compare diffusive and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c1; + if (dum + dum2 > 0) + { + // step out: no transport against the dV_dcell gradient if c = 0 in jcell... + if (i < i_max) + i++; + continue; + } + } + + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_i *= sol_D[icell].spec[i].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + if (ct[icell].v_m[k].z == 0 || only_counter) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + else + { + if (abs(ct[icell].v_m[k].z) == 1) + // there is always H+ and OH-... + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + else + { + dum1 = it_sc->Get_mass_water() / t_aq2; + dum2 = it_sc->Get_z_gMCD_map()[1] / dum1; + g_j += pow(dum2, ct[icell].v_m[k].z) * dum1; + } + } + } + g_j *= sol_D[icell].spec[i].erm_ddl; + } + } + + b_i = A1 * sol_D[icell].spec[i].Dwt; + b_j = A2; + if (sol_D[icell].tk_x == sol_D[jcell].tk_x) + b_j *= sol_D[icell].spec[i].Dwt; + else + { + dum2 = sol_D[icell].spec[i].Dwt / sol_D[icell].viscos_f; + dum2 *= exp(sol_D[icell].spec[i].dw_t / sol_D[jcell].tk_x - sol_D[icell].spec[i].dw_t / sol_D[icell].tk_x); + dum2 *= sol_D[jcell].viscos_f; + b_j *= dum2; + } + calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant); + + k++; + } + if (i < i_max) + i++; + } + + else if (i == i_max || + (j < j_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) > 0)) + { + /* species 'name' is only in jcell */ + if (il_calcs && sol_D[jcell].spec[j].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[jcell].spec[j].name; + ct[icell].v_m_il[k_il].D = sol_D[jcell].spec[j].Dwt; + ct[icell].v_m_il[k_il].z = sol_D[jcell].spec[j].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * sol_D[jcell].spec[j].c * + cec12 / (2 * ct[icell].v_m_il[k_il].z); + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = sol_D[jcell].spec[j].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[jcell].spec[j].name; + ct[icell].v_m[k].z = sol_D[jcell].spec[j].z; + ct[icell].v_m[k].grad = sol_D[jcell].spec[j].c; /* assume d log(gamma) / d log(c) = 0 */ + c2 = sol_D[jcell].spec[j].c / 2; + ct[icell].v_m[k].c = c2; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c2; + + if (dV_dcell && ct[icell].v_m[k].z && !fix_current) + { + // compare diffuse and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c2; + // don't transport unavailable moles against the gradient + if (dum + dum2 < 0) + { + // step out... + if (j < j_max) + j++; + continue; + } + } + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + if (ct[icell].v_m[k].z == 0 || only_counter) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + else + { + if (abs(ct[icell].v_m[k].z) == 1) + // there is always H+ and OH-... + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + else + { + dum1 = it_sc->Get_mass_water() / t_aq1; + dum2 = it_sc->Get_z_gMCD_map()[1] / dum1; + g_i += pow(dum2, ct[icell].v_m[k].z) * dum1; + } + } + } + g_i *= sol_D[jcell].spec[j].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_j *= sol_D[jcell].spec[j].erm_ddl; + } + } + b_i = A1; + b_j = A2 * sol_D[jcell].spec[j].Dwt; + if (sol_D[icell].tk_x == sol_D[jcell].tk_x) + b_i *= sol_D[jcell].spec[j].Dwt; + else + { + dum2 = sol_D[jcell].spec[j].Dwt / sol_D[jcell].viscos_f; + dum2 *= exp(sol_D[jcell].spec[j].dw_t / sol_D[icell].tk_x - sol_D[jcell].spec[j].dw_t / sol_D[jcell].tk_x); + dum2 *= sol_D[icell].viscos_f; + b_i *= dum2; + } + calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant); + + k++; + } + if (j < j_max) + j++; + } + else if (strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) == 0) + { + /* species 'name' is in both cells */ + if (il_calcs && sol_D[icell].spec[i].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name; + if (sol_D[icell].spec[i].Dwt == 0 || sol_D[jcell].spec[j].Dwt == 0) + ct[icell].v_m_il[k_il].D = 0.0; + else + ct[icell].v_m_il[k_il].D = + (sol_D[icell].spec[i].Dwt + sol_D[jcell].spec[j].Dwt) / 2; + + ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * (sol_D[icell].spec[i].c + + sol_D[jcell].spec[j].c) * cec12 / ct[icell].v_m_il[k_il].z; + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c) * + cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[icell].spec[i].name; + ct[icell].v_m[k].z = sol_D[icell].spec[i].z; + ct[icell].v_m[k].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c); + c1 = sol_D[icell].spec[i].c / 2; + c2 = sol_D[jcell].spec[j].c / 2; + ct[icell].v_m[k].c = c1 + c2; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * ct[icell].v_m[k].c; + + if (dV_dcell && ct[icell].v_m[k].z && !fix_current && !implicit) + { + // compare diffuse and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * (c1 + c2); + // don't transport unavailable moles against the gradient + if (fabs(dum) < fabs(dum2) && + ((dum2 >= 0 && sol_D[jcell].spec[j].c * aq2 < 1e-12) || + (dum2 <= 0 && sol_D[icell].spec[i].c * aq1 < 1e-12))) + { + // step out: + if (i < i_max) + i++; + if (j < j_max) + j++; + continue; + } + } + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_i *= sol_D[icell].spec[i].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_j *= sol_D[jcell].spec[j].erm_ddl; + } + } + b_i = A1 * sol_D[icell].spec[i].Dwt; + b_j = A2 * sol_D[jcell].spec[j].Dwt; + calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant); + + //ddlm = sol_D[jcell].spec[j].lm - sol_D[icell].spec[i].lm; // appt: this could give an incorrect large factor for implicit + //if (fabs(ddlm) > 1e-10) + // ct[icell].v_m[k].grad *= (1 + (sol_D[jcell].spec[j].lg - sol_D[icell].spec[i].lg) / ddlm); + ddlm = sol_D[jcell].spec[j].lm - sol_D[icell].spec[i].lm; + dum1 = sol_D[jcell].spec[j].lg - sol_D[icell].spec[i].lg; + dum = 1; + if (ddlm) + dum += dum1 / ddlm; + if (dum > 0.2 && dum < 2.25) + { + ct[icell].v_m[k].grad *= dum; + if (implicit) + ct[icell].v_m[k].D = dum; + } + //if (ct[icell].v_m[k].b_ij > max_b) + // max_b = ct[icell].v_m[k].b_ij; + k++; + } + if (i < i_max) + i++; + if (j < j_max) + j++; + } + } + if (!dV_dcell && !ct[icell].Dz2c) + k = 0; + ct[icell].J_ij_count_spec = i_max = k; + ct[icell].J_ij_il_count_spec = k_il; + if (icell == count_cells) + ct[jcell].J_ij_count_spec = ct[icell].J_ij_count_spec; + + if (implicit && stagnant < 2) + return(max_b); + /* + * fill in J_ij... + */ + if (dV_dcell) + { + current_cells[icell].ele = current_cells[icell].dif = 0; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (!ct[icell].v_m[i].z) + continue; + current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].zc; + current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].grad; + } + current_cells[icell].R = tk_x2 / (current_cells[icell].ele * F_Re3); + sum_R += current_cells[icell].R; + sum_Rd += (current_cells[0].dif - current_cells[icell].dif) * current_cells[icell].R; + return(il_calcs); + } + + // dV_dcell, 2nd pass, current_x has been calculated, and + // voltage was adapted to give equal current in the cells. +dV_dcell2: + + //ct[icell].J_ij_sum = 0; + Sum_zM = 0.0; + + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (ct[icell].v_m[i].z) + Sum_zM += ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * ct[icell].v_m[i].grad; + } + std::map J_map; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + class J_ij_save J_save; + ct[icell].J_ij[i].tot1 = -ct[icell].v_m[i].grad; + J_save.flux_c = ct[icell].J_ij[i].tot1; + ct[icell].J_ij[i].charge = ct[icell].v_m[i].z; + if (!dV_dcell && ct[icell].v_m[i].z && ct[icell].Dz2c > 0) + { + ct[icell].J_ij[i].tot1 += Sum_zM * ct[icell].v_m[i].zc / ct[icell].Dz2c; + } + J_save.flux_t = ct[icell].J_ij[i].tot1; + if (stagnant) + { + ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * 2 * mixf; + J_save.flux_c *= ct[icell].v_m[i].b_ij * 2 * mixf; + J_save.flux_t *= ct[icell].v_m[i].b_ij * 2 * mixf; + } + else + { + ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * DDt; + J_save.flux_c *= ct[icell].v_m[i].b_ij * DDt; + J_save.flux_t *= ct[icell].v_m[i].b_ij * DDt; + } + J_map[ct[icell].J_ij[i].name] = J_save; + ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1; + //ct[icell].J_ij_sum += ct[icell].v_m[i].z * ct[icell].J_ij[i].tot1; + } + cell_J_ij[icell] = J_map; + // assure that icell and jcell have dl water when checking negative conc's in MCD + ct[icell].dl_s = dl_aq1; + ct[jcell].dl_s = dl_aq2; + + if (dV_dcell) + { + //ct[icell].J_ij_sum = 0; + dV = cell_data[jcell].potV - cell_data[icell].potV; + dum = dV * F_Re3 / tk_x2; + // perhaps adapt dV for getting equal current... + //current_cells[icell].ele = current_cells[icell].dif = 0; + //for (i = 0; i < ct[icell].J_ij_count_spec; i++) + //{ + // if (!ct[icell].v_m[i].z) + // continue; + // current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + // ct[icell].v_m[i].zc * dum; + // current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + // ct[icell].v_m[i].grad; + //} + //dum1 = (current_x - current_cells[icell].dif) / current_cells[icell].ele; + //if (isnan(dum1)) + // dum1 = 1; + //dum *= dum1; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (!ct[icell].v_m[i].z) + continue; + ct[icell].J_ij[i].tot1 -= ct[icell].v_m[i].b_ij * + ct[icell].v_m[i].zc * dum * DDt; + ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1; + //ct[icell].J_ij_sum += ct[icell].v_m[i].z * ct[icell].J_ij[i].tot1; + } + current_A = current_x * F_C_MOL; + } + /* + * calculate interlayer mass transfer... + */ + if (il_calcs && ct[icell].Dz2c_il != 0 && ct[icell].J_ij_il_count_spec > 0) + { + cxxExchange *ex_ptr1 = Utilities::Rxn_find(Rxn_exchange_map, icell); + cxxExchange *ex_ptr2 = Utilities::Rxn_find(Rxn_exchange_map, jcell); + Sum_zM = 0.0; + i_max = k_il = ct[icell].J_ij_il_count_spec; + for (i = 0; i < i_max; i++) + Sum_zM += ct[icell].v_m_il[i].Dz * ct[icell].v_m_il[i].grad; + for (i = 0; i < i_max; i++) + { + ct[icell].J_ij_il[i].tot1 = -ct[icell].v_m_il[i].D * ct[icell].v_m_il[i].grad + + Sum_zM * ct[icell].v_m_il[i].Dzc / ct[icell].Dz2c_il; + if (stagnant) + ct[icell].J_ij_il[i].tot1 *= ct[icell].mixf_il; + else + ct[icell].J_ij_il[i].tot1 *= ct[icell].A_ij_il * DDt; + //ct[icell].J_ij_sum += ct[icell].v_m_il[i].z * ct[icell].J_ij_il[i].tot1; + ct[icell].J_ij_il[i].tot2 = ct[icell].J_ij_il[i].tot1; + // ct[icell].J_ij_il[i].charge = ct[icell].v_m_il[i].z; // appt not used now + } + + /* express the transfer in elemental moles... */ + tot1_h = tot1_o = tot2_h = tot2_o = 0.0; + m_s = (class M_S *) free_check_null(m_s); + m_s = (class M_S *) PHRQ_malloc((size_t) count_moles_added * + sizeof(class M_S)); + if (m_s == NULL) + malloc_error(); + for (i1 = 0; i1 < count_moles_added; i1++) + { + m_s[i1].charge = 0; + m_s[i1].name = NULL; + m_s[i1].tot1 = 0; + m_s[i1].tot2 = 0; + } + count_m_s = 0; + fill_m_s(ct[icell].J_ij_il, k_il, icell, stagnant); + + /* do the mass transfer... */ + if (icell > 0 || stagnant) + { + size_t k; + for (k = 0; k < ex_ptr1->Get_exchange_comps().size(); k++) + { + cxxNameDouble nd(ex_ptr1->Get_exchange_comps()[k].Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + i_max = 0; + for (; it != nd.end(); it++) + { + if (strcmp("X", it->first.c_str()) == 0) + i_max = 1; + } + if (i_max) + break; + } + + if (k < ex_ptr1->Get_exchange_comps().size()) + { + cxxExchComp &comp_ref = ex_ptr1->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + /* transfer O and H... */ + for (; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp("H", elt_ptr->name) == 0) + { + if (coef < rc1 * tot1_h) + { + tot1_h -= coef; + comp_ref.Get_totals().insert("H", 0); + } + else + { + comp_ref.Get_totals().insert("H", coef - rc1 * tot1_h); + tot1_h *= (1 - rc1); + } + } + else if (strcmp("O", elt_ptr->name) == 0) + { + if (coef < rc1 * tot1_o) + { + tot1_o -= coef; + comp_ref.Get_totals().insert("O", 0); + } + else + { + comp_ref.Get_totals().insert("O", coef - rc1 * tot1_o); + tot1_o *= (1 - rc1); + } + } + } + /* transfer other elements... */ + j_max = 0; /* if j_max turns true, reallocate the exchange structure */ + for (j = 0; j < count_m_s; j++) + { + // Make sure list includes each element + comp_ref.Get_totals().add(m_s[j].name, 0); + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for (; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp(m_s[j].name, elt_ptr->name) != 0) + continue; + + /* rc1 part goes to exchange species... */ + if (coef < rc1 * m_s[j].tot1) + { + m_s[j].tot1 -= coef; + comp_ref.Get_totals().insert(m_s[j].name, 0); + } + else + { + comp_ref.Get_totals().insert(m_s[j].name, coef - rc1 * m_s[j].tot1); + m_s[j].tot1 *= (1 - rc1); + } + } + } + } + } + if (icell < count_cells || stagnant) + { + size_t k; + for (k = 0; k < ex_ptr2->Get_exchange_comps().size(); k++) + { + cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + i_max = 0; + for (; it != nd.end(); it++) + { + if (strcmp("X", it->first.c_str()) == 0) + i_max = 1; + } + if (i_max) + break; + } + if (k < ex_ptr2->Get_exchange_comps().size()) + { + cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + /* transfer O and H... */ + for (; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + + if (strcmp("H", elt_ptr->name) == 0) + { + if (coef < -rc2 * tot2_h) + { + tot2_h += coef; + comp_ref.Get_totals().insert("H", 0); + } + else + { + comp_ref.Get_totals().insert("H", coef + rc2 * tot2_h); + tot2_h *= (1 - rc2); + } + } + else if (strcmp("O", elt_ptr->name) == 0) + { + if (coef < -rc2 * tot2_o) + { + tot2_o += coef; + comp_ref.Get_totals().insert("O", 0); + } + else + { + comp_ref.Get_totals().insert("O", coef + rc2 * tot2_o); + tot2_o *= (1 - rc2); + } + } + } + /* transfer other elements... */ + for (j = 0; j < count_m_s; j++) + { + // Make sure list includes each element + comp_ref.Get_totals().add(m_s[j].name, 0); + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for (; it != nd.end(); it++) + { + class element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp(m_s[j].name, elt_ptr->name) != 0) + continue; + + /* rc2 part goes to exchange species... */ + if (coef < -rc2 * m_s[j].tot2) + { + m_s[j].tot2 += coef; + comp_ref.Get_totals().insert(m_s[j].name, 0); + } + else + { + comp_ref.Get_totals().insert(m_s[j].name, coef + rc2 * m_s[j].tot2); + m_s[j].tot2 *= (1 - rc2); + } + } + } + } + } + } + /* do not transport charge imbalance */ + //ct[icell].J_ij_sum = 0; + //V_M = (struct V_M *) free_check_null(V_M); + if (il_calcs) + ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il); + return (il_calcs); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +disp_surf(LDBLE DDt) +/* ---------------------------------------------------------------------- */ +/* +* Disperse/diffuse surfaces: +* surface[n1] = SS(mixf * surface[n2]) + (1 - SS(mixf) * surface[i1]) +* where SS is Sum of, f2 is a mixing factor. +* Implementation: +* Mobile surface comps and charges are mixed face by face and 1 by 1 in surface[n1]: +Step (from cell 1 to count_cells + 1): +* 1. Surface n1 is made a copy of cell[i1]'s surface, if it exists, or else +* b. a copy of the first encountered mobile surface[n2] from cell i2 = i1 - 1. +* 2 a. Column surfaces are mixed by dispersion/diffusion +* b. Column surfaces are mixed by MCD (if multi_Dflag is true) +* 3. Surfaces n1 and n2 are stored in a temporary surface, with n_user = i1 or i2. +* 4. When done for all cells, new surfaces are copied into the cells. +* NOTE... The surfaces' diffuse_layer, edl and phases/kinetics relations must be identical, +* but only mobile surface_comp's (Dw > 0) and their surface_charge's are transported. +*/ +{ + int i, i1, i2, k, k1/*, n1, n2*/; + int charge_done, surf1, surf2; + LDBLE f1, f2, mixf, mixf_store, mcd_mixf; + LDBLE lav, A_ij, por, Dp1, Dp2; + cxxMix * mix_ptr; + cxxSurface *surface_ptr1, *surface_ptr2; + LDBLE viscos_f; + /* + * temperature and viscosity correction for MCD coefficient, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + */ + viscos_f = viscos_0; + viscos_f = tk_x * viscos_0_25 / (298.15 * viscos_f); + + //n1 = 0; + //n2 = n1 + 1; + cxxSurface surface_n1, surface_n2; + cxxSurface *surface_n2_ptr; + + std::map Rxn_temp_surface_map; + + for (i1 = 1; i1 <= count_cells + 1; i1++) + { + if (i1 <= count_cells && cell_data[i1].por < multi_Dpor_lim) + continue; + + if (i1 == 1 && bcon_first != 1) + continue; + if (i1 == count_cells + 1 && bcon_last != 1) + continue; + + i2 = i1 - 1; + if (i2 > 0 && cell_data[i2].por < multi_Dpor_lim) + continue; + /* + * step 1. define surface n1 from cell i1, if it exists... + */ + surface_n1.Set_n_user(-99); + surface_n2.Set_n_user(-99); + + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1); + if (surface_ptr1 != NULL) + { + surface_n1 = *surface_ptr1; + } + + surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2); + surf1 = surf2 = 0; + if (surface_ptr2 != NULL) + { + if (surface_ptr2->Get_transport()) + surf2 = 1; + } + if (surface_ptr1 != NULL) + { + if (surface_ptr1->Get_transport()) + surf1 = 1; + } + + mixf = mixf_store = 0; + if (surf1 || surf2) + { + /* + * step 2a. Dispersive mixing of mobile surfaces from column cells i2 < i1... + */ + if (i1 <= count_cells) + mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, i1); + else + mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, count_cells); + + std::vector num; + std::vector frac; + mix_ptr->Vectorize(num, frac); + for (size_t i3 = 0; i3 < num.size(); i3++) + { + if (i1 <= count_cells) + { + if (num[i3] == i2) + { + mixf = mixf_store = frac[i3]; + break; + } + } + else if (num[i3] == i1) + { + mixf = mixf_store = frac[i3]; + break; + } + } + + /* step 1b. If cell i1 has no surface, get the mobile comps from surface i2... */ + if (surface_n1.Get_n_user() == -99) + { + surface_n1 = mobile_surface_copy(surface_ptr2, i1, false); + /* limit charges to 1... */ + if (surface_n1.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name(); + surface_n1 = sum_surface_comp(&surface_n1, 0, + &surface_n1, charge_name, 1, surface_n1.Get_surface_comps()[0].Get_Dw()); + } + f1 = 0; + } + else + f1 = 1; + /* find the (possibly modified) surface in the previous cell i2... */ + f2 = 1; + if (i2 > 0 || bcon_first == 1) + { + surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2); + if (surface_n2_ptr != NULL) + { + surface_n2 = *surface_n2_ptr; + } + + /* if not found... */ + else + { + /* copy it from surface_ptr2... */ + if (surface_ptr2 != NULL) + { + surface_n2 = *surface_ptr2; + surface_n2.Set_n_user_both(i2); + } + else + { + /* or make it a mobile copy of the surface in cell i1... */ + surface_n2 = mobile_surface_copy(surface_ptr1, i2, false); + /* limit charges to 1... */ + if (surface_n2.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name(); + surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1, + surface_n2.Get_surface_comps()[0].Get_Dw()); + } + f2 = 0; + } + } + } + + /* + * For MCD, step 2b. Find physical dimensions and porosity of the cells... + */ + if (i1 == 1) + { + por = cell_data[1].por; + lav = cell_data[1].length / 2; + A_ij = Utilities::Rxn_find(Rxn_solution_map, 1)->Get_mass_water() / cell_data[1].length; + } + else if (i1 == count_cells + 1) + { + por = cell_data[count_cells].por; + lav = cell_data[count_cells].length / 2; + A_ij = + Utilities::Rxn_find(Rxn_solution_map, count_cells)->Get_mass_water() / + cell_data[count_cells].length; + } + else + { + por = cell_data[i2].por; + lav = + (cell_data[i1].length + cell_data[i2].length) / 2; + A_ij = + Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water() / + (cell_data[i1].length * cell_data[i1].por); + A_ij += + Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water() / + (cell_data[i2].length * cell_data[i2].por); + A_ij /= 2; + A_ij *= + (cell_data[i1].por < + cell_data[i2].por ? cell_data[i1].por : cell_data[i2].por); + } + + /* mix in comps with the same charge structure... */ + if (surf2) + { + for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* Define the MCD diffusion coefficient... */ + mcd_mixf = 0; + if (multi_Dflag) + { + Dp2 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn); + Dp1 = 0; + if (surface_ptr1 != NULL && surface_ptr1->Get_transport()) + { + for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp1 = + comp_k1_ptr->Get_Dw() * + pow(cell_data[i1].por, multi_Dn); + break; + } + } + if (Dp1 > 0) + Dp2 = (Dp2 + Dp1) / 2; + + /* and the mixing factor... */ + mcd_mixf = Dp2 * viscos_f * A_ij * DDt / lav; + } + mixf = mixf_store + mcd_mixf; + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + if (i1 <= count_cells) + { + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + } + if (i2 > 0) + { + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + } + surface_n1.Set_n_user_both(i1); + } + } + if (surf1) + { + for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++) + { + cxxSurfaceComp * comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp * comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* Define the MCD diffusion coefficient... */ + mcd_mixf = 0; + if (multi_Dflag) + { + if (i1 <= count_cells) + { + Dp1 = + comp_k_ptr->Get_Dw() * + pow(cell_data[i1].por, multi_Dn); + } + else + { + Dp1 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn); + } + Dp2 = 0; + if (surface_ptr2 != NULL && surface_ptr2->Get_transport()) + { + for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp * comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp2 = comp_k1_ptr->Get_Dw() * pow(por, multi_Dn); + break; + } + } + if (Dp2 > 0) + Dp1 = (Dp1 + Dp2) / 2; + + /* and the mixing factor... */ + mcd_mixf = Dp1 * viscos_f * A_ij * DDt / lav; + } + mixf = mixf_store + mcd_mixf; + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + if (i2 > 0) + { + surface_n2 = sum_surface_comp + (&surface_n2, f2, surface_ptr1, charge_name, mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + } + if (i1 <= count_cells) + { + surface_n1 = sum_surface_comp + (&surface_n1, f1, surface_ptr1, charge_name, -mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + } + surface_n2.Set_n_user_both(i2); + } + } + } + + /* + * Step 3. copy surface[n1] and [n2] in a new temporary surface... + */ + if (surface_n1.Get_n_user() == -99) + continue; + + Rxn_temp_surface_map[i1] = surface_n1; + { + cxxSurface t; + surface_n1 = t; + } + surface_n1.Set_n_user_both(-99); + + if (surface_n2.Get_n_user() == i2) + { + surface_n2.Set_n_user_both(i2); + Rxn_temp_surface_map[i2] = surface_n2; + { + cxxSurface t; + surface_n2 = t; + } + surface_n2.Set_n_user_both(-99); + } + } + /* + * Step 4. Dispersion/diffusion is done. New surfaces can be copied in the cell's surface... + */ + //n2 = 0; + std::map::iterator jit = Rxn_temp_surface_map.begin(); + for (; jit != Rxn_temp_surface_map.end(); jit++) + { + i = jit->first; + assert(i == jit->second.Get_n_user()); + if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1)) + { + continue; + } + if (i >= 0 && i <= 1 + count_cells * (1 + stag_data.count_stag)) + { + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + if (surface_ptr1 != NULL) + { + Rxn_surface_map[i] = jit->second; + } + else + { + //Add to map + Rxn_surface_map[i] = jit->second; + } + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +cxxSurface Phreeqc:: +sum_surface_comp(cxxSurface *source1, LDBLE f1, cxxSurface *source2, +std::string charge_name, LDBLE f2, LDBLE new_Dw) +/* ---------------------------------------------------------------------- */ +{ + /* + * Takes fraction f1 of the 1st surface, adds fraction f2 of the 2nd surface's comps[k] and its charge. + * The result goes in target + */ + int new_n_user; + cxxSurface *surface_ptr1, *surface_ptr2; + std::string token; + /* + * Find surfaces + */ + surface_ptr1 = source1; + if (surface_ptr1 == NULL) + { + error_string = sformatf("Null pointer for surface 1 in sum_surface."); + error_msg(error_string, STOP); + input_error++; + return (ERROR); + } + surface_ptr2 = source2; + /* + * Store data for structure surface + */ + new_n_user = surface_ptr1->Get_n_user(); + cxxSurface temp_surface(*surface_ptr1); + temp_surface.Set_n_user_both(new_n_user); + temp_surface.Set_description("Copy"); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_n_solution(-99); + /* + * Multiply component compositions by f1 + */ + temp_surface.multiply(f1); + /* + * Add in surface_ptr2 + */ + // Only components with same charge as component k + cxxSurface addee(*surface_ptr2); + addee.Get_surface_comps().clear(); + addee.Get_surface_charges().clear(); + + for (std::vector::iterator it = surface_ptr2->Get_surface_comps().begin(); + it != surface_ptr2->Get_surface_comps().end(); it++) + { + if (it->Get_charge_name() == charge_name) + { + addee.Get_surface_comps().push_back(*it); + } + } + for (std::vector::iterator it = surface_ptr2->Get_surface_charges().begin(); + it != surface_ptr2->Get_surface_charges().end(); it++) + { + if (it->Get_name() == charge_name) + { + addee.Get_surface_charges().push_back(*it); + } + } + + if (f2 == 0) + f2 = 1e-30; + temp_surface.add(addee, f2); + temp_surface.Set_transport(false); + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + if (temp_surface.Get_surface_comps()[i].Get_charge_name() == charge_name) + { + temp_surface.Get_surface_comps()[i].Set_Dw(new_Dw); + } + if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0) + { + temp_surface.Set_transport(true); + } + } + /* + * Finish up + */ + temp_surface.Sort_comps(); + return (temp_surface); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_surfaces(cxxSurface *surface_ptr1, cxxSurface *surface_ptr2) +/* ---------------------------------------------------------------------- */ +{ + /* checks if surfaces can be mixed... + */ + int n_user1, n_user2, return_code; + + return_code = OK; + n_user1 = surface_ptr1->Get_n_user(); + n_user2 = surface_ptr2->Get_n_user(); + + if (surface_ptr1->Get_dl_type() != surface_ptr2->Get_dl_type()) + { + error_string = sformatf( + "Surfaces %d and %d differ in definition of diffuse layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + + if (surface_ptr1->Get_type() != surface_ptr2->Get_type()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of electrical double layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_only_counter_ions() != surface_ptr2->Get_only_counter_ions()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of only counter ions in the diffuse layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_related_phases() != surface_ptr2->Get_related_phases()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of related phases (sites proportional to moles of an equilibrium phase). Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_related_rate() != surface_ptr2->Get_related_rate()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of related rate (sites proportional to moles of a kinetic reactant). Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + + return (return_code); +} +/* ---------------------------------------------------------------------- */ +cxxSurface Phreeqc:: +mobile_surface_copy(cxxSurface *surface_old_ptr, +int n_user_new, bool move_old) +/* ---------------------------------------------------------------------- */ +{ + /* + * Copies mobile comps from surface_old_ptr to surf_ptr1, + * comps and charges with Dw > 0 are moved if move_old == TRUE, else copied. + * NOTE... when all comps are moved, the old surface is deleted and surfaces are sorted again, + * which will modify pointers and surface numbers. + * User number of new surface structure is n_user_new, structure is freed when n_user_new is already defined + */ + cxxSurface temp_surface(*surface_old_ptr); + /* + * Store moving surface's properties in temp_surface + */ + temp_surface.Set_n_user_both(n_user_new); + std::ostringstream desc; + desc << "Surface defined in simulation " << simulation << "."; + temp_surface.Set_description(desc.str().c_str()); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_transport(true); + + + size_t count_comps = surface_old_ptr->Get_surface_comps().size(); + int i1, i2; + i1 = i2 = 0; + temp_surface.Get_surface_comps().clear(); + temp_surface.Get_surface_charges().clear(); + /* see if comps must be moved, Dw > 0 */ + for (size_t i = 0; i < count_comps; i++) + { + cxxSurfaceComp &comp_ref = surface_old_ptr->Get_surface_comps()[i]; + if (comp_ref.Get_Dw() > 0) + { + i1++; + + // copy comp + temp_surface.Get_surface_comps().push_back(comp_ref); + + // copy charge, if needed + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(comp_ref.Get_charge_name()); + if (charge_ptr == NULL) + { + i2++; + cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name()); + temp_surface.Get_surface_charges().push_back(*old_charge_ptr); + } + } + } + + if (i1 > 0) + { + /* OK, store moved parts from old surface, but first: + get immobile surface comps from new surface... */ + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user_new); + if (surf_ptr != NULL) + { + for (size_t k = 0; k < surf_ptr->Get_surface_comps().size(); k++) + { + cxxSurfaceComp &comp_ref = surf_ptr->Get_surface_comps()[k]; + if (comp_ref.Get_Dw() > 0) + continue; + bool charge_done(false); + for (size_t k1 = 0; k1 < k; k1++) + { + if (surf_ptr->Get_surface_comps()[k1].Get_charge_name() == + comp_ref.Get_charge_name()) + { + charge_done = true; + break; + } + } + if (charge_done) + continue; + temp_surface = sum_surface_comp(&temp_surface, 1, surf_ptr, comp_ref.Get_charge_name(), 1, 0); + } + } + // Return value is temp_surface + temp_surface.Set_n_user_both(n_user_new); + } + + /* delete moved parts from old surface */ + if (move_old && i1 > 0) + { + cxxSurface replace_old(temp_surface); + int n_user_old = surface_old_ptr->Get_n_user(); + if ((size_t) i1 != count_comps) + { + /* redefine old surface with only unmovable comps (Dw = 0) */ + /* other temp_surface members were set above */ + replace_old.Set_n_user_both(n_user_old); + replace_old.Set_transport(false); + replace_old.Get_surface_comps().clear(); + replace_old.Get_surface_charges().clear(); + + i1 = i2 = 0; + for (size_t i = 0; i < count_comps; i++) + { + if (surface_old_ptr->Get_surface_comps()[i].Get_Dw() == 0) + { + i1++; + // copy surface comp + cxxSurfaceComp & comp_ref = surface_old_ptr->Get_surface_comps()[i]; + replace_old.Get_surface_comps().push_back(comp_ref); + cxxSurfaceCharge *charge_ptr = replace_old.Find_charge(comp_ref.Get_charge_name()); + + // copy surface charge if necessary + if (charge_ptr == NULL) + { + i2++; + cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name()); + replace_old.Get_surface_charges().push_back(*old_charge_ptr); + } + } + } + if (replace_old.Get_surface_comps().size() == 0) + { + Rxn_surface_map.erase(surface_old_ptr->Get_n_user()); + } + else + { + replace_old.Sort_comps(); + Rxn_surface_map[surface_old_ptr->Get_n_user()] = replace_old; + } + } + } + temp_surface.Sort_comps(); + return (temp_surface); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +diff_stag_surf(int mobile_cell) +/* ---------------------------------------------------------------------- */ +/* +* Diffuse stagnant and mobile surfaces, following the steps of disp_surf. +* First the mobile/stagnant surfaces are mixed, then the stagnant surfaces +* when not already done. +* If mixing factors among the cells are defined expicitly, it is assumed that +* mixing with a lower numbered cell was done when that cell was processed: +* for any cell in MCD, need only include the mixing factors for higher numbered cells. +*/ +{ + int i, i1, i2, k, k1, /*n1, n2,*/ ns; + int charge_done, surf1, surf2; + LDBLE f1, f2, mixf, mixf_store; + LDBLE Dp1, Dp2; + cxxMix *mix_ptr; + cxxSurface *surface_ptr1, *surface_ptr2; + LDBLE viscos_f; + /* + * temperature and viscosity correction for MCD coefficient, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + */ + viscos_f = viscos_0; + viscos_f = tk_x * viscos_0_25 / (298.15 * viscos_f); + + cxxSurface surface_n1, surface_n2; + cxxSurface *surface_n1_ptr = &surface_n1; + cxxSurface *surface_n2_ptr; + std::map Rxn_temp_surface_map; + + for (ns = 0; ns < stag_data.count_stag; ns++) + { + + i1 = mobile_cell + 1 + ns * count_cells; + if (ns == 0) + i1--; + + if (cell_data[i1].por < multi_Dpor_lim) + continue; + surface_n1.Set_n_user_both(-99); + surface_n2.Set_n_user_both(-99); + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1); + /* + * step 2a. mix surfaces... + */ + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, i1); + if (mix_ptr == NULL) + continue; + + std::vector num; + std::vector frac; + mix_ptr->Vectorize(num, frac); + for (size_t i3 = 0; i3 < num.size(); i3++) + { + if ((i2 = num[i3]) <= i1) + continue; + if (cell_data[i2].por < multi_Dpor_lim) + continue; + surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2); + surf1 = surf2 = 0; + if (surface_ptr2 != NULL) + { + if (surface_ptr2->Get_transport()) + surf2 = 1; + } + if (surface_ptr1 != NULL) + { + if (surface_ptr1->Get_transport()) + surf1 = 1; + } + if (!surf1 && !surf2) + continue; + mixf = mixf_store = frac[i3];; + + /* find the (possibly modified) surface in cell i1... */ + f1 = 1; + surface_n1_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i1); + if (surface_n1_ptr != NULL) + { + surface_n1 = *surface_n1_ptr; + //n1 = 0; + } + /* if not found... */ + else + { + /* copy it from surface_ptr1... */ + if (surface_ptr1 != NULL) + { + surface_n1 = *surface_ptr1; + } + else + { + /* or make it a mobile copy of the surface in cell i2... */ + surface_n1 = mobile_surface_copy(surface_ptr2, i1, false); + + /* limit comps to 1... */ + if (surface_n1.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name(); + surface_n1 = sum_surface_comp(&surface_n1, 0, &surface_n1, charge_name, 1, + surface_n1.Get_surface_comps()[0].Get_Dw()); + } + f1 = 0; + } + } + /* find the (possibly modified) surface in cell i2... */ + f2 = 1; + surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2); + + if (surface_n2_ptr != NULL) + { + surface_n2 = *surface_n2_ptr; + } + /* if not found... */ + else + { + //n2 = 1; + /* copy it from surface_ptr2... */ + if (surface_ptr2 != NULL) + { + surface_n2 = *surface_ptr2; + } + else + { + /* or make it a mobile copy of the surface in cell i1... */ + surface_n2 = mobile_surface_copy(surface_ptr1, i2, false); + /* limit comps to 1... */ + if (surface_n2.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name(); + surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1, + surface_n2.Get_surface_comps()[0].Get_Dw()); + } + f2 = 0; + } + } + + /* For MCD, step 2b. Adapt mixf to default values... */ + if (multi_Dflag) + { + mixf_store *= + (cell_data[i1].por <= cell_data[i2].por ? cell_data[i1].por : cell_data[i2].por); + mixf_store /= (default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor); + } + + /* mix in comps with the same charge structure... */ + if (surf2) + { + for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* find diffusion coefficients of surfaces... */ + if (multi_Dflag) + { + Dp2 = comp_k_ptr->Get_Dw() * + pow(cell_data[i2].por, multi_Dn) * viscos_f; + Dp1 = 0; + if (surf1) + { + for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp1 = comp_k1_ptr->Get_Dw() * pow(cell_data[i1].por, multi_Dn) * viscos_f; + break; + } + } + if (Dp1 > 0) + Dp2 = (Dp2 + Dp1) / 2; + + /* and adapt the mixing factor... */ + mixf = mixf_store * Dp2; + mixf /= Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water(); + } + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + } + } + + if (surf1) + { + for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* find diffusion coefficients of surfaces... */ + if (multi_Dflag) + { + Dp1 = comp_k_ptr->Get_Dw() * pow(cell_data[i1].por, multi_Dn) * viscos_f; + + Dp2 = 0; + if (surf2) + { + for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp2 = comp_k1_ptr->Get_Dw() * pow(cell_data[i2].por, multi_Dn) * viscos_f; + break; + } + } + if (Dp2 > 0) + Dp1 = (Dp1 + Dp2) / 2; + + /* and adapt the mixing factor... */ + mixf = mixf_store * Dp1; + mixf /= Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water(); + } + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr1, charge_name, mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr1, charge_name, -mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + } + } + + /* + * Step 3. copy surface[n1] and [n2] in a new temporary surface... + */ + if (surface_n1.Get_n_user() == -99) + continue; + + surface_n1.Set_n_user_both(i1); + Rxn_temp_surface_map[i1] = surface_n1; + + assert(surface_n2.Get_n_user() != -99); + assert(surface_n2.Get_n_user() == i2); + surface_n2.Set_n_user_both(i2); + Rxn_temp_surface_map[i2] = surface_n2; + } + } + /* + * Step 4. Diffusion is done. New surfaces can be copied in the cells... + */ + //n2 = 0; + std::map::iterator jit = Rxn_temp_surface_map.begin(); + for (; jit != Rxn_temp_surface_map.end(); jit++) + { + i = jit->first; + assert(i == jit->second.Get_n_user()); + if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1)) + { + continue; + } + if (i >= 0 && i <= 1 + count_cells * (1 + stag_data.count_stag)) + { + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + //if (surface_ptr1 != NULL) + //{ + // Rxn_surface_map[i] = jit->second; + //} + //else + { + //Add to map + Rxn_surface_map[i] = jit->second; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reformat_surf(const char *comp_name, LDBLE fraction, const char *new_comp_name, +LDBLE new_Dw, int l_cell) +/* ---------------------------------------------------------------------- */ +{ + cxxSurface *surface_ptr; + + if ((surface_ptr = Utilities::Rxn_find(Rxn_surface_map, l_cell)) == NULL) + return (OK); + if (surface_ptr->Find_charge(comp_name) == NULL) + return (OK); + // Assume comp_name is charge name + std::string old_charge_name = comp_name; + std::string new_charge_name = new_comp_name; + if (fraction > 0.99999999) + fraction = 0.99999999; + + cxxSurface temp_surface(*surface_ptr); + + cxxSurface change; + + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]); + std::string charge_name = comp_ptr->Get_charge_name(); + if (charge_name == old_charge_name) + { + cxxSurfaceComp comp(*comp_ptr); + comp.multiply(fraction); + std::string std_comp_name = comp_ptr->Get_formula(); + Utilities::replace(comp_name, new_comp_name, std_comp_name); + comp.Set_formula(std_comp_name.c_str()); + comp.Set_charge_name(new_comp_name); + cxxNameDouble nd; + cxxNameDouble::iterator it; + for (it = comp.Get_totals().begin(); it != comp.Get_totals().end(); it++) + { + std::string tot_name(it->first); + Utilities::replace(comp_name, new_comp_name, tot_name); + nd[tot_name] = it->second; + } + comp.Set_totals(nd); + change.Get_surface_comps().push_back(comp); + comp_ptr->multiply(1 - fraction); + } + } + for (size_t i = 0; i < temp_surface.Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(temp_surface.Get_surface_charges()[i]); + std::string charge_name = charge_ptr->Get_name(); + if (charge_name == old_charge_name) + { + cxxSurfaceCharge charge(*charge_ptr); + charge.multiply(fraction); + std::string std_charge_name = charge_ptr->Get_name(); + Utilities::replace(comp_name, new_comp_name, std_charge_name); + charge.Set_name(std_charge_name.c_str()); + change.Get_surface_charges().push_back(charge); + charge_ptr->multiply(1 - fraction); + } + } + temp_surface.add(change, 1.0); + // change Dw + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]); + std::string charge_name = comp_ptr->Get_charge_name(); + if (charge_name == new_charge_name) + { + comp_ptr->Set_Dw(new_Dw); + } + } + temp_surface.Set_transport(false); + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0) + { + temp_surface.Set_transport(true); + break; + } + } + temp_surface.Sort_comps(); + Rxn_surface_map[l_cell] = temp_surface; + return OK; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +viscosity(void) +/* ---------------------------------------------------------------------- */ +{ + + /* from Atkins, 1994. Physical Chemistry, 5th ed. */ + //viscos = + // pow((LDBLE) 10., + // -(1.37023 * (tc_x - 20) + + // 0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x)); + /* Huber et al., 2009, J. Phys. Chem. Ref. Data, Vol. 38, 101-125 */ + LDBLE H[4] = { 1.67752, 2.20462, 0.6366564, -0.241605 }; + LDBLE Tb = tk_x / 647.096, denom = H[0], mu0; + int i, j, i1; + for (i = 1; i < 4; i++) + denom += H[i] / pow(Tb, i); + mu0 = 100.0 * sqrt(Tb) / denom; + + LDBLE H2[42] = + { 5.20094e-1, 2.22531e-1, -2.81378e-1, 1.61913e-1, -3.25372e-2, 0, 0, + 8.50895e-2, 9.99115e-1, -9.06851e-1, 2.57399e-1, 0, 0, 0, + -1.08374, 1.88797, -7.72479e-1, 0, 0, 0, 0, + -2.89555e-1, 1.26613, -4.89837e-1, 0, 6.98452e-2, 0, -4.35673e-3, + 0, 0, -2.57040e-1, 0, 0, 8.72102e-3, 0, + 0, 1.20573e-1, 0, 0, 0, 0, -5.93264e-4 }; + LDBLE Rb = rho_0 / 0.322, Tb_1, Rb_1, S1 = 0.0, S2, mu1; + Tb_1 = 1.0 / Tb - 1.0; Rb_1 = Rb - 1.0; + for (i = 0; i < 6; i++) + { + S2 = 0.0; + for (j = 0; j < 7; j++) + { + i1 = 7 * i; + if (!H2[i1 + j]) + continue; + if (j) + S2 += H2[i1 + j] * pow(Rb_1, j); + else + S2 += H2[i1]; + } + if (i) + S1 += S2 * pow(Tb_1, i); + else + S1 += S2; + } + mu1 = exp(Rb * S1); + viscos_0 = viscos = mu0 * mu1 / 1e3; + viscos_0_25 = 0.8900239182946; + //#define OLD_VISCOSITY +#ifdef OLD_VISCOSITY + /* from Atkins, 1994. Physical Chemistry, 5th ed. */ + viscos = + pow((LDBLE) 10., + -(1.37023 * (tc_x - 20) + + 0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x)); + viscos_0_25 = 0.88862; +#endif + //return viscos; + if (!print_viscosity) + return viscos; + + /* (modified) Jones-Dole eqn for viscosity: + viscos / viscos_0 = + 1 + A * eq_tot^0.5 + + f_an * (Sum(B_i * m_i) + + Sum(D_i * m_i * ((1 + f_I) * mu_x^d3_i + (m_i * f_z)^d3_i) / (2 + f_I))) + A calculated from Falkenhagen-Dole + B_i = b0 + b1*exp(b2 * tc), b0..2 in Jones_Dole[0..2], read in SOLUTION_SPECIES + D_i = d1 * exp(d2 * tc), d1, 2 in Jones_Dole[3, 4] + d3_i in Jones_Dole[5] + Jones_Dole[6] contains the anion factor, 1 for Cl-, variable for other anions + f_z = (z * z + |z|) / 2, the contribution of the ion to mu_x, if z = 0: f_z = mu_x / m_i + f_I = variable, depends on d3_i > 1, or d3_i < 1. + tc is limited to 200 C. + + + A from Falkenhagen-Dole for a salt: + A = 4.3787e-14 * TK**1.5 / (eps_r**0.5)* (z1 + z2)**-0.5 / (D1 * D2) * psi + psi = (D1*z2 + D2*z1)/4 - z1*z2 * (D1-D2)**2 / ((D1*z1 + D2*z2)**0.5 + ((D1 + D2) * (z1 + z2))**0.5)**2 + D1, z1 for the cation, D2, |z2| for the anion of the salt. + We use the harmonic mean of the Dw's, and the arithmetic mean of the z's, + both weighted by the equivalent concentration. + */ + LDBLE D1, D2, z1, z2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, t1, t2, ta; + LDBLE A, psi, Bc = 0, Dc = 0, Dw = 0.0, l_z, f_z, lm, V_an, m_an, V_Cl, tc; + + m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = V_an = m_an = V_Cl = ta = 0; + + tc = (tc_x > 200) ? 200 : tc_x; + + for (i = 0; i < (int)this->s_x.size(); i++) + { + if (s_x[i]->type != AQ && s_x[i]->type > HPLUS) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if (s_x[i]->Jones_Dole[0] || s_x[i]->Jones_Dole[1] || s_x[i]->Jones_Dole[3]) + { + t1 = s_x[i]->moles / mass_water_aq_x; + l_z = fabs(s_x[i]->z); + if (l_z) + f_z = (l_z * l_z + l_z) / 2; + else + f_z = mu_x / t1; + //if data at tc's other than 25 are scarce, put the values found for 25 C in [7] and [8], optimize [1], [2], and [4]... + if (s_x[i]->Jones_Dole[7] || s_x[i]->Jones_Dole[8]) + { + s_x[i]->Jones_Dole[0] = s_x[i]->Jones_Dole[7] - + s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * 25.0); + s_x[i]->Jones_Dole[3] = + s_x[i]->Jones_Dole[8] / exp(-s_x[i]->Jones_Dole[4] * 25.0); + } + // find B * m and D * m * mu^d3 + Bc += (s_x[i]->Jones_Dole[0] + + s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * tc)) * + t1; + // define f_I from the exponent of the D * m^d3 term... + if (s_x[i]->Jones_Dole[5] >= 1) + t2 = mu_x / 3 / s_x[i]->Jones_Dole[5]; + else if (s_x[i]->Jones_Dole[5] > 0.4) + t2 = -0.8 / s_x[i]->Jones_Dole[5]; + else + t2 = -1; + Dc += (s_x[i]->Jones_Dole[3] * exp(-s_x[i]->Jones_Dole[4] * tc)) * + t1 * (pow(mu_x, s_x[i]->Jones_Dole[5])*(1 + t2) + pow(t1 * f_z, s_x[i]->Jones_Dole[5])) / (2 + t2); + //output_msg(sformatf("\t%s\t%e\t%e\t%e\n", s_x[i]->name, t1, Bc, Dc )); + } + // parms for A... + if ((l_z = s_x[i]->z) == 0) + continue; + Dw = s_x[i]->dw; + if (Dw) + { + Dw *= (0.89 / viscos_0 * tk_x / 298.15); + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); + } + if (l_z < 0) + { + if (!strcmp(s_x[i]->name, "Cl-")) + // volumina for f_an... + { + V_Cl = s_x[i]->logk[vm_tc]; + V_an += V_Cl * s_x[i]->moles; + ta += s_x[i]->moles; + m_an += s_x[i]->moles; + } + else// if (s_x[i]->Jones_Dole[6]) + { + V_an += s_x[i]->logk[vm_tc] * s_x[i]->Jones_Dole[6] * s_x[i]->moles; + ta += s_x[i]->moles; + m_an += s_x[i]->moles; + } + if (Dw) + { + // anions for A... + m_min += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_min -= t1; + eq_dw_min -= t1 / Dw; + } + } + else if (Dw) + { + // cations for A... + m_plus += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_plus += t1; + eq_dw_plus += t1 / Dw; + } + } + if (m_plus && m_min && eq_dw_plus && eq_dw_min) + { + z1 = eq_plus / m_plus; z2 = eq_min / m_min; + D1 = eq_plus / eq_dw_plus; D2 = eq_min / eq_dw_min; + + t1 = (D1 - D2) / (sqrt(D1 * z1 + D2 * z2) + sqrt((D1 + D2) * (z1 + z2))); + psi = (D1 * z2 + D2 * z1) / 4.0 - z1 * z2 * t1 * t1; + // Here A is A * viscos_0, avoids multiplication later on... + A = 4.3787e-14 * pow(tk_x, 1.5) / (sqrt(eps_r * (z1 + z2) / ((z1 > z2) ? z1 : z2)) * (D1 * D2)) * psi; + } + else + A = 0; + viscos = viscos_0 + A * sqrt((eq_plus + eq_min) / 2 / mass_water_aq_x); + if (m_an) + { + V_an /= m_an; + ta /= m_an; + } + if (!V_Cl) + V_Cl = calc_vm_Cl(); + if (V_an && V_Cl && ta) + viscos += (viscos_0 * (2 - ta * V_an / V_Cl) * (Bc + Dc)); + else + viscos += (viscos_0 * (Bc + Dc)); + if (viscos < 0) + { + viscos = 0; + warning_msg("viscosity < 0, reset to 0."); + } + return viscos; + +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_vm_Cl(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Calculate molar volume of Cl- with a Redlich type eqn: + Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4). + * Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2 + * for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221. + Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av. + If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5). + DH_Av and DH_B are from calc_dielectrics(tc, pa). + * coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228). + * b4 = logk[vmi4], or + * coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2 + */ + LDBLE V_Cl = 0; + LDBLE pb_s = 2600. + patm_x * 1.01325, TK_s = tc_x + 45.15, sqrt_mu = sqrt(mu_x); + class species *s_ptr; + + s_ptr = s_search("Cl-"); + if (!s_ptr) + return V_Cl; + + if (s_ptr->logk[vma1]) + { + /* supcrt volume at I = 0... */ + V_Cl = s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s + + (s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s - + s_ptr->logk[wref] * QBrn; + + /* the ionic strength term * I^0.5... */ + if (s_ptr->logk[b_Av] < 1e-5) + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + /* pitzer... */ + //s_ptr->rxn_x.logk[vm_tc] += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + // log(1 + s_ptr->logk[b_Av] * sqrt(mu_x)) / s_ptr->logk[b_Av]; + /* extended DH... */ + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0) + { + LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s; + if (s_ptr->logk[vmi4] == 1.0) + V_Cl += bi * mu_x; + else + V_Cl += bi * pow(mu_x, s_ptr->logk[vmi4]); + } + } + else if (s_ptr->millero[0]) + { + /* Millero volume at I = 0... */ + V_Cl = s_ptr->millero[0] + tc_x * (s_ptr->millero[1] + tc_x * s_ptr->millero[2]); + if (s_ptr->z) + { + /* the ionic strength terms... */ + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu + + (s_ptr->millero[3] + tc_x * (s_ptr->millero[4] + tc_x * s_ptr->millero[5])) * mu_x; + } + } + return V_Cl; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +flux_mcd(const char* species_name, int option) +/* ---------------------------------------------------------------------- */ +{ + class species* s_ptr; + double f = 0.0, dum = 0.0; + if (state == TRANSPORT && multi_Dflag) + { + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS) + { + int n = cell_no; + std::map >::iterator + j_map_iter = cell_J_ij.find(n); + if (j_map_iter != cell_J_ij.end()) + { + std::map::iterator + j_save_iter = j_map_iter->second.find(species_name); + if (j_save_iter != j_map_iter->second.end()) + { + if (option == 1) + { + f = j_save_iter->second.flux_t; + } + else if (option == 2) + { + f = j_save_iter->second.flux_c; + } + } + } + } + } + return (f); +} diff --git a/phreeqcpp/utilities.cpp b/phreeqcpp/utilities.cpp new file mode 100644 index 00000000..d7989864 --- /dev/null +++ b/phreeqcpp/utilities.cpp @@ -0,0 +1,1463 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "NameDouble.h" +#include "Exchange.h" +#include "Solution.h" +#include + +#if defined(PHREEQCI_GUI) +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif +#endif + +/* ---------------------------------------------------------------------- */ +double Phreeqc:: +calc_alk(CReaction& rxn_ref) +/* ---------------------------------------------------------------------- */ +{ + LDBLE return_value; + class master* master_ptr; + + return_value = 0.0; + class rxn_token* r_token = &rxn_ref.token[1]; + while (r_token->s != NULL) + { + master_ptr = r_token->s->secondary; + if (master_ptr == NULL) + { + master_ptr = r_token->s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Non-master species in secondary reaction, %s.", + rxn_ref.token[0].s->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + return_value += r_token->coef * master_ptr->alk; + r_token++; + } + return (return_value); +}/* ---------------------------------------------------------------------- */ +double Phreeqc:: +calc_delta_v(CReaction& r_ref, bool phase) +/* ---------------------------------------------------------------------- */ +{ + /* calculate delta_v from molar volumes */ + double d_v = 0.0; + if (phase) + { + /* for phases: reactants have coef's < 0, products have coef's > 0, v.v. for species */ + for (size_t i = 1; r_ref.Get_tokens()[i].s; i++) + { + if (!r_ref.Get_tokens()[i].s) + continue; + d_v += r_ref.Get_tokens()[i].coef * r_ref.Get_tokens()[i].s->logk[vm_tc]; + } + } + else + { + for (size_t i = 0; r_ref.token[i].name /*|| r_ptr->token[i].s*/; i++) + { + if (!r_ref.Get_tokens()[i].s) + continue; + d_v -= r_ref.Get_tokens()[i].coef * r_ref.Get_tokens()[i].s->logk[vm_tc]; + } + } + return d_v; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_dielectrics(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ + /* Relative dielectric constant of pure water, eps as a function of (P, T) + Bradley and Pitzer, 1979, JPC 83, 1599. + (newer data in Fernandez et al., 1995, JPCRD 24, 33, + and Fernandez et al., 1997, JPCRD 26, 1125, show its correctness) + + d(eps)/d(P), Debye-Hueckel A and B, and Av (for Av, see Pitzer et al., 1984, JPCRD 13, p. 4) + */ + if (llnl_temp.size() > 0) return OK; + if (tc > 350.) + { + tc = 350.; + } + LDBLE T = tc + 273.15; + LDBLE u1 = 3.4279e2, u2 = -5.0866e-3, u3 = 9.469e-7, u4 = -2.0525, + u5 = 3.1159e3, u6 = -1.8289e2, u7 = -8.0325e3, u8 = 4.2142e6, + u9 = 2.1417; + LDBLE d1000 = u1 * exp(T * (u2 + T * u3)); // relative dielectric constant at 1000 bar + LDBLE c = u4 + u5 / (u6 + T); + LDBLE b = u7 + u8 / T + u9 * T; + LDBLE pb = pa * 1.01325; // pa in bar + eps_r = d1000 + c * log((b + pb) / (b + 1e3)); // relative dielectric constant + if (eps_r <= 0) + { + eps_r = 10.; + warning_msg("Relative dielectric constant is negative.\nTemperature is out of range of parameterization."); + } + + /* qe^2 / (eps_r * kB * T) = 4.803204e-10**2 / 1.38065e-16 / (eps_r * T) + = 1.671008e-3 (esu^2 / (erg/K)) / (eps_r * T) */ + LDBLE e2_DkT = 1.671008e-3 / (eps_r * T); + + DH_B = sqrt(8 * pi * AVOGADRO * e2_DkT * rho_0 / 1e3); // Debye length parameter, 1/cm(mol/kg)^-0.5 + + DH_A = DH_B * e2_DkT / (2. * LOG_10); //(mol/kg)^-0.5 + + /* A0 in pitzer */ + if (pitzer_model || sit_model) + { + A0 = DH_B * e2_DkT / 6.0; + if (pitzer_model && aphi != NULL) + { + calc_pitz_param(aphi, T, 298.15); + A0 = aphi->p; + } + } + + /* Debye-Hueckel limiting slope = DH_B * e2_DkT * RT * (d(ln(eps_r)) / d(P) - compressibility) */ + DH_Av = DH_B * e2_DkT * R_LITER_ATM * 1e3 * T * (c / (b + pb) * 1.01325 / eps_r - kappa_0 / 3.); // (cm3/mol)(mol/kg)^-0.5 + + DH_B /= 1e8; // kappa, 1/Angstrom(mol/kg)^-0.5 + + /* the Born functions, * 41.84 to give molal volumes in cm3/mol... */ + ZBrn = (-1 / eps_r + 1.0) * 41.84004; + QBrn = c / (b + pb) / eps_r / eps_r * 41.84004; + /* dgdP from subroutine gShok2 in supcrt92, g is neglected here (at tc < 300)... + and, dgdP is small. Better, adapt Wref to experimental Vm's */ + dgdP = 0; + //if (tc > 150 && rho_0 < 1.0) + //{ + // LDBLE sc[7] = {1, -0.2037662e+01, 0.5747000e-02, -0.6557892e-05, + // 0.6107361e+01, -0.1074377e-01, 0.1268348e-04}; + // LDBLE csc[4] = {1, 0.3666666e+02, -0.1504956e-9, 0.5017997e-13}; + // LDBLE sa = sc[1] + tc * (sc[2] + tc * sc[3]); + // LDBLE sb = sc[4] + tc * (sc[5] + tc * sc[6]); + + // dgdP = - sa * sb * pow(1.0 - rho_0, sb - 1.0) * rho_0 * kappa_0 / 1.01325; + + // LDBLE ft = pow((tc - 155.0)/300.0, 4.8) + csc[1] * pow((tc - 155.0)/300.0, 16.0); + // LDBLE dfdP = ft * (-3.0 * csc[2] * pow(1000.0 - pb, 2) - 4.0 * csc[3] * pow(1000.0 - pb, 3)); + // dgdP -= dfdP; + //} + + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_rho_0(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ + /* Density of pure water + Wagner and Pruss, 2002, JPCRD 31, 387, eqn. 2.6, along the saturation pressure line + + interpolation 0 - 300 oC, 0.006 - 1000 atm... + */ + if (llnl_temp.size() > 0) return OK; + if (tc > 350.) + { + if (need_temp_msg < 1) + { + std::ostringstream w_msg; + w_msg << "Fitting range for dielectric constant of pure water is 0-350 C.\n"; + w_msg << "Fitting range for density along the saturation pressure line is 0-374 C,\n"; + w_msg << " for higher pressures up to 1000 atm 0-300 C.\n"; + w_msg << "Using temperature of 350 C for dielectric and density calculation."; + warning_msg(w_msg.str().c_str()); + need_temp_msg++; + } + tc = 350.; + } + LDBLE T = tc + 273.15; + //eqn. 2.6... + LDBLE Tc = 647.096, th = 1 - T / Tc; + LDBLE b1 = 1.99274064, b2 = 1.09965342, b3 = -0.510839303, + b4 = -1.75493479, b5 = -45.5170352, b6 = -6.7469445e5; + rho_0_sat = 322.0 * (1.0 + b1 * pow(th, (LDBLE) 1./3.) + b2 * pow(th, (LDBLE) 2./3.) + b3 * pow(th, (LDBLE) 5./3.) +\ + b4 * pow(th, (LDBLE) 16./3.) + b5 * pow(th, (LDBLE) 43./3.) + b6 * pow(th, (LDBLE) 110./3)); + //pressure... + LDBLE p0 = 5.1880000E-02 + tc * (-4.1885519E-04 + tc * ( 6.6780748E-06 + tc * (-3.6648699E-08 + tc * 8.3501912E-11))); + LDBLE p1 = -6.0251348E-06 + tc * ( 3.6696407E-07 + tc * (-9.2056269E-09 + tc * ( 6.7024182E-11 + tc * -1.5947241E-13))); + LDBLE p2 = -2.2983596E-09 + tc * (-4.0133819E-10 + tc * ( 1.2619821E-11 + tc * (-9.8952363E-14 + tc * 2.3363281E-16))); + LDBLE p3 = 7.0517647E-11 + tc * ( 6.8566831E-12 + tc * (-2.2829750E-13 + tc * ( 1.8113313E-15 + tc * -4.2475324E-18))); + /* The minimal pressure equals the saturation pressure... */ + if (ah2o_x <= 1.0) + p_sat = exp(11.6702 - 3816.44 / (T - 46.13)) * ah2o_x; + else + p_sat = exp(11.6702 - 3816.44 / (T - 46.13)); + //ah2o_x0 = ah2o_x; // for updating rho in model(): compare with new ah2o_x + if (pa < p_sat || (use.Get_solution_ptr() && use.Get_solution_ptr()->Get_patm() < p_sat)) + { + pa = p_sat; + } + if (!use.Get_gas_phase_in()) + patm_x = pa; + pa -= (p_sat - 1e-6); + rho_0 = rho_0_sat + pa * (p0 + pa * (p1 + pa * (p2 + sqrt(pa) * p3))); + if (rho_0 < 0.01) + rho_0 = 0.01; + + /* compressibility, d(ln(rho)) / d(P), 1/atm... */ + kappa_0 = (p0 + pa * (2 * p1 + pa * (3 * p2 + sqrt(pa) * 3.5 * p3))) / rho_0; + + return (rho_0 / 1e3); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +compute_gfw(const char *string, LDBLE * gfw) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input: string contains a chemical formula + * Output: gfw contains the calculated gfw + */ + std::string str(string); + std::map::iterator it; + it = gfw_map.find(str); + if (it != gfw_map.end()) + { + *gfw = it->second; + return OK; + } + + int i; + char token[MAX_LENGTH]; + const char* cptr; + + count_elts = 0; + paren_count = 0; + strcpy(token, string); + cptr = token; + if (get_elts_in_species(&cptr, 1.0) == ERROR) + { + return (ERROR); + } + *gfw = 0.0; + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->gfw <= 0.0) + { + return (ERROR); + } + *gfw += elt_list[i].coef * (elt_list[i].elt)->gfw; + } + gfw_map[str] = *gfw; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token(char *token_ptr, const char **cptr, int *length) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **cptr to *token_ptr until first space is encountered. + * + * Arguments: + * *token_ptr output, place to store token + * + * **cptr input, character string to read token from + * output, next position after token + * + * length output, length of token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * UNKNOWN. + */ + int i, return_value; + char c; + +/* + * Read to end of whitespace + */ + while (isspace((int) (c = **cptr))) + (*cptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + i = 0; + while ((!isspace((int) (c = **cptr))) && + /* c != ',' && */ + c != ';' && c != '\0') + { + token_ptr[i] = c; + (*cptr)++; + i++; + } + token_ptr[i] = '\0'; + *length = i; + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token(std::string &token, const char **cptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **cptr to *token until first space is encountered. + * + * Arguments: + * &token_ptr output, place to store token + * + * **cptr input, character string to read token from + * output, next position after token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * UNKNOWN. + */ + int return_value; + char c; + +/* + * Read to end of whitespace + */ + token.clear(); + while (isspace((int) (c = **cptr))) + (*cptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + char c_char[2]; + c_char[1] = '\0'; + while ((!isspace((int) (c = **cptr))) && + /* c != ',' && */ + c != ';' && c != '\0') + { + c_char[0] = c; + token.append(c_char); + (*cptr)++; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dup_print(const char* cptr, int emphasis) +/* ---------------------------------------------------------------------- */ +{ +/* + * print character string to output and logfile + * if emphasis == TRUE the print is set off by + * a row of dashes before and after the character string. + * + */ + int l; + + if (pr.headings == FALSE) + return (OK); + std::string save_in(cptr); + l = (int) strlen(cptr); + if (emphasis == TRUE) + { + std::string dash; + dash.resize(l, '-'); + output_msg(sformatf("%s\n%s\n%s\n\n", dash.c_str(), save_in.c_str(), dash.c_str())); + log_msg(sformatf("%s\n%s\n%s\n\n", dash.c_str(), save_in.c_str(), dash.c_str())); + } + else + { + output_msg(sformatf("%s\n\n", save_in.c_str())); + log_msg(sformatf("%s\n\n", save_in.c_str())); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +equal(LDBLE a, LDBLE b, LDBLE eps) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks equality between two LDBLE precision numbers + */ + if (fabs(a - b) <= eps) + return (TRUE); + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +free_check_null(void *ptr) +/* ---------------------------------------------------------------------- */ +{ + if (ptr != NULL) + { + PHRQ_free(ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_token(const char** eqnaddr, std::string& string, LDBLE* l_z, int* l) +/* ---------------------------------------------------------------------- */ +/* + * Function finds next species in equation, coefficient has already + * been removed. Also determines charge on the species. + * + * Arguments: + * *eqnaddr input, pointer to position in eqn to start parsing + * output, pointer to a pointer to next position in eqn to start + * parsing. + * *string input pointer to place to store token + * *l_z charge on token + * *l length of token + * + * Returns: + * ERROR, + * OK. + */ +{ + int i, j; + int ltoken, lcharge; + char c; + const char* cptr, * ptr1, * rest; + char charge[MAX_LENGTH]; + + string.clear(); + rest = *eqnaddr; + cptr = *eqnaddr; + i = 0; + /* + * Find end of token or begining of charge + */ + while (((c = *cptr) != '+') && (c != '-') && (c != '=') && (c != '\0')) + { + string.push_back(c); + i++; + if (c == '[') + { + cptr++; + while ((c = *cptr) != ']') + { + if (c == '\0') + { + error_string = sformatf( + "No final bracket \"]\" for element name, %s.", + string.c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + string.push_back(c); + i++; + cptr++; + } + string.push_back(c); + i++; + } + + cptr++; + } + ltoken = i; + /* + * Check for an empty string + */ + if (i == 0) + { + error_string = sformatf("NULL string detected in get_token, %s.", rest); + error_msg(error_string, CONTINUE); + return (ERROR); + } + /* + * End of token is = or \0, charge is zero + */ + if (c == '=' || c == '\0') + { + *eqnaddr = cptr; + lcharge = 0; + *l_z = 0.0; + } + else + { + /* + * Copy characters into charge until next species or end is detected + */ + j = 0; + ptr1 = cptr; + while ((isalpha((int)(c = *ptr1)) == FALSE) && + (c != '(') && + (c != ')') && + (c != ']') && (c != '[') && (c != '=') && (c != '\0')) + { + charge[j++] = c; + /* error if no more space */ + if (j >= MAX_LENGTH) + { + error_msg("The charge on a species has exceeded MAX_LENGTH characters.", + CONTINUE); + return (ERROR); + } + ptr1++; + } + /* + * Go back to last + or - if not end of side, + * everything before the last + or - in charge is part of the charge + */ + if ((c != '=') && (c != '\0')) + { + while (((c = *ptr1) != '+') && (c != '-')) + { + j--; + ptr1--; + } + } + charge[j] = '\0'; + lcharge = j; + *eqnaddr = ptr1; + /* + * Charge has been written, now need to check if charge has legal format + */ + if (get_charge(charge, l_z) == OK) + { + string.append(charge); + } + else + { + return (ERROR); + } + } + *l = ltoken + lcharge; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isamong(char c, const char *s_l) +/* ---------------------------------------------------------------------- */ +/* + * Function checks if c is among the characters in the string s + * + * Arguments: + * c input, character to check + * *s string of characters + * + * Returns: + * TRUE if c is in set, + * FALSE if c in not in set. + */ +{ + int i; + + for (i = 0; s_l[i] != '\0'; i++) + { + if (c == s_l[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +islegit(const char c) +/* ---------------------------------------------------------------------- */ +/* + * Function checks for legal characters for chemical equations + * + * Argument: + * c input, character to check + * + * Returns: + * TRUE if c is in set, + * FALSE if c in not in set. + */ +{ + if (isalpha((int) c) || isdigit((int) c) || isamong(c, "+-=().:_[]")) + { + return (TRUE); + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +malloc_error(void) +/* ---------------------------------------------------------------------- */ +{ + error_msg("NULL pointer returned from malloc or realloc.", CONTINUE); + error_msg("Program terminating.", STOP); + return; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +parse_couple(char *token) +/* ---------------------------------------------------------------------- */ +{ +/* + * Parse couple puts redox couples in standard form + * "+" is removed and couples are rewritten in sort + * order. + */ + int e1, e2, p1, p2; + const char* cptr; + std::string elt1, elt2; + char paren1[MAX_LENGTH], paren2[MAX_LENGTH]; + + if (strcmp_nocase_arg1(token, "pe") == 0) + { + str_tolower(token); + return (OK); + } + while (replace("+", "", token) == TRUE); + cptr = token; + get_elt(&cptr, elt1, &e1); + if (*cptr != '(') + { + error_string = sformatf( "Element name must be followed by " + "parentheses in redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + paren_count = 1; + paren1[0] = '('; + p1 = 1; + while (*cptr != '\0') + { + cptr++; + if (*cptr == '/' || *cptr == '\0') + { + error_string = sformatf( + "End of line or " "/" + " encountered before end of parentheses, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + paren1[p1++] = *cptr; + if (*cptr == '(') + paren_count++; + if (*cptr == ')') + paren_count--; + if (paren_count == 0) + break; + } + paren1[p1] = '\0'; + cptr++; + if (*cptr != '/') + { + error_string = sformatf( " " "/" " must follow parentheses " + "ending first half of redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + cptr++; + get_elt(&cptr, elt2, &e2); + if (strcmp(elt1.c_str(), elt2.c_str()) != 0) + { + error_string = sformatf( "Redox couple must be two redox states " + "of the same element, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (*cptr != '(') + { + error_string = sformatf( "Element name must be followed by " + "parentheses in redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + paren2[0] = '('; + paren_count = 1; + p2 = 1; + while (*cptr != '\0') + { + cptr++; + if (*cptr == '/' || *cptr == '\0') + { + error_string = sformatf( "End of line or " "/" " encountered" + " before end of parentheses, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + + paren2[p2++] = *cptr; + if (*cptr == '(') + paren_count++; + if (*cptr == ')') + paren_count--; + if (paren_count == 0) + break; + } + paren2[p2] = '\0'; + if (strcmp(paren1, paren2) < 0) + { + strcpy(token, elt1.c_str()); + strcat(token, paren1); + strcat(token, "/"); + strcat(token, elt2.c_str()); + strcat(token, paren2); + } + else if (strcmp(paren1, paren2) > 0) + { + strcpy(token, elt2.c_str()); + strcat(token, paren2); + strcat(token, "/"); + strcat(token, elt1.c_str()); + strcat(token, paren1); + } + else + { + error_string = sformatf( "Both parts of redox couple are the same, %s.", + token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_centered(const char *string) +/* ---------------------------------------------------------------------- */ +{ + int i, l, l1, l2; + char token[MAX_LENGTH]; + + l = (int) strlen(string); + l1 = (79 - l) / 2; + l2 = 79 - l - l1; + for (i = 0; i < l1; i++) + token[i] = '-'; + token[i] = '\0'; + strcat(token, string); + for (i = 0; i < l2; i++) + token[i + l1 + l] = '-'; + token[79] = '\0'; + output_msg(sformatf("%s\n\n", token)); + return (OK); +} +bool Phreeqc:: +replace(const char *str1, const char *str2, std::string & str) +{ + size_t pos = str.find(str1); + if (pos != std::string::npos) + { + size_t l = strlen(str1); + str.replace(pos, l, str2); + return true; + } + return false; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +replace(const char *str1, const char *str2, char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function replaces str1 with str2 in str + * + * Arguments: + * str1 search str for str1 + * str2 replace str1 if str1 found in str + * str string to be searched + * + * Returns + * TRUE if string was replaced + * FALSE if string was not replaced + */ + int l, l1, l2; + char* ptr_start; + + ptr_start = strstr(str, str1); +/* + * Str1 not found, return + */ + if (ptr_start == NULL) + return (FALSE); +/* + * Str1 found, replace Str1 with Str2 + */ + l = (int) strlen(str); + l1 = (int) strlen(str1); + l2 = (int) strlen(str2); +/* + * Make gap in str long enough for str2 + */ + /* The plus one includes the terminating NULL */ + memmove(ptr_start + l2, ptr_start + l1, l - (ptr_start - str + l1) + 1); +/* + * Copy str2 into str + */ + memcpy(ptr_start, str2, l2); + return (TRUE); +} +void Phreeqc:: +replace(std::string &stds, const char* str1, const char* str2) +{ + size_t pos; + while ((pos = stds.find(str1)) != std::string::npos) { + stds.replace(pos, 1, str2); + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +space(void **ptr, int i, int *max, int struct_size) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine has 4 functions, allocate space, reallocate space, test to + * determine whether space is available, and free space. + * + * Arguments: + * ptr pointer to malloced space + * i value for test + * i = INIT, allocates space + * i >= 0 && i < max, space is available, return + * i >= max, reallocate space + * i = FREE, free space. + * max maximum value for i with available space + * struct size size of structure to be allocated + */ +/* + * Return if space exists + */ + if ((i >= 0) && (i + 1 < *max)) + { + return; + } +/* + * Realloc space + */ + if (i + 1 >= *max) + { + if (*max > 1000) + { + *max += 1000; + } + else + { + *max *= 2; + } + if (i + 1 > *max) + *max = i + 1; + *ptr = PHRQ_realloc(*ptr, (size_t) (*max) * struct_size); + if (*ptr == NULL) + malloc_error(); + return; + } +/* + * Allocate space + */ + if (i == INIT) + { +/* free(*ptr); */ + *ptr = PHRQ_malloc((size_t) (*max) * struct_size); + if (*ptr == NULL) + malloc_error(); + return; + } +/* + * Free space + */ +/* + if ( i == FREE ) { + free(*ptr); + return; + } + */ +/* + * Error + */ + error_msg("Illegal argument to function space.", CONTINUE); + error_msg("Program terminating.", STOP); + return; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +squeeze_white(char *s_l) +/* ---------------------------------------------------------------------- */ +/* + * Delete all white space from string s + * + * Argument: + * *s_l input, character string, possibly containing white space + * output, character string with all white space removed + * + * Return: void + */ +{ + int i, j; + + for (i = j = 0; s_l[i] != '\0'; i++) + { + if (!isspace((int) s_l[i])) + s_l[j++] = s_l[i]; + } + s_l[j] = '\0'; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +str_tolower(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Replaces string, str, with same string, lower case + */ + char* ptr; + ptr = str; + while (*ptr != '\0') + { + *ptr = (char) tolower(*ptr); + ptr++; + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +str_toupper(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Replaces string, str, with same string, lower case + */ + char* ptr; + ptr = str; + while (*ptr != '\0') + { + *ptr = (char) toupper(*ptr); + ptr++; + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +strcmp_nocase(const char *str1, const char *str2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare two strings disregarding case + */ + int c1, c2; + while ((c1 = tolower(*str1++)) == (c2 = tolower(*str2++))) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} +void Phreeqc::str_tolower(std::string &name) +{ + std::transform(name.begin(), name.end(), name.begin(), tolower); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +strcmp_nocase_arg1(const char *str1, const char *str2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare two strings disregarding case + */ + int c1, c2; + while ((c1 = tolower(*str1++)) == (c2 = *str2++)) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +/* ---------------------------------------------------------------------- */ +char * Phreeqc:: +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) +_string_duplicate(const char *token, const char *szFileName, int nLine) +#else +string_duplicate(const char *token) +#endif +/* ---------------------------------------------------------------------- */ +{ + int l; + char *str; + + if (token == NULL) + return NULL; + l = (int) strlen(token); +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + str = (char *) _malloc_dbg((size_t) (l + 1) * sizeof(char), _NORMAL_BLOCK, szFileName, nLine); +#else + str = (char *) PHRQ_malloc(((size_t)l + 1) * sizeof(char)); +#endif + + if (str == NULL) + malloc_error(); + strcpy(str, token); + return (str); +} + +/* ---------------------------------------------------------------------- */ +const char * Phreeqc:: +string_hsave(const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + if (str == NULL) return (NULL); + std::map::const_iterator it; + it = strings_map.find(str); + if (it != strings_map.end()) + { + return (it->second->c_str()); + } + + std::string *stdstr = new std::string(str); + strings_map[*stdstr] = stdstr; + return(stdstr->c_str()); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +strings_map_clear() +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + std::map::iterator it; + for (it = strings_map.begin(); it != strings_map.end(); it++) + { + delete it->second; + } + strings_map.clear(); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +under(LDBLE xval) +/* ---------------------------------------------------------------------- */ +{ +/* + * Exponentiate a number, x, but censor large and small numbers + * log values less than MIN_LM are set to 0 + * log values greater than MAX_LM are set to 10**MAX_LM + */ +/* if (xval < MIN_LM) { */ + if (xval < -40.) + { + return (0.0); + } + if (xval > MAX_LM) + { + return ( MAX_M ); + } + return (pow ((LDBLE) 10.0, xval)); +} +#ifndef PHREEQCI_GUI +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +status(int count, const char *str, bool rk_string) +/* ---------------------------------------------------------------------- */ +{ + char sim_str[20]; + char state_str[45]; + char spin_str[2]; + clock_t t2; + + if (pr.status == FALSE || phast == TRUE) + return (OK); + + if (state == INITIALIZE) + { + screen_string = sformatf("\n%-80s", "Initializing..."); + screen_msg(screen_string.c_str()); + status_on = true; + return (OK); + } +#ifdef NPP + t2 = clock(); + if (((state < ADVECTION && reaction_step < count_total_steps) || + (state == ADVECTION && (advection_step < count_ad_shifts || cell_no < count_cells)) || + (state == TRANSPORT && (transport_step < count_shifts || (mixrun < nmix /*&& + cell_no < count_cells*/)))) + && (int) (1e3 / CLOCKS_PER_SEC * (t2 - status_timer)) < status_interval) + return (OK); +#endif + + switch (state) + { + case INITIALIZE: + break; + case TRANSPORT: + if (str != NULL) + { + if (rk_string) + { + + screen_string = screen_string.substr(0, 43); + screen_string.append(str); + status_string = screen_string; + } + else + { + screen_string = "\r"; + screen_string.append(str); + status_string = screen_string; + } + status_on = true; + } + case PHAST: + break; + default: + // if str not NULL, print it + if (str != NULL && !rk_string) + { + screen_string = "\r"; + screen_string.append(str); + status_string = screen_string; + } + else + // print state + { + std::string stdstr; + if (str != NULL && rk_string) + { + stdstr = str; + } + sprintf(sim_str, "\rSimulation %d.", simulation); + sprintf(state_str, " "); + sprintf(spin_str, " "); + switch (state) + { + default: + break; + case INITIAL_SOLUTION: + sprintf(state_str, "Initial solution %d.", use.Get_solution_ptr()->Get_n_user()); + break; + case INITIAL_EXCHANGE: + sprintf(state_str, "Initial exchange %d.", use.Get_exchange_ptr()->Get_n_user()); + break; + case INITIAL_SURFACE: + sprintf(state_str, "Initial surface %d.", use.Get_surface_ptr()->Get_n_user()); + break; + case INVERSE: + sprintf(state_str, "Inverse %d. Models = %d.", use.Get_inverse_ptr()->n_user, count); + break; + case REACTION: + if (use.Get_kinetics_in() == TRUE) + { + sprintf(state_str, "Kinetic step %d.", reaction_step); + } + else + { + sprintf(state_str, "Reaction step %d.", reaction_step); + } + break; + case ADVECTION: + sprintf(state_str, "Advection, shift %d.", advection_step); + break; + } + spinner++; + if (spinner == 1) + { + spin_str[0] = '/'; + } + else if (spinner == 2) + { + spin_str[0] = '-'; + } + else + { + spin_str[0] = '\\'; + spinner = 0; + } + if (use.Get_kinetics_in() == TRUE) + { + screen_string = sformatf("%-15s%-27s%38s", sim_str, state_str, stdstr.c_str()); + status_string = screen_string; + } + else + { + screen_string = sformatf("%-15s%-27s%1s%45s", sim_str, state_str, spin_str, stdstr.c_str()); + status_string = screen_string; + } + } + status_on = true; + break; + } + +#ifndef NPP + t2 = clock(); + if ((int) (1e3 / CLOCKS_PER_SEC * (t2 - status_timer)) > status_interval) +#endif + { + status_timer = t2; + screen_msg(status_string.c_str()); + status_string.clear(); + } + return (OK); +} +#endif /*PHREEQCI_GUI */ +# include + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from left and right of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, start, end, length; + char* ptr_start; + + l = (int) strlen(str); + /* + * leading whitespace + */ + for (i = 0; i < l; i++) + { + if (isspace((int) str[i])) + continue; + break; + } + if (i == l) + return (EMPTY); + start = i; + ptr_start = &(str[i]); + /* + * trailing whitespace + */ + for (i = l - 1; i >= 0; i--) + { + if (isspace((int) str[i])) + continue; + break; + } + end = i; + if (start == 0 && end == l) + return (FALSE); + length = end - start + 1; + memmove((void *) str, (void *) ptr_start, (size_t) length); + str[length] = '\0'; + + return (TRUE); +} +void Phreeqc::string_trim_left(std::string& str) +{ + const std::string& chars = "\t\n "; + str.erase(0, str.find_first_not_of(chars)); +} +void Phreeqc::string_trim_right(std::string& str) +{ + const std::string& chars = "\t\n "; + str.erase(str.find_last_not_of(chars) + 1); +} +void Phreeqc::string_trim(std::string& str) +{ + const std::string& chars = "\t\n "; + str.erase(0, str.find_first_not_of(chars)); + str.erase(str.find_last_not_of(chars) + 1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim_right(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from right of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, end, length; + + l = (int) strlen(str); + for (i = l - 1; i >= 0; i--) + { + if (isspace((int) str[i])) + continue; + break; + } + end = i; + length = end + 1; + str[length] = '\0'; + if (end == 0) + return (EMPTY); + if (end == l) + return (FALSE); + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim_left(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from left of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, start, end, length; + char* ptr_start; + + l = (int) strlen(str); + /* + * leading whitespace + */ + for (i = 0; i < l; i++) + { + if (isspace((int) str[i])) + continue; + break; + } + if (i == l) + return (EMPTY); + start = i; + ptr_start = &(str[i]); + end = l; + if (start == 0 && end == l) + return (FALSE); + length = end - start + 1; + memmove((void *) str, (void *) ptr_start, (size_t) length); + str[length] = '\0'; + + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +char * Phreeqc:: +string_pad(const char *str, int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function returns new string padded to width i + * or returns str if longer + * Arguments: + * str string to pad + * + * Returns + * new string of with i + */ + int j, l, max; + char *str_ptr; + + l = (int) strlen(str); + max = l; + if (l < i) + max = i; + str_ptr = (char *) PHRQ_malloc((((size_t)max + 1) * sizeof(char))); + if (str_ptr == NULL) + malloc_error(); + else + strcpy(str_ptr, str); + if (i > l) + { + for (j = l; j < i; j++) + { + str_ptr[j] = ' '; + } + str_ptr[i] = '\0'; + } + return (str_ptr); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_input_errors() +/* ---------------------------------------------------------------------- */ +{ + if (input_error == 0) + { + return phrq_io->Get_io_error_count(); + } + return input_error; +}