diff --git a/cython/CMakeLists.txt b/cython/CMakeLists.txt index 1f5aebe..3abd0d7 100644 --- a/cython/CMakeLists.txt +++ b/cython/CMakeLists.txt @@ -1,193 +1,84 @@ # ======================= # # # # Python specmicp lib # # # # ======================= # # Build the cython interface # ========================== -find_package(PythonLibs REQUIRED) +# Import the correct version of python + +option(PYTHON_VERSION_3 "Version of python for cython compilation" ON) + +if (PYTHON_VERSION_3) +find_package(PythonLibs 3 REQUIRED) +else() +find_package(PythonLibs 2.7 REQUIRED) +endif() include_directories(${PYTHON_INCLUDE_PATH}) -option(PYTHON_VERSION_3 "Version of python for cython compilation" ON) +# include cython only once python is configured +include(UseCython) # main variables # =============== # directories # ----------- set(PY_SPECMICP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/specmicp) set(PY_SPECMICP_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/specmicp) set(PY_INCLUDES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/includes) -# cython flags -# ------------ - -set(CYTHON_FLAGS --cplus) - -if(${PYTHON_VERSION_3}) - set(CYTHON_FLAGS ${CYTHON_FLAGS} -3) -else() - set(CYTHON_FLAGS ${CYTHON_FLAGS} -2) -endif() - -if (CMAKE_BUILD_TYPE MATCHES DEBUG) - set(CYTHON_FLAGS ${CYTHON_FLAGS} -Wextra) -endif() - - -# cython compilation macro -# ------------------------ -# USAGE : cython_compilation(dir base_name dependancies) -macro(cython_compilation dir base_name) -add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${base_name}.cpp - COMMAND cython ${CYTHON_FLAGS} -I ${dir} -I ${PY_INCLUDES_DIR} -o ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${base_name}.cpp ${dir}/${base_name}.pyx - DEPENDS ${ARGN}) -# ${ARGN} is the list of arguments passed after the last expected argument -# i.e. ${ARGN} = dependancies -endmacro(cython_compilation) # common includes # =============== add_custom_target(cython_includes SOURCES ${PY_INCLUDES_DIR}/memory.pxd ${PY_INCLUDES_DIR}/eigen.pxd ) -# specmicp module +# SpecMiCP module # =============== - -file(COPY ${PY_SPECMICP_SOURCE_DIR}/__init__.py - DESTINATION ${PY_SPECMICP_BINARY_DIR} -) - -# specmicp.database -# ================= - -# Sources -add_custom_target(cython_specmicp_database_files SOURCES - ${PY_SPECMICP_SOURCE_DIR}/database.pxd - ${PY_SPECMICP_SOURCE_DIR}/database.pyx -) -set(cython_database_dependancies - ${PY_SPECMICP_SOURCE_DIR}/database.pxd - ${PY_SPECMICP_SOURCE_DIR}/database.pyx - ${PY_INCLUDES_DIR}/memory.pxd - ${PY_INCLUDES_DIR}/eigen.pxd -) - -# Cython compilation -cython_compilation(${PY_SPECMICP_SOURCE_DIR} database ${cython_database_dependancies}) - -# build the specmicp.database library -add_library(database SHARED ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/database.cpp ${DATABASE_LIBFILE}) - -set_target_properties(database - PROPERTIES PREFIX "" - LIBRARY_OUTPUT_DIRECTORY "${PY_SPECMICP_BINARY_DIR}" -) -#target_link_libraries(database specmicp_database) - -# specmicp.equilibrium_state -# ========================== - -# Sources -add_custom_target(cython_specmicp_equilibrium_state_files SOURCES - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pxd - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pyx -) -set(cython_equilibrium_state_dependancies - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pxd - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pyx - ${PY_INCLUDES_DIR}/eigen.pxd -) -# Cython compilation -cython_compilation(${PY_SPECMICP_SOURCE_DIR} equilibrium_state ${cython_equilibrium_state_dependancies}) - -add_library(equilibrium_state SHARED - ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/equilibrium_state.cpp - ${SPECMICP_LIBFILE} - ${DATABASE_LIBFILE}) - -set_target_properties(equilibrium_state - PROPERTIES PREFIX "" - LIBRARY_OUTPUT_DIRECTORY "${PY_SPECMICP_BINARY_DIR}" -) -target_link_libraries(equilibrium_state jsoncpp) - - -# specmic.reaction_path -# ===================== - -# Sources -add_custom_target(cython_specmicp_reaction_path_files SOURCES - ${PY_SPECMICP_SOURCE_DIR}/solver_options.pxd - ${PY_SPECMICP_SOURCE_DIR}/reaction_path.pxd - ${PY_SPECMICP_SOURCE_DIR}/reaction_path.pyx -) -set(cython_reaction_path_dependancies - ${PY_SPECMICP_SOURCE_DIR}/solver_options.pxd - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pxd - ${PY_SPECMICP_SOURCE_DIR}/equilibrium_state.pyx - ${PY_SPECMICP_SOURCE_DIR}/reaction_path.pxd - ${PY_SPECMICP_SOURCE_DIR}/reaction_path.pyx - ${PY_INCLUDES_DIR}/eigen.pxd -) -# Cython compilation -cython_compilation(${PY_SPECMICP_SOURCE_DIR} reaction_path ${cython_reaction_path_dependancies}) - -# build the specmicp.reaction_path library -add_library(reaction_path SHARED - ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/reaction_path.cpp - ${SPECMICP_LIBFILE} - ${DATABASE_LIBFILE}) - -set_target_properties(reaction_path - PROPERTIES PREFIX "" - LIBRARY_OUTPUT_DIRECTORY "${PY_SPECMICP_BINARY_DIR}" -) -target_link_libraries(reaction_path jsoncpp) - +add_subdirectory(specmicp) # ========== # # # # TEST # # # # ========== # # Prelude # ======= # directories # ----------- set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../tests/cython) set(BINARY_TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/tests) # variables to configure the scripts # ---------------------------------- set(python_module_path ${CMAKE_CURRENT_BINARY_DIR}) set(database_path ${CMAKE_CURRENT_BINARY_DIR}/../data/cemdata_specmicp.js) # macro to configure the scripts # ------------------------------- macro(configure_python_test test_name) configure_file(${TEST_DIR}/${test_name} ${BINARY_TEST_DIR}${CMAKE_FILES_DIRECTORY}/${test_name} @ONLY) file(COPY ${BINARY_TEST_DIR}${CMAKE_FILES_DIRECTORY}/${test_name} DESTINATION ${BINARY_TEST_DIR} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endmacro(configure_python_test) # Scripts # ======= # database unittest configure_python_test(database.py) # thermocarbo configure_python_test(thermocarbo.py) diff --git a/cython/specmicp/CMakeLists.txt b/cython/specmicp/CMakeLists.txt new file mode 100644 index 0000000..fc1c6a8 --- /dev/null +++ b/cython/specmicp/CMakeLists.txt @@ -0,0 +1,55 @@ +# Build the python module : SpecMiCP +# ================================== + +set(PY_INCLUDES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../includes) + +include_directories(${PY_INCLUDES_DIR}) +include_directories(${EIGEN3_INCLUDE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${JSONCPP_DIR}) + +set(cython_specmicp_files + database.pxd + database.pyx + solution.pxd + solution.pyx + +) + +# specmicp module +# --------------- + +file(COPY __init__.py + DESTINATION ${PY_SPECMICP_BINARY_DIR} +) + +# database +# --------- + +set_source_files_properties( + database.pyx + PROPERTIES CYTHON_IS_CXX TRUE ) + +cython_add_module(database database.pyx ${DATABASE_LIBFILE}) + +# solution +# -------- + +set_source_files_properties( + solution.pyx + PROPERTIES CYTHON_IS_CXX TRUE ) + +cython_add_module(solution solution.pyx ${ADIMENSIONAL_DIR}/adimensional_system_solution_extractor.cpp) + +# specmicp module +# --------------- + +file(COPY __init__.py + DESTINATION ${PY_SPECMICP_BINARY_DIR} +) + +add_custom_target(cython_specmicp + DEPENDS database solution + COMMENT "Built the python specmicp module" + SOURCES ${cython_specmicp_files} +) diff --git a/cython/specmicp/database.pxd b/cython/specmicp/database.pxd index 9dc85b7..4117d5d 100644 --- a/cython/specmicp/database.pxd +++ b/cython/specmicp/database.pxd @@ -1,62 +1,71 @@ from libcpp.string cimport string from libcpp.vector cimport vector from libcpp.map cimport map as cmap from libcpp cimport bool from memory cimport shared_ptr from eigen cimport MatrixXd, VectorXd + # The raw container cdef extern from "database/data_container.hpp" namespace "specmicp::database": cdef cppclass DataContainer: - # Cython-cpp interface - # + int water_index(); + int electron_index(); + # basis # ----- - int nb_component - vector[string] labels_basis - # + int nb_component(); + int nb_aqueous_components(); + int get_id_component(string); + string get_label_component(int); + # aqueous - #-------- - int nb_aqueous - vector[string] labels_aqueous - MatrixXd nu_aqueous - VectorXd logk_aqueous - # + # ------- + int nb_aqueous(); + int get_id_aqueous(string); + string get_label_aqueous(int); + float nu_aqueous(int, int); + float logk_aqueous(int); + # mineral - #-------- - int nb_mineral - vector[string] labels_minerals - MatrixXd nu_mineral - VectorXd logk_mineral + # ------- + int nb_mineral(); + int get_id_mineral(string); + string get_label_mineral(int); + float nu_mineral(int, int); + float logk_mineral(int); + + int nb_mineral_kinetic(); + # The database manager # Used to creat and modify the database cdef extern from "database/database.hpp" namespace "specmicp::database": cdef cppclass Database: # cython-cpp interface - list of methods that we can access # Database should be the only module accessible from python Database() Database(string) except + void parse_database(string) except + shared_ptr[DataContainer] get_database() void swap_components(cmap[string, string]) void minerals_keep_only(vector[string]) + bool is_valid() # The following methods are from # specmicp::database::DatabaseModule - bool is_database_canonical() - int component_label_to_id(string) + int component_label_to_id(string) int aqueous_label_to_id(string) int mineral_label_to_id(string) # header cdef class DatabaseManager: """ The Python database handler Use this class for checking values in the database or switching basis/dropping minerals or components... """ cdef Database *database cdef shared_ptr[DataContainer] container cdef shared_ptr[DataContainer] get_raw_db(self) cdef DataContainer* _get(self) diff --git a/cython/specmicp/database.pyx b/cython/specmicp/database.pyx index 45ef177..1283e33 100644 --- a/cython/specmicp/database.pyx +++ b/cython/specmicp/database.pyx @@ -1,205 +1,202 @@ from libcpp.map cimport map as cmap from libcpp.pair cimport pair from memory cimport shared_ptr from database cimport Database, DataContainer # Used to indicate that something does not exist cdef enum: - no_equation = -1 + no_species = -1 cdef class DatabaseManager: """ The Python database handler Use this class for checking values in the database or switching basis/dropping minerals or components... """ def __cinit__(self, bytes filepath): """filepath is the path to the database file""" self.database = new Database(filepath) self.container = self.database.get_database() cdef shared_ptr[DataContainer] get_raw_db(self): return self.container cdef DataContainer* _get(self): return self.container.get() - def is_canonical(self): - """Return true if the databse is canonical. - Should be true if the database has been correctly initialized. - """ - return self.database.is_database_canonical() + def is_valid(self): + return self.database.is_valid(); # --------- # # Component # # --------- # property nb_component: """The number of component in the database.""" - def __get__(self): return self._get().nb_component + def __get__(self): return self._get().nb_component() # labels and id # -------------- def component_label_to_id(self, bytes comp): """ Return the id of a component.""" cdef int ans = self.database.component_label_to_id(comp) - if (ans == no_equation): + if (ans == no_species): raise ValueError("Species : '"+comp.decode()+"' is not a component.") return ans def component_id_to_label(self, int idc): """ Return the label of a component.""" if (idc >= self.nb_component): raise ValueError("'"+str(idc)+ "' is not a valid index for a component") - return bytes(self._get().labels_basis[idc]) + return bytes(self._get().get_label_component(idc)) def print_basis(self): """Print the components in the basis, with their ID""" print("The basis is :") cdef int i - for i in range(self._get().nb_component): - print(" - "+str(i)+" : "+bytes(self._get().labels_basis[i]).decode()) + for i in range(self.nb_component): + print(" - "+str(i)+" : "+bytes(self._get().get_label_component(i)).decode()) # basis switch # ------------ def swap_components(self, dict swapping): """Swap components in the basis - + swapping is a dictionnary where the keys are the labels of the components to replace and the values are the labels of the secondary species to add in the basis. Raise value errors if the labels are invalid """ cdef cmap[string,string] input_map cdef int idsp for (key, value) in swapping.items(): idsp = self.component_label_to_id(key) - if (idsp == no_equation): + if (idsp == no_species): raise ValueError("'"+key.decode()+"' is not a valid component.") idsp = self.aqueous_label_to_id(value) - if (idsp == no_equation): + if (idsp == no_species): raise ValueError("'"+key.decode()+ "' is not a valid secondary aqueous species.") input_map.insert(pair[string, string](key, value)) self.database.swap_components(input_map) - # --------------- # + # --------------- # # Aqueous species # # --------------- # property nb_aqueous: """The number of aqueous species in the database""" - def __get__(self): return self._get().nb_aqueous + def __get__(self): return self._get().nb_aqueous() # labels and id # ------------- def aqueous_id_to_label(self, int ids): if (ids >= self.nb_aqueous): raise ValueError("'"+str(ids)+ "' is not a valid index for a secondary aqueous species") - return bytes(self._get().labels_aqueous[ids]) + return bytes(self._get().get_label_aqueous(ids)) def aqueous_label_to_id(self, bytes aqueous): """ Return the id of a secondary aqueous species""" cdef int ans = self.database.aqueous_label_to_id(aqueous) - if (ans == no_equation): + if (ans == no_species): raise ValueError("Species : '"+aqueous.decode()+ "' is not a secondary aqueous species.") return ans def print_aqueous(self): """ Print the secondary species""" print("The secondary aqueous species are :") cdef int i - for i in range(self._get().nb_aqueous): - print(" - "+str(i)+" : "+bytes(self._get().labels_aqueous[i]).decode()) + for i in range(self.nb_aqueous): + print(" - "+str(i)+" : "+bytes(self._get().get_label_aqueous(i)).decode()) # properties # ---------- def nu_aqueous(self, int ids, int idc): """Return stoichiometric coefficient of a secondary aqueous species""" - if (ids >= self._get().nb_aqueous or idc >= self._get().nb_component): - raise ValueError("uncorrect bounds") + if (ids >= self.nb_aqueous or idc >= self.nb_component): + raise ValueError("Incorrect bounds") return self._get().nu_aqueous(ids, idc) def l_nu_aqueous(self, bytes aqueous, bytes component): """Return stoichiometric coefficient of a secondary aqueous species""" return self._get().nu_aqueous(self.aqueous_label_to_id(aqueous), self.component_label_to_id(component)) def logk_aqueous(self, int ids): """Return the equilibrium constant of a secondary aqueous species""" - if (ids >= self._get().nb_aqueous): + if (ids >= self.nb_aqueous): raise ValueError("Index : '"+str(ids) +"' is not valid for an aqueous species") return self._get().logk_aqueous(ids) def l_logk_aqueous(self, bytes aqueous): """Return the equilibrium constant of a secondary aqueous species""" return self._get().logk_aqueous(self.aqueous_label_to_id(aqueous)) # ------------ # # Solid phases # # ------------ # property nb_mineral: """The number of mineral in the database""" - def __get__(self): return self._get().nb_mineral + def __get__(self): return self._get().nb_mineral() # labels and id # ------------- def mineral_id_to_label(self, int idm): """Return the label of a mineral.""" if (idm >= self.nb_mineral): raise ValueError("'"+str(idm)+ "' is not a valid index for a solid phase") - return bytes(self._get().labels_minerals[idm]) + return bytes(self._get().get_label_mineral(idm)) def mineral_label_to_id(self, bytes mineral): """ Return the id of a secondary aqueous species""" ans = self.database.mineral_label_to_id(mineral) - if (ans == no_equation): + if (ans == no_species): raise ValueError("Species : '"+mineral.decode()+ "' is not a solid phase at equilibrium in the database.") return ans def print_minerals(self): """ Print the solid phases at equilibrium""" print("The solid phases at equilibrium are :") cdef int i - for i in range(self._get().nb_mineral): - print(" - "+str(i)+" : "+bytes(self._get().labels_minerals[i]).decode()) - + for i in range(self.nb_mineral): + print(" - "+str(i)+" : "+bytes(self._get().get_label_mineral(i)).decode()) + def minerals_keep_only(self, list_mineral_to_keep): """Keep only the solid phases in 'list_mineral_to_keep'""" # we first verify that the list is valid for label in list_mineral_to_keep: self.mineral_label_to_id(label) # just check that it is not raising any error self.database.minerals_keep_only(list_mineral_to_keep) # properties # ---------- def logk_mineral(self, int idm): """Return the equilibrium constant of a solid phase""" - if (idm >= self._get().nb_mineral): + if (idm >= self.nb_mineral): raise ValueError("Index : '"+str(idm) +"'is not valid for a solid phase") return self._get().logk_mineral(idm) def l_logk_mineral(self, bytes mineral): """Return the equilibrium constant of a solid phase""" return self._get().logk_mineral(self.mineral_label_to_id(mineral)) def nu_mineral(self, int idm, int idc): """Return stoichometric coefficient of a mineral""" - if (idm >= self._get().nb_mineral or idc >= self._get().nb_component): - raise ValueError("uncorrect argument, exceed bounds") + if (idm >= self.nb_mineral or idc >= self.nb_component): + raise ValueError("Incorrect argument, exceed bounds") return self._get().nu_mineral(idm, idc) def l_nu_mineral(self, bytes mineral, bytes component): """Return stoichometric coefficient of a mineral""" return self._get().nu_mineral(self.mineral_label_to_id(mineral), self.component_label_to_id(component)) - + diff --git a/cython/specmicp/solution.pxd b/cython/specmicp/solution.pxd new file mode 100644 index 0000000..3daf6b8 --- /dev/null +++ b/cython/specmicp/solution.pxd @@ -0,0 +1,37 @@ +from libcpp cimport bool + +cdef extern from "specmicp/adimensional/adimensional_system_solution.hpp" namespace "specmicp": + cdef cppclass AdimensionalSystemSolution: + bool is_valid + +cdef extern from "specmicp/adimensional/adimensional_system_solution_extractor.hpp" namespace "specmicp": + cdef cppclass AdimensionalSystemSolutionExtractor: + float volume_fraction_water() + float density_water() + float saturation_water() + float saturation_gas_phase() + float porosity() + float pE() + float Eh() + float molality_component(int) + float activity_component(int) + float volume_fraction_mineral(int) + float mole_concentration_mineral(int) + float mass_concentration_mineral(int) + float saturation_index(int) + float free_surface_concentration() + float ionic_strength() + float pH() + float molality_aqueous(int) + float activity_aqueous(int) + float gas_fugacity(int) + float sorbed_species_molalities(int) + float total_aqueous_concentration(int) + float total_solid_concentration(int) + float total_immobile_concentration(int) + +cdef class SpecmicpSolution: + """This class contains the solution from a specmicp computation.""" + cdef AdimensionalSystemSolutionExtractor* extractor + + diff --git a/cython/specmicp/solution.pyx b/cython/specmicp/solution.pyx new file mode 100644 index 0000000..a0eadc2 --- /dev/null +++ b/cython/specmicp/solution.pyx @@ -0,0 +1,90 @@ +from solution cimport SpecmicpSolution + +cdef class SpecmicpSolution: + def __cinit__(self): + pass + # water and gas volume fraction + # ----------------------------- + def volume_fraction_water(self): + """Return the volume fraction of water""" + return self.extractor.volume_fraction_water() + def density_water(self): + """Return the density of water (in the correct units)""" + return self.extractor.density_water() + def porosity(self): + """Return the porosity""" + return self.extractor.porosity() + def saturation_water(self): + """Return the saturation of the liquid phase""" + return self.extractor.saturation_water() + def saturation_gas_phase(self): + """Return the saturation of the gas phase""" + return self.extractor.saturation_gas_phase() + # solution properties + # ------------------- + def pE(self): + """Return pE""" + return self.extractor.pE() + def Eh(self): + """Return Eh""" + return self.extractor.Eh() + def pH(self): + """Return the pH of the solution""" + return self.extractor.pH() + def ionic_strength(self): + """Return the ionic strength of the solution""" + return self.extractor.ionic_strength() + # components + def molality_component(self, index): + """Return molality of component 'index'""" + return self.extractor.molality_component(index) + def activity_component(self, index): + """Return the molality of component 'index'""" + return self.extractor.activity_component(index) + # minerals + # -------- + def volume_fraction_mineral(self, index): + """Return the volume fraction of mineral 'index'""" + return self.extractor.volume_fraction_mineral(index) + def mole_concentration_mineral(self, index): + """Return the concentration (mol/V) of mineral 'index'""" + return self.extractor.mole_concentration_mineral(index) + def mass_concentration_mineral(self, index): + """Return the concentration (M/V) of mineral 'index'""" + return self.extractor.mass_concentration_mineral(index) + def saturation_index(self, index): + """Return the saturation index of mineral 'index'""" + return self.extractor.saturation_index(index) + # aqueous species + # --------------- + def molality_aqueous(self, index): + """Return the molality of aqueous 'index'""" + return self.extractor.molality_aqueous(index) + def activity_aqueous(self, index): + """Return the activity of aqueous 'index'""" + return self.extractor.activity_aqueous(index) + # gas + # --- + def fugacity_gas(self, index): + """Return the fugacity of gas 'index'""" + return self.extractor.gas_fugacity(index) + # sorption model + # -------------- + def concentration_free_surface_sites(self): + """Return the concentration of free surface sites""" + return self.extractor.free_surface_concentration() + def molality_sorbed_species(self, index): + """Return the molality of sorbed_species 'index'""" + return self.extractor.sorbed_species_molalities(index) + # total concentrations + # -------------------- + def total_aqueous_concentration(self, index): + """Return the total aqueous concentration of component 'index'""" + return self.extractor.total_aqueous_concentration(index) + def total_solid_concentration(self, index): + """Return the total solid concentration of component 'index'""" + return self.extractor.total_solid_concentration(index) + def total_immobile_concentration(self, index): + """Return the total immobile (solid+sorbed) concentration of component + 'index'""" + return self.extractor.total_immobile_concentration(index) diff --git a/tests/cython/database.py b/tests/cython/database.py index d76b951..0f3ed16 100644 --- a/tests/cython/database.py +++ b/tests/cython/database.py @@ -1,75 +1,76 @@ #!/usr/bin/env python import unittest as ut import sys -lib_directory="@python_module_path@" -database_path=b"@database_path@" +lib_directory = "@python_module_path@" +database_path = b"@database_path@" sys.path.insert(0, lib_directory) import specmicp.database as database + class TestDatabase(ut.TestCase): def test_init(self): db = database.DatabaseManager(database_path) - self.assertTrue(db.is_canonical()) + self.assertTrue(db.is_valid()) self.assertTrue(db.nb_component > 0) self.assertTrue(db.nb_aqueous > 0) - self.assertTrue(db.nb_mineral >0) - + self.assertTrue(db.nb_mineral > 0) + def test_access_aqueous(self): db = database.DatabaseManager(database_path) self.assertTrue(db.nb_aqueous > 0) self.assertTrue(db.nb_aqueous > 2) self.assertTrue(db.nb_component > 3) label = db.aqueous_id_to_label(2) self.assertEqual(db.aqueous_label_to_id(label), 2) self.assertIsNotNone(db.nu_aqueous(2, 2)) self.assertEqual(db.l_nu_aqueous(label, db.component_id_to_label(2)), db.nu_aqueous(2, 2)) self.assertIsNotNone(db.nu_aqueous(2, 3)) self.assertEqual(db.l_nu_aqueous(label, db.component_id_to_label(3)), db.nu_aqueous(2, 3)) def test_access_mineral(self): db = database.DatabaseManager(database_path) self.assertTrue(db.nb_mineral > 0) self.assertTrue(db.nb_mineral > 2) self.assertTrue(db.nb_component > 3) label = db.mineral_id_to_label(2) self.assertEqual(db.mineral_label_to_id(label), 2) self.assertIsNotNone(db.nu_mineral(2, 2)) self.assertEqual(db.l_nu_mineral(label, db.component_id_to_label(2)), db.nu_mineral(2, 2)) self.assertIsNotNone(db.nu_mineral(2, 3)) self.assertEqual(db.l_nu_mineral(label, db.component_id_to_label(3)), db.nu_mineral(2, 3)) def test_switch_basis(self): db = database.DatabaseManager(database_path) swapping = {b"H[+]": b"HO[-]", b"Al[3+]": b"Al(OH)4[-]" } db.swap_components(swapping) for (key, value) in swapping.items(): self.assertRaises(ValueError, db.component_label_to_id, key) self.assertNotEqual(db.aqueous_label_to_id(key), -1) self.assertNotEqual(db.component_label_to_id(value), -1) self.assertRaises(ValueError, db.aqueous_label_to_id, value) def mineral_keep_only(self): db = database.DatabaseManager(database_path) mineral_to_keep = (b"Portlandite", b"Gypsum", b"Straetlingite") logks = [db.l_logk_mineral(mineral) for mineral in mineral_to_keep] db.minerals_keep_only(mineral_to_keep) db.assertEqual(db.nb_mineral, 3) for i in range(db.nb_mineral): assertTrue(db.mineral_id_to_label(i) in mineral_to_keep) assertTrue(db.logk_mineral(i) in logks) if __name__ == '__main__': ut.main()