diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de7415..e455eb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,109 +1,137 @@ project(specmicp) cmake_minimum_required(VERSION 2.8) ####################### External Package ##################################### set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") find_package(Eigen3 REQUIRED) # This module comes from the Eigen3 Package find_package(CxxTest) # Necessary if you want the tests ######################## Compilation flags ################################# if(UNIX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -std=c++11") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DNDEBUG") message(STATUS "c++ flags ${CMAKE_CXX_FLAGS}") else() message(WARNING "not tested !") endif() ################ Directories ##################### set(PROJECT_TEST_DIR ${PROJECT_SOURCE_DIR}/tests) set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) # include include_directories(${EIGEN3_INCLUDE_DIR}) include_directories(${PROJECT_SOURCE_DIR}) ################ Modules ########################## # -------- MiCPSolver ---------------- set(MICPSOLVER_DIR ${PROJECT_SOURCE_DIR}/micpsolver) add_custom_target(specmisolver_inc SOURCES ${MICPSOLVER_DIR}/micpsolver.hpp ${MICPSOLVER_DIR}/micpsolver.inl ${MICPSOLVER_DIR}/micpprog.hpp ${MICPSOLVER_DIR}/ncp_function.hpp ${MICPSOLVER_DIR}/estimate_cond_number.hpp ) # ------- SpecMiCP --------------- set(SPECMICP_DIR ${PROJECT_SOURCE_DIR}/specmicp) add_custom_target(specmic_inc SOURCES #${SPECMICP_DIR}/specmicp.hpp ${SPECMICP_DIR}/thermodata.hpp ) set(SPECMICP_LIBFILE ${SPECMICP_DIR}/specmicp.cpp ) add_library(specmicp ${SPECMICP_LIBFILE}) +# -------- Database ---------------- + +set(DATABASE_DIR ${PROJECT_SOURCE_DIR}/database) + +set(DATABASE_LIBFILE ${DATABASE_DIR}/reader.cpp) + +add_custom_target(data_incl SOURCES + ${DATABASE_DIR}/common_def.hpp + ${DATABASE_DIR}/data_container.hpp) + +add_library(specmicp_database ${DATABASE_LIBFILE}) + # ------- Utils ------------------ set(UTILS_DIR ${PROJECT_SOURCE_DIR}/utils) add_custom_target(utils_inc SOURCES ${UTILS_DIR}/log.hpp) +# ------- Data ------------------- + +set(DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) +add_custom_target(data SOURCES ${DATA_DIR}/specmicp_database.js) +file(INSTALL ${DATA_DIR}/specmicp_database.js + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + # ------ Documentation ----------- # add a target to generate API documentation with Doxygen find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif(DOXYGEN_FOUND) # --------- Test ---------------------- # CXX Test # ======== if(CXXTEST_FOUND) set(CXXTEST_USE_PYTHON TRUE) include_directories(${CXXTEST_INCLUDE_DIR}) enable_testing() # MiCP Solver # ------------ CXXTEST_ADD_TEST(test_cond_number test_cond_number.cpp ${PROJECT_TEST_DIR}/micpsolver/test_cond_number.hpp) CXXTEST_ADD_TEST(test_ncp_funtion test_ncp_function.cpp ${PROJECT_TEST_DIR}/micpsolver/test_ncp_function.hpp) CXXTEST_ADD_TEST(test_micpsolver test_micpsolver.cpp ${PROJECT_TEST_DIR}/micpsolver/test_micpsolver.hpp) + + # Database + # -------- + + CXXTEST_ADD_TEST(test_data_reader + test_data_reader.cpp + ${PROJECT_TEST_DIR}/database/test_data_reader.hpp) + target_link_libraries(test_data_reader specmicp_database jsoncpp) + endif() # Test 'Perso' # ============ add_executable(thermocarbo ${PROJECT_TEST_DIR}/specmicp/thermocarbo.cpp) target_link_libraries(thermocarbo specmicp) diff --git a/data/specmicp_database.js b/data/specmicp_database.js new file mode 100644 index 0000000..4b8fb48 --- /dev/null +++ b/data/specmicp_database.js @@ -0,0 +1,202 @@ +{ + "Metadata": { + "version": "0.0.1", + "last-modification": "01/12/2013" + }, + "Basis": [ + { + "label": "H2O" + }, + { + "label": "HO[-]", + "activity": { + "a": 10.65, + "b": 0 + } + }, + { + "label": "Ca[2+]", + "activity": { + "a": 4.86, + "b": 0.15 + } + }, + { + "label": "SiO(HO)3[-]", + "activity": { + "a": 10.65, + "b": 0.00 + } + }, + { + "label": "SO4[2-]", + "activity": { + "a": 5.31, + "b": -0.07 + } + }, + { + "label": "HCO3[-]", + "activity": { + "a": 5.40, + "b": 0.00 + } + }, + { + "label": "Na[+]", + "activity": { + "a": 4.32, + "b": 0.06 + } + }, + { + "label": "Cl[+]", + "activity": { + "a": 3.71, + "b": 0.01 + } + } + ], + "Aqueous": [ + { + "label": "H[+]", + "composition": "H2O, - HO[-]", + "log_k": 13.9995, + "activity": { + "a": 9.00, + "b": 0.00 + } + }, + { + "label": "Si(OH)4", + "composition": "SiO(HO)3[-], H2O, - HO[-]", + "log_k": 4.8595 + }, + { + "label": "SiO2(OH)2[2-]", + "composition": "SiO(HO)3[-], H2O, - HO[-]", + "log_k": 0.1005, + "activity": { + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "CaSO4(aq)", + "composition": "Ca[2+], SO4[2-]", + "log_k": -2.3 + }, + { + "label": "CaOH+", + "composition": "Ca[2+], HO[-]", + "log_k": -1.2195, + "activity":{ + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "CO3[2-]", + "composition": "HCO3[-], - H2O, HO[-]", + "log_k": -3.6706, + "activity": { + "a": 5.40, + "b": 0.00 + } + }, + { + "label": "CO2(aq)", + "composition": "HCO3[-], - HO[-]", + "log_k": 7.6476 + }, + { + "label": "CaCO3(aq)", + "composition": "Ca[2+], HCO3[-], HO[-], -H2O", + "log_k": -6.8947 + }, + { + "label": "CaHCO3[+]", + "composition": "Ca[2+], HCO3[-]", + "log_k": -1.1057, + "activity": { + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "CaSiO(HO)3[+]", + "composition": "Ca[2+], SiO(HO)3[-]", + "log_k": -1.2, + "activity": { + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "CaSiO2(HO)2(aq)", + "composition": "Ca[2+], SiO(HO)3[-], HO[-], - H2O", + "log_k": -4.4995 + }, + { + "label": "NaOH(aq)", + "composition": "Na[+], HO[-]", + "log_k": 0.1805 + }, + { + "label": "NaSO4[-]", + "composition": "Na[+], SO4[2-]", + "log_k": -0.7, + "activity": { + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "NaCO3[-]", + "composition": "Na[+], HO[-], HCO3[-], - H2O", + "log_k": -4.9405, + "activity": { + "a": 4.00, + "b": 0.00 + } + }, + { + "label": "NaHCO3(aq)", + "composition": "Na[+], HCO3[-]", + "log_k": 0.25 + } + ], + "Minerals": [ + { + "label": "Portlandite", + "composition": "Ca[2+], 2 HO[-]", + "log_k": -5.1995 + }, + { + "label": "SiO2,am", + "composition": "SiO(HO)3[-], - H2O, - HO[-]", + "log_k": 1.476 + }, + { + "label": "CSH,jennite", + "composition": "1.6667 Ca[2+], 2.3333 HO[-], SiO(HO)3[-], -0.5567 H2O", + "log_k": -13.17 + }, + { + "label": "CSH,tobermorite", + "composition": "0.8333 Ca[2+], 0.6667 HO[-], SiO(HO)3[-], -0.5 H2O", + "log_k": -8.00 + }, + { + "label": "Calcite", + "composition": "Ca[2+], HCO3[-], HO[-], -H2O", + "log_k": -12.1505 + }, + { + "label": "Gypsum", + "composition": "Ca[2+], SO4[2-], 2 H2O", + "log_k": -4.5809 + } + ] +} + diff --git a/src/database/common_def.hpp b/src/database/common_def.hpp new file mode 100644 index 0000000..b9c7ade --- /dev/null +++ b/src/database/common_def.hpp @@ -0,0 +1,38 @@ +/*------------------------------------------------------- + + - Module : database + - File : common_def.hpp + - Author : Fabien Georget + + Copyright (c) 2014, Fabien Georget, Princeton University + +---------------------------------------------------------*/ + +#ifndef SPECMICP_DATABASE_COMMONDEF_HPP +#define SPECMICP_DATABASE_COMMONDEF_HPP + +#include +#include + +//! \file common_def.hpp common type definitions + +namespace specmicp { +namespace database { + + +//! ID of a species +using id_species_t = unsigned int; +//! Vector containing a list of species (by ID) +using list_species_t = std::vector; +//! Vector containing each strings +using vector_labels_t = std::vector; +//! Stoechiometric coefficients for the reactions +using reaction_mat_t = Eigen::Matrix; +//! Vector containing logK values +using logK_vector_t = Eigen::Matrix; + + +} // end namespace database +} // end namespace specmicp + +#endif // SPECMICP_DATABASE_COMMONDEF_HPP diff --git a/src/database/data_container.hpp b/src/database/data_container.hpp new file mode 100644 index 0000000..0266fda --- /dev/null +++ b/src/database/data_container.hpp @@ -0,0 +1,47 @@ +/*------------------------------------------------------- + + - Module : database + - File : data_container.hpp + - Author : Fabien Georget + + Copyright (c) 2014, Fabien Georget, Princeton University + +---------------------------------------------------------*/ + +#ifndef SPECMICP_DATABASE_DATACONTAINER_HPP +#define SPECMICP_DATABASE_DATACONTAINER_HPP + +//! \file data_container.hpp Storage class for thermodynamics database + +#include "common_def.hpp" +#include + +namespace specmicp { +namespace database { + +struct DataContainer +{ + int nb_component; //!< Number of components == size of the basis + int nb_aqueous; //!< Number of aqueous species (not taking into acount the basis) + int nb_mineral; //!< Number of minerals + + vector_labels_t labels_basis; //!< labels of the components + std::map map_labels_basis; //!< map labels <-> id + vector_labels_t labels_aqueous; //!< labels of the aqueous species + vector_labels_t labels_minerals; //!< labels of the minerals + + reaction_mat_t nu_aqueous; //!< Stoechiometric coefficient for aqueous species + logK_vector_t logk_aqueous; //!< LogK for aqueous species + + reaction_mat_t nu_mineral; //!< Stoichiometric coefficient for minerals + logK_vector_t logk_mineral; //!< LogK for minerals + + Eigen::Matrix param_aq; //!< Aqueous parameters + +}; + +} // end namespace database + +} // end namespace specmicp + +#endif // SPECMICP_DATABASE_DATACONTAINER_HPP diff --git a/src/database/reader.cpp b/src/database/reader.cpp new file mode 100644 index 0000000..88722c4 --- /dev/null +++ b/src/database/reader.cpp @@ -0,0 +1,292 @@ +/*------------------------------------------------------- + + - Module : database + - File : reader.cpp + - Author : Fabien Georget + + Copyright (c) 2014, Fabien Georget, Princeton University + +---------------------------------------------------------*/ + +#include "reader.hpp" + +#include "jsoncpp/json/reader.h" +#include +#include + +#include +#include +#include +#include + +#include "utils/log.hpp" + +#define INDB_SECTION_BASIS "Basis" +#define INDB_SECTION_AQUEOUS "Aqueous" +#define INDB_SECTION_MINERALS "Minerals" +#define INDB_SECTION_GAS "Gas" + +#define INDB_ATTRIBUTE_LABEL "label" +#define INDB_ATTRIBUTE_ACTIVITY "activity" +#define INDB_ATTRIBUTE_ACTIVITY_A "a" +#define INDB_ATTRIBUTE_ACTIVITY_B "b" + +#define INDB_ATTRIBUTE_COMPOSITION "composition" +#define INDB_ATTRIBUTE_LOGK "log_k" + +#define INDB_OPEN_DELIMITER_CHARGE '[' +#define INDB_CLOSE_DELIMITER_CHARGE ']' + + +namespace specmicp { +namespace database { + +void ChemyDataReader::parse() +{ + std::ifstream datafile(m_filepath, std::ifstream::binary); + Json::Reader reader; + if (not datafile.is_open()) + { + ERROR << "Database not found : " << m_filepath; + std::string message("Database not found : " + m_filepath); + throw std::invalid_argument(message); + + } + DEBUG << "Parsing database"; + bool parsingSuccessful = reader.parse( datafile, m_root); + if ( !parsingSuccessful ) + { + // report to the user the failure and their locations in the document. + std::string message("Failed to parse configuration\n" + reader.getFormatedErrorMessages()); + throw std::runtime_error(message); + } + + parse_size_db(); + parse_basis(); + parse_aqueous(); + parse_minerals(); + + datafile.close(); +} + +void ChemyDataReader::check_database() +{ + +} + +void ChemyDataReader::parse_size_db() +{ + data->nb_component = m_root[INDB_SECTION_BASIS].size(); + data->nb_aqueous = m_root[INDB_SECTION_AQUEOUS].size(); + data->nb_mineral = m_root[INDB_SECTION_MINERALS].size(); + //n_size_gas = m_root[INDB_SECTION_GAS].size(); + + data->param_aq = Eigen::MatrixXd::Zero(data->nb_component+data->nb_aqueous, 3); +} + +void ChemyDataReader::parse_basis() +{ + Json::Value& basis = get_mandatory_section(m_root, INDB_SECTION_BASIS); + DEBUG << "Size basis : " << basis.size(); + int size_basis = basis.size(); + data->labels_basis.reserve(size_basis); + + for (int id=0; idmap_labels_basis[label] = id; + data->labels_basis.push_back(label); + + data->param_aq(id, 0) = charge_from_label(label); + if (species.isMember(INDB_ATTRIBUTE_ACTIVITY)) + { + data->param_aq(id, 1) = species[INDB_ATTRIBUTE_ACTIVITY][INDB_ATTRIBUTE_ACTIVITY_A].asDouble(); + data->param_aq(id, 2) = species[INDB_ATTRIBUTE_ACTIVITY][INDB_ATTRIBUTE_ACTIVITY_B].asDouble(); + } + } +} + +void ChemyDataReader::parse_aqueous() +{ + Json::Value& aqueous = get_mandatory_section(m_root, INDB_SECTION_AQUEOUS); + data->labels_aqueous.reserve(data->nb_aqueous); + data->nu_aqueous = reaction_mat_t::Zero(data->nb_aqueous, data->nb_component); + data->logk_aqueous = logK_vector_t(data->nb_aqueous); + + for (int id=0; idnb_aqueous; ++id) + { + Json::Value& species = aqueous[id]; + check_for_mandatory_value(species, INDB_ATTRIBUTE_LABEL); + std::string label = species[INDB_ATTRIBUTE_LABEL].asString(); + data->labels_aqueous.push_back(label); + + data->param_aq(id, 0) = charge_from_label(label); + if (species.isMember(INDB_ATTRIBUTE_ACTIVITY)) + { + data->param_aq(id, 1) = species[INDB_ATTRIBUTE_ACTIVITY][INDB_ATTRIBUTE_ACTIVITY_A].asDouble(); + data->param_aq(id, 2) = species[INDB_ATTRIBUTE_ACTIVITY][INDB_ATTRIBUTE_ACTIVITY_B].asDouble(); + } + + // equation + check_for_mandatory_value(species, INDB_ATTRIBUTE_COMPOSITION); + std::map compo; + parse_equation(species[INDB_ATTRIBUTE_COMPOSITION].asString(), compo); + for (auto it=compo.begin(); it != compo.end(); ++it) + { + data->nu_aqueous(id, data->map_labels_basis[it->first]) = it->second; + } + check_for_mandatory_value(species, INDB_ATTRIBUTE_LOGK); + data->logk_aqueous(id) = species[INDB_ATTRIBUTE_LOGK].asDouble(); + } +} + +void ChemyDataReader::parse_minerals() +{ + Json::Value& minerals = get_mandatory_section(m_root, INDB_SECTION_MINERALS); + data->labels_minerals.reserve(data->nb_mineral); + data->nu_mineral = reaction_mat_t::Zero(data->nb_mineral, data->nb_component); + data->logk_mineral = logK_vector_t(data->nb_mineral); + + for (int id=0; idnb_mineral; ++id) + { + Json::Value& species = minerals[id]; + check_for_mandatory_value(species, INDB_ATTRIBUTE_LABEL); + data->labels_minerals.push_back(species[INDB_ATTRIBUTE_LABEL].asString()); + + // equation + check_for_mandatory_value(species, INDB_ATTRIBUTE_COMPOSITION); + std::map compo; + parse_equation(species[INDB_ATTRIBUTE_COMPOSITION].asString(), compo); + for (auto it=compo.begin(); it != compo.end(); ++it) + { + data->nu_mineral(id, data->map_labels_basis[it->first]) = it->second; + } + check_for_mandatory_value(species, INDB_ATTRIBUTE_LOGK); + data->logk_mineral(id) = species[INDB_ATTRIBUTE_LOGK].asDouble(); + } +} + +void parse_equation(const std::string& equation, std::map& compo) +{ + std::vector list_compo; + boost::split(list_compo, equation, [](char input){return input == ',';}, boost::token_compress_on); + + for (auto it=list_compo.begin(); it!=list_compo.end(); ++it) + { + std::string& toparse = *it; + double coeff = 0; + + boost::trim_all(toparse); + + unsigned int pos_end = 0; + while (pos_end < toparse.size()) + { + if (std::isalpha(toparse[pos_end])) // or std::isblank(toparse[pos_end])) + { + + break; + } + ++pos_end; + } + std::string label(toparse.substr(pos_end, toparse.size()-pos_end)); + boost::trim_all(label); + if (pos_end == 0) coeff =1; + else if (pos_end == 1 and toparse[0] == '-') coeff=-1; + else + { + std::string tofloat = toparse.substr(0, pos_end); + while (pos_end > 1) + { + if ((tofloat[0] == '-' or tofloat[0] == '+') + and std::isspace(tofloat[1]) ) + { + tofloat = tofloat[0] + tofloat.substr(2,pos_end-2); + --pos_end; + continue; + } + else + { + break; + } + + } + if ( tofloat[pos_end-1] == '-' ) {coeff = -1;} + else if (tofloat[pos_end-1] == '+') {coeff = +1;} + else {coeff = std::stof(tofloat);} + } + + compo[label] = coeff; + } +} + + +double charge_from_label(const std::string& label) +{ + int sign=1; + auto start= label.end(); + auto stop = label.end(); + for (auto it=label.begin(); it != label.end(); ++it) + { + if (*it == INDB_OPEN_DELIMITER_CHARGE) { start = it; } + } + if (start == label.end()) {return 0;} // no charge specified + for (auto it=start+1; it != label.end(); ++it) + { + if (*it == INDB_CLOSE_DELIMITER_CHARGE) { stop = it; } + } + if (stop == label.end()) {throw db_invalid_syntax("Missing : closing delimiter");} + start = start+1; + if (stop == start) {return 0;} // nothing inside bracket + // get the signs + if (*(stop-1) == '-') + { + sign = -1; + stop =stop-1; + } + else if (*(stop-1) == '+') + { + sign = +1; + stop = stop-1; + } + if (stop == start) + { + return sign; // just the sign, |z|=1 + } + double charge; + try + { + charge = sign*std::stof(std::string(start,stop)); + } + catch (std::invalid_argument&) + { + throw db_invalid_syntax("Bad formatting : charge"); + } + return charge; + +} + +} // end namespace specmicp +} // end namespace chemy + + +#undef INDB_SECTION_BASIS +#undef INDB_SECTION_AQUEOUS +#undef INDB_SECTION_MINERALS +#undef INDB_SECTION_GAS + +#undef INDB_ATTRIBUTE_NAME + + +#undef INDB_ATTRIBUTE_LABEL +#undef INDB_ATTRIBUTE_ACTIVITY +#undef INDB_ATTRIBUTE_ACTIVITY_A +#undef INDB_ATTRIBUTE_ACTIVITY_B + +#undef INDB_ATTRIBUTE_COMPOSITION +#undef INDB_ATTRIBUTE_LOGK + +#undef INDB_OPEN_DELIMITER_CHARGE +#undef INDB_CLOSE_DELIMITER_CHARGE diff --git a/src/database/reader.hpp b/src/database/reader.hpp new file mode 100644 index 0000000..c712f4b --- /dev/null +++ b/src/database/reader.hpp @@ -0,0 +1,107 @@ +/*------------------------------------------------------- + + - Module : database + - File : reader.hpp + - Author : Fabien Georget + + Copyright (c) 2014, Fabien Georget, Princeton University + +---------------------------------------------------------*/ + +#ifndef DATABASE_READER_H +#define DATABASE_READER_H + +//! \file reader.hpp Read the json database + +#include +#include +#include "data_container.hpp" + +namespace specmicp { +namespace database { + + +class ChemyDataReader +{ +public: + using DataContainerPtr = std::shared_ptr; + + ChemyDataReader() { + data = std::make_shared(); + } + ChemyDataReader(std::string filepath): + m_filepath(filepath) + { + data = std::make_shared(); + } + + void set_database_path(std::string filepath){ + m_filepath = filepath; + } + + void parse(); + + DataContainerPtr get_database() {return data;} + +private: + void parse_metadata(); + void parse_basis(); + void parse_aqueous(); + void parse_minerals(); + + void check_database(); + void parse_size_db(); + + std::string m_filepath; + Json::Value m_root; + + DataContainerPtr data; + +}; + +//! Parse an equation +void parse_equation(const std::string &equation, + std::map& compo); + +//! Get the charge of a species by parsing the label +double charge_from_label(const std::string& label); + + +//! This error is thrown when a bad syntax is detected in the database +class db_invalid_syntax : public std::exception +{ + std::string m_msg; + const char *m_msg_cstr; + +public: + db_invalid_syntax (const std::string &msg): + m_msg("DB Invalid Syntax :" + msg), m_msg_cstr(m_msg.c_str()) + {} + + const char *what() throw() + { + return m_msg_cstr; + } + + // ... +}; + +inline void check_for_mandatory_value(const Json::Value& root, const std::string& attribute) +{ + if (not root.isMember(attribute)) + { + throw db_invalid_syntax("Missing : mandatory parameter '" + attribute +"'."); + } +} + +inline Json::Value& get_mandatory_section(Json::Value& root, const std::string& section) +{ + check_for_mandatory_value(root, section); + return root[section]; +} + + +} // end namespace database +} // end namespace specmicp + +#endif // DATABASE_READER_H diff --git a/tests/database/test_data_reader.hpp b/tests/database/test_data_reader.hpp new file mode 100644 index 0000000..7715599 --- /dev/null +++ b/tests/database/test_data_reader.hpp @@ -0,0 +1,72 @@ +/*------------------------------------------------------- + + - Module : chemy + - File : check_data_reader.h + - Author : Fabien Georget + + Copyright (c) 2013, Fabien Georget, Princeton University + + This is a test file for databasereader. + +---------------------------------------------------------*/ + + +#include +#include "database/reader.hpp" + + +class TestSuite_DatabaseReader : public CxxTest::TestSuite +{ +public: + void test_reader() + { + specmicp::database::ChemyDataReader reader("data/specmicp_database.js"); + reader.parse(); + + specmicp::database::ChemyDataReader reader2("does_not_exist"); + TS_ASSERT_THROWS(reader2.parse(), std::invalid_argument); + + } + + void test_parse_equation() + { + std::map compo; + std::string eq = "A, 2 B, -C, -3D, SDF, - 3 de, - X"; + specmicp::database::parse_equation(eq, compo); + + TS_ASSERT_EQUALS(compo["A"],1); + TS_ASSERT_EQUALS(compo["B"],2); + TS_ASSERT_EQUALS(compo["C"],-1); + TS_ASSERT_EQUALS(compo["D"],-3); + TS_ASSERT_EQUALS(compo["SDF"],1); + TS_ASSERT_EQUALS(compo["de"],-3); + TS_ASSERT_EQUALS(compo["X"], -1); + + } + + void test_parse_charge() + { + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("neutral"), 0); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("neutral[]"), 0); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[+]"), 1); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[-]"), -1); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[2]"), 2); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[2-]"), -2); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[2+]"), +2); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[-3]"), -3); + TS_ASSERT_EQUALS(specmicp::database::charge_from_label("charge[+1]"), +1); + + TS_ASSERT_THROWS(specmicp::database::charge_from_label("charge[+3"), specmicp::database::db_invalid_syntax); + TS_ASSERT_THROWS(specmicp::database::charge_from_label("charge[ddd]"), specmicp::database::db_invalid_syntax); + } + + void test_parse() + { + specmicp::database::ChemyDataReader reader("data/specmicp_database.js"); + reader.parse(); + + std::shared_ptr thedata = reader.get_database(); + + TS_ASSERT_EQUALS(thedata->nb_component, 8); + } +};