diff --git a/src/specmicp/CMakeLists.txt b/src/specmicp/CMakeLists.txt index 5dab0d8..2134dc0 100644 --- a/src/specmicp/CMakeLists.txt +++ b/src/specmicp/CMakeLists.txt @@ -1,147 +1,80 @@ # The speciation solver # ===================== -# Directories -set(ADIMENSIONAL_DIR adimensional) -set(ADIMKINETICS_DIR adimensional_kinetics) -set(PROBLEM_SOLVER_DIR problem_solver) -set(IO_DIR io) +# var to store the files -# Headers file without a source file -add_custom_target(specmicp_incl SOURCES - ${ADIMENSIONAL_DIR}/adimensional_system_structs.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_numbering.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_pcfm_structs.hpp - - ${ADIMENSIONAL_DIR}/config_solution_output_format.h - ${ADIMENSIONAL_DIR}/config_default_options_solver.h - - - ${PROBLEM_SOLVER_DIR}/formulation.hpp - - ${ADIMKINETICS_DIR}/kinetic_model.hpp - ${ADIMKINETICS_DIR}/kinetic_variables.hpp - ${ADIMKINETICS_DIR}/kinetic_system_solver_structs.hpp +set(specmicp_srcs "" + CACHE INTERNAL "specmicp files" FORCE) +set(specmicp_headers "" + CACHE INTERNAL "specmicp headers" FORCE ) -# Source for the library -set(SPECMICP_LIBFILE - - ${ADIMENSIONAL_DIR}/adimensional_system.cpp - ${ADIMENSIONAL_DIR}/adimensional_system_solver.cpp - - ${ADIMENSIONAL_DIR}/adimensional_system_solver_structs.cpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution_extractor.cpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution_saver.cpp - ${ADIMENSIONAL_DIR}/adimensional_system_pcfm.cpp - ${ADIMENSIONAL_DIR}/equilibrium_curve.cpp - - ${PROBLEM_SOLVER_DIR}/dissolver.cpp - - ${IO_DIR}/configuration.cpp - ${IO_DIR}/print.cpp - - # kinetic - ${ADIMKINETICS_DIR}/kinetic_system.cpp - ${ADIMKINETICS_DIR}/kinetic_system_solver.cpp - ${ADIMKINETICS_DIR}/kinetic_system_euler_solver.cpp +# macro to add to vars + +macro(add_to_specmicp_srcs_list LIST_NAME) + set(tmp "") + foreach (src ${${LIST_NAME}}) + list(APPEND tmp ${CMAKE_CURRENT_SOURCE_DIR}/${src}) + endforeach(src) + set(specmicp_srcs "${specmicp_srcs};${tmp}" CACHE INTERNAL + "specmicp files" FORCE) +endmacro(add_to_specmicp_srcs_list) + + +macro(add_to_specmicp_headers_list LIST_NAME) + set( tmp "") + foreach(header ${${LIST_NAME}}) + LIST(APPEND tmp ${CMAKE_CURRENT_SOURCE_DIR}/${header}) + endforeach(header) + set(specmicp_headers "${specmicp_headers};${tmp}" + CACHE INTERNAL "specmicp headers" FORCE) +endmacro(add_to_specmicp_headers_list) + +set(specmicp_main_headers + specmicp.hpp ) +add_to_specmicp_headers_list(specmicp_main_headers) -if (HDF5_FOUND) - list(APPEND SPECMICP_LIBFILE - ${IO_DIR}/hdf5_adimensional.cpp - ) - - include_directories(${HDF5_INCLUDE_DIRS}) - set_source_files_properties( - ${IO_DIR}/hdf5_adimensional.cpp - PROPERTIES COMPILE_DEFINITIONS HDF5_DEFINITIONS - ) -endif() - +add_subdirectory(adimensional) +add_subdirectory(adimensional_kinetics) +add_subdirectory(problem_solver) +add_subdirectory(io) -set_visibility_flag(${SPECMICP_LIBFILE}) -set_pgo_flag(${SPECMICP_LIBFILE}) +set_visibility_flag(${specmicp_srcs}) +set_pgo_flag(${specmicp_srcs}) if(SPECMICP_DEBUG_EQUATION_FD_JACOBIAN) set_source_files_properties(${ADIMENSIONAL_DIR}/adimensional_system.cpp PROPERTIES COMPILE_DEFINITIONS "SPECMICP_DEBUG_EQUATION_FD_JACOBIAN" ) endif() -add_library(objspecmicp OBJECT ${SPECMICP_LIBFILE}) +add_library(objspecmicp OBJECT + ${specmicp_srcs} + ${specmicp_headers} +) set_property(TARGET objspecmicp PROPERTY POSITION_INDEPENDENT_CODE 1) add_library(specmicp SHARED $) install(TARGETS specmicp LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} ) -# include files -# -------------- - -set(SPECMICP_ADIMENSIONAL_INCLUDES - ${ADIMENSIONAL_DIR}/adimensional_system_structs.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution_saver.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solver_structs.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_numbering.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_pcfm_structs.hpp - ${ADIMENSIONAL_DIR}/adimensional_system.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solver.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_solution_extractor.hpp - ${ADIMENSIONAL_DIR}/adimensional_system_pcfm.hpp - ${ADIMENSIONAL_DIR}/equilibrium_curve.hpp - -) -set(SPECMICP_PROBLEM_SOLVER_INCLUDES - ${PROBLEM_SOLVER_DIR}/dissolver.hpp - ${PROBLEM_SOLVER_DIR}/formulation.hpp -) -set(SPECMICP_ADIMENSIONAL_KINETICS_INCLUDES - ${ADIMKINETICS_DIR}/kinetic_model.hpp - ${ADIMKINETICS_DIR}/kinetic_variables.hpp - ${ADIMKINETICS_DIR}/kinetic_system_solver_structs.hpp - ${ADIMKINETICS_DIR}/kinetic_system.hpp - ${ADIMKINETICS_DIR}/kinetic_system_solver.hpp - ${ADIMKINETICS_DIR}/kinetic_system_euler_solver.hpp -) - -set(SPECMICP_IO_INCLUDES - ${IO_DIR}/configuration.hpp - ${IO_DIR}/print.hpp -) - -install(FILES ${SPECMICP_ADIMENSIONAL_INCLUDES} - DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/adimensional -) -install(FILES ${SPECMICP_PROBLEM_SOLVER_INCLUDES} - DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/problem_solver -) -install(FILES ${SPECMICP_ADIMENSIONAL_KINETICS_INCLUDES} - DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/adimensional_kinetics -) - -install(FILES ${SPECMICP_IO_INCLUDES} - DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/io -) - # static libraries # ---------------- if(SPECMICP_BUILD_STATIC) add_library(specmicp_static STATIC $) install(TARGETS specmicp_static ARCHIVE DESTINATION ${STATIC_LIBRARY_INSTALL_DIR} ) else() add_library(specmicp_static EXCLUDE_FROM_ALL STATIC $) endif() set_target_properties(specmicp_static PROPERTIES OUTPUT_NAME specmicp) diff --git a/src/specmicp/adimensional/CMakeLists.txt b/src/specmicp/adimensional/CMakeLists.txt new file mode 100644 index 0000000..457f037 --- /dev/null +++ b/src/specmicp/adimensional/CMakeLists.txt @@ -0,0 +1,36 @@ +set( specmicp_adim_srcs + adimensional_system.cpp + adimensional_system_pcfm.cpp + adimensional_system_solution_extractor.cpp + adimensional_system_solution_saver.cpp + adimensional_system_solver.cpp + adimensional_system_solver_structs.cpp + equilibrium_curve.cpp +) + +set( specmicp_adim_headers_to_install + adimensional_system.hpp + adimensional_system_numbering.hpp + adimensional_system_pcfm.hpp + adimensional_system_pcfm_structs.hpp + adimensional_system_solution_saver.hpp + adimensional_system_solution.hpp + adimensional_system_solution_extractor.hpp + adimensional_system_solver.hpp + adimensional_system_solver_structs.hpp + adimensional_system_structs.hpp + equilibrium_curve.hpp +) + +set( specmicp_adim_headers + config_default_options_solver.h + config_solution_output_format.h +) +LIST(APPEND specmicp_adim_headers ${specmicp_adim_headers_to_install}) + +add_to_specmicp_srcs_list(specmicp_adim_srcs) +add_to_specmicp_headers_list(specmicp_adim_headers) + +INSTALL(FILES ${specmicp_adim_headers_to_install} + DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/adimensional +) diff --git a/src/specmicp/adimensional/adimensional_system.cpp b/src/specmicp/adimensional/adimensional_system.cpp index c6b27e1..874718f 100644 --- a/src/specmicp/adimensional/adimensional_system.cpp +++ b/src/specmicp/adimensional/adimensional_system.cpp @@ -1,1430 +1,1430 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "adimensional_system.hpp" -#include "../../utils/log.hpp" +#include "adimensional_system_solution.hpp" -#include "../../physics/constants.hpp" -#include "../../physics/laws.hpp" +#include "specmicp_common/log.hpp" -#include "adimensional_system_solution.hpp" +#include "specmicp_common/physics/constants.hpp" +#include "specmicp_common/physics/laws.hpp" +#include #include #include // uncomment to activate the finite difference jacobian // #define SPECMICP_DEBUG_EQUATION_FD_JACOBIAN namespace specmicp { //constexpr scalar_t log10f() const {return std::log(10.0);} //constexpr scalar_t log10 = log10f(); static const scalar_t log10 = std::log(10.0); // Constructor // =========== // No previous solution // -------------------- AdimensionalSystem::AdimensionalSystem( RawDatabasePtr& ptrdata, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemOptions& options, const units::UnitsSet& units_set ): AdimemsionalSystemNumbering(ptrdata), OptionsHandler(options), units::UnitBaseClass(units_set), m_inert_volume_fraction(constraints.inert_volume_fraction), m_second(ptrdata), m_equations(total_dofs(), ptrdata) { specmicp_assert(ptrdata->is_valid()); m_fixed_values.setZero(ptrdata->nb_component()+1); number_eq(constraints); } // Previous solution // ----------------- AdimensionalSystem::AdimensionalSystem( RawDatabasePtr& ptrdata, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemOptions& options, const units::UnitsSet& units_set ): AdimemsionalSystemNumbering(ptrdata), OptionsHandler(options), units::UnitBaseClass(units_set), m_inert_volume_fraction(constraints.inert_volume_fraction), m_second(previous_solution), m_equations(total_dofs(), ptrdata) { specmicp_assert(ptrdata->is_valid()); m_fixed_values.setZero(ptrdata->nb_component()+1); number_eq(constraints); } // Secondary variables constructor // =============================== // No previous solution // -------------------- AdimensionalSystem::SecondaryVariables::SecondaryVariables( const RawDatabasePtr& data ): secondary_molalities(Vector::Zero(data->nb_aqueous())), loggamma(Vector::Zero(data->nb_component()+data->nb_aqueous())), gas_fugacity(Vector::Zero(data->nb_gas())), gas_concentration(Vector::Zero(data->nb_gas())), sorbed_concentrations(Vector::Zero(data->nb_sorbed())) {} // Previous solution // ----------------- AdimensionalSystem::SecondaryVariables::SecondaryVariables( const AdimensionalSystemSolution& previous_solution ): secondary_molalities(previous_solution.secondary_molalities), loggamma(previous_solution.log_gamma), gas_fugacity(previous_solution.gas_fugacities), gas_concentration(Vector::Zero(previous_solution.gas_fugacities.rows())), sorbed_concentrations(previous_solution.sorbed_molalities) {} // IdEquations constructor // ======================= AdimensionalSystem::IdEquations::IdEquations( index_t nb_dofs, const RawDatabasePtr& data ): ideq(nb_dofs, no_equation), component_equation_type(data->nb_component()+1, no_equation), fixed_activity_species(data->nb_component()+1, no_species), active_aqueous(data->nb_aqueous(), false), active_gas(data->nb_gas(), false), active_sorbed(data->nb_sorbed()) {} // Equation numbering // ================== // Note : this function also computes scaling factor that would be used in the computation // ------ void AdimensionalSystem::number_eq( const AdimensionalSystemConstraints& constraints ) { index_t neq = 0; // units compute_scaling_factors(); // Water // ===== if (constraints.water_equation != WaterEquationType::NoEquation) { m_equations.type_equation(dof_water()) = static_cast(constraints.water_equation); if (constraints.water_equation == WaterEquationType::MassConservation) { m_fixed_values(dof_water()) = constraints.total_concentrations(dof_water()); if (constraints.water_partial_pressure.use_partial_pressure_model) { m_equations.use_water_pressure_model = true; m_equations.water_pressure_model = constraints.water_partial_pressure.partial_pressure_model; } } m_equations.add_equation(dof_water(), &neq); } // Aqueous components // ================== number_eq_aqueous_component(constraints, neq); // Surface model // ============= if (constraints.surface_model.model_type == SurfaceEquationType::Equilibrium) { // add the equation m_equations.add_equation(dof_surface(), &neq); m_equations.type_equation(dof_surface()) = static_cast(constraints.surface_model.model_type); // setup the total concentration m_fixed_values(dof_surface()) = constraints.surface_model.concentration; } // Secondary species // ================= // Secondary aqueous species // ------------------------- bool include_half_cell_reaction = (constraints.electron_constraint.equation_type != ElectronEquationType::NoEquation); bool solve_electron_equation {false}; for (auto j: m_data->range_aqueous()) { bool can_exist { true }; if ( include_half_cell_reaction or not m_data->is_half_cell_reaction(j)) for (const auto& k: m_equations.nonactive_component) { if (m_data->nu_aqueous(j, k) != 0.0) { can_exist = false; break; } } else { can_exist = false; } m_equations.set_aqueous_active(j, can_exist); if (can_exist and m_data->is_half_cell_reaction(j)) solve_electron_equation = true; //std::cout << m_data->labels_aqueous[j] << "can exist ? " << can_exist << " - logk : " << m_data->logk_aqueous(j) << std::endl; } // Gas // --- for (index_t k: m_data->range_gas()) { bool can_exist = true; for (const index_t& n: m_equations.nonactive_component) { if (m_data->nu_gas(k, n) != 0.0) { can_exist = false; break; } } m_equations.set_gas_active(k, can_exist); } // Sorbed species // -------------- for (index_t s: m_data->range_sorbed()) { // Check if the surface model is computed if (constraints.surface_model.model_type != SurfaceEquationType::Equilibrium) { m_equations.set_sorbed_active(s, false); continue; } // If so, check that all components of the sorbed species exist bool can_exist = true; for (const index_t& k: m_equations.nonactive_component) { if (m_data->nu_sorbed(s, k) != 0.0) { can_exist = false; break; } } m_equations.set_sorbed_active(s, can_exist); } // Electron equation // ----------------- if (solve_electron_equation) { m_equations.add_equation(dof_electron(), &neq); m_equations.type_equation(dof_electron()) = static_cast(constraints.electron_constraint.equation_type); if (constraints.electron_constraint.equation_type == ElectronEquationType::Equilibrium) { m_fixed_values(dof_electron()) = 0.0; } else if (constraints.electron_constraint.equation_type == ElectronEquationType::FixedpE) { m_fixed_values(dof_electron()) = constraints.electron_constraint.fixed_value; m_equations.related_species(dof_electron()) = constraints.electron_constraint.species; //assert(m_fixed_activity_species[dof_electron()] >= 0 // and m_fixed_activity_species[dof_electron()] < m_data->nb_aqueous()); //assert(m_data->is_half_cell_reaction(m_fixed_activity_species[dof_electron()])); } // scaling if (get_options().scaling_electron == 0.0) { for (index_t component : m_data->range_aqueous_component()) { if (aqueous_component_equation_type(component) == AqueousComponentEquationType::MassConservation) { get_options().scaling_electron = total_concentration_bc(component); break; } } } } // above equations are 'free' (i.e. non constrained) m_equations.nb_free_variables = neq; // following equations are complementarity conditions // Minerals // ======== for (index_t m: m_data->range_mineral()) { bool can_precipitate = true; // just check that the molar volume exist auto molar_volume = m_data->molar_volume_mineral(m); // Remove minerals that cannot precipitate for (index_t& k: m_equations.nonactive_component) { if (m_data->nu_mineral(m, k) != 0.0 and molar_volume > 0.0) { can_precipitate = false; break; // this is not a mineral that can precipitate } } if (can_precipitate) { m_equations.add_equation(dof_mineral(m), &neq); } } m_equations.nb_tot_variables = neq; m_equations.nb_complementarity_variables = m_equations.nb_tot_variables - m_equations.nb_free_variables; } void AdimensionalSystem::number_eq_aqueous_component( const AdimensionalSystemConstraints& constraints, index_t& neq ) { using EqT = AqueousComponentEquationType; // First set the charge keeper if (constraints.charge_keeper != no_species) { if (constraints.charge_keeper == 0 or constraints.charge_keeper > m_data->nb_component()) { throw std::invalid_argument("The charge keeper must be an aqueous component. Invalid argument : " + std::to_string(constraints.charge_keeper)); } m_equations.type_equation(dof_component(constraints.charge_keeper)) = static_cast(EqT::ChargeBalance); } // Then go over fix fugacity gas for (const auto& it: constraints.fixed_fugacity_cs) { if (m_equations.type_equation(dof_component(it.id_component)) != static_cast(EqT::NoEquation)) { throw std::invalid_argument("Component '" + m_data->components.get_label(it.id_component) + "' is already constrained, a fixed fugacity condition can not be applied"); } m_equations.type_equation(dof_component(it.id_component)) = static_cast(EqT::FixedFugacity); m_fixed_values(it.id_component) = it.log_value; m_equations.related_species(it.id_component) = it.id_gas; } // Then over the fixed activity species for (const auto& it: constraints.fixed_activity_cs) { if (m_equations.type_equation(dof_component(it.id_component)) != static_cast(EqT::NoEquation)) { throw std::invalid_argument("Component '" + m_data->components.get_label(it.id_component) + "' is already constrained, a fixed activity condition can not be applied."); } m_equations.type_equation(dof_component(it.id_component)) = static_cast(EqT::FixedActivity); m_fixed_values(it.id_component) = it.log_value; } // Then the fixed molality components for (const auto& it: constraints.fixed_molality_cs) { if (m_equations.type_equation(dof_component(it.id_component)) != static_cast(EqT::NoEquation)) { throw std::invalid_argument("Component '" + m_data->components.get_label(it.id_component) + "' is already constrained, a fixed molality condition can not be applied."); } m_equations.type_equation(dof_component(it.id_component)) = static_cast(EqT::FixedMolality); m_fixed_values(it.id_component) = it.log_value; } // Finally number the equations for (index_t component: m_data->range_aqueous_component()) { // If no equation is assigned yet if (m_equations.type_equation(dof_component(component)) == static_cast(EqT::NoEquation)) { // Mass is conserved for this component //###FIXME: H[+], HO[-] const scalar_t& total_concentration = constraints.total_concentrations(dof_component(component)); if (std::abs(total_concentration) > get_options().cutoff_total_concentration) { m_equations.type_equation(dof_component(component)) = static_cast(EqT::MassConservation); m_fixed_values(dof_component(component)) = total_concentration; m_equations.add_equation(component, &neq); } else // add component to the nonactive component list { m_equations.add_non_active_component(component); } } // If equation is already assigned else { m_equations.add_equation(component, &neq); } } if (stdlog::ReportLevel() >= logger::Debug and m_equations.nonactive_component.size() > 0) { // if in debug mode list the non active components DEBUG << "Non active components :"; for (auto it: m_equations.nonactive_component) { DEBUG << " - " << it; } } } // Units // ================= void AdimensionalSystem::set_units(const units::UnitsSet& unit_set) { units::UnitBaseClass::set_units(unit_set); compute_scaling_factors(); } void AdimensionalSystem::compute_scaling_factors() { m_scaling_molar_volume = m_data->scaling_molar_volume(get_units().length); // Unit scaling for the gaseous total concentration // transform mol/m^3 into the right unit (pressure are always in Pa) switch (get_units().length) { case units::LengthUnit::decimeter: m_scaling_gas = 1e-3; break; case units::LengthUnit::centimeter: m_scaling_gas = 1e-6; break; default: m_scaling_gas = 1.0; break; } } // ================ // // // // Residuals // // // // ================ // scalar_t AdimensionalSystem::weigthed_sum_aqueous(index_t component) const { scalar_t sum = 0.0; for (index_t aqueous: m_data->range_aqueous()) { if (not is_aqueous_active(aqueous)) continue; sum += m_data->nu_aqueous(aqueous,component)*secondary_molality(aqueous); } return sum; } scalar_t AdimensionalSystem::diff_weigthed_sum_aqueous(index_t diff_component, index_t component) const { scalar_t sum = 0.0; for (index_t aqueous: m_data->range_aqueous()) { if (not is_aqueous_active(aqueous)) continue; sum += log10*m_data->nu_aqueous(aqueous,diff_component)*m_data->nu_aqueous(aqueous,component)*secondary_molality(aqueous); } return sum; } scalar_t AdimensionalSystem::weigthed_sum_sorbed(index_t component) const { scalar_t sum = 0.0; for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; sum += m_data->nu_sorbed(s, component)*sorbed_species_concentration(s); } return sum; } scalar_t AdimensionalSystem::diff_weigthed_sum_sorbed(index_t diff_component, index_t component) const { scalar_t sum = 0.0; for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; sum += log10*m_data->nu_sorbed(s, diff_component)*m_data->nu_sorbed(s, component)*sorbed_species_concentration(s); } return sum; } scalar_t AdimensionalSystem::diff_surface_weigthed_sum_sorbed(index_t component) const { scalar_t sum = 0.0; for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; sum += log10*m_data->nb_sorption_sites(s)*m_data->nu_sorbed(s, component)*sorbed_species_concentration(s); } return sum; } scalar_t AdimensionalSystem::weigthed_sum_mineral(const Vector& x, index_t component) const { scalar_t sum = 0.0; for (index_t m: m_data->range_mineral()) { if (ideq_min(m) == no_equation or m_data->nu_mineral(m, component) == 0.0) continue; const auto concentration = volume_fraction_mineral(x, m)/molar_volume_mineral(m); sum += m_data->nu_mineral(m, component)*concentration; } return sum; } scalar_t AdimensionalSystem::weigthed_sum_gas(index_t component) const { scalar_t sum = 0.0; for (index_t k: m_data->range_gas()) { if (not is_active_gas(k) or m_data->nu_gas(k, component) == 0.0) continue; sum += m_data->nu_gas(k, component)*gas_concentration(k); } return sum; } scalar_t AdimensionalSystem::diff_weigthed_sum_gas(index_t diff_component, index_t component) const { scalar_t sum = 0.0; for (index_t k: m_data->range_gas()) { if (not is_active_gas(k) or m_data->nu_gas(k, component) == 0.0) continue; sum += log10*m_data->nu_gas(k, diff_component)*m_data->nu_gas(k, component)*gas_concentration(k); } return sum; } scalar_t AdimensionalSystem::residual_water_conservation(const Vector& x) const { specmicp_assert(water_equation_type() == WaterEquationType::MassConservation); scalar_t res = total_concentration_bc(0); const scalar_t conc_w = density_water() * volume_fraction_water(x); res = total_concentration_bc(0); res -= conc_w/m_data->molar_mass_basis(0); res -= conc_w*weigthed_sum_aqueous(0); if (ideq_surf() != no_equation) res -= conc_w*weigthed_sum_sorbed(0); res -= weigthed_sum_mineral(x, 0); if (m_data->nb_gas() > 0) res -= weigthed_sum_gas(0); if (m_equations.use_water_pressure_model) { // water pressure const scalar_t porosity = m_second.porosity; scalar_t sat_w = volume_fraction_water(x) / porosity; if (sat_w < 1) { const scalar_t pressure = m_equations.water_pressure_model(sat_w); res -= m_scaling_gas*(porosity - volume_fraction_water(x))*( pressure / (constants::gas_constant*temperature())); } } res /= total_concentration_bc(0); return res; } scalar_t AdimensionalSystem::residual_water_saturation(const Vector& x) const { specmicp_assert(water_equation_type() == WaterEquationType::SaturatedSystem); scalar_t res = 1 - volume_fraction_water(x) - m_inert_volume_fraction; for (index_t mineral: m_data->range_mineral()) { res -= volume_fraction_mineral(x, mineral); } return res; } scalar_t AdimensionalSystem::residual_component(const Vector &x, index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::MassConservation); const scalar_t conc_w = density_water()*volume_fraction_water(x); scalar_t res = total_concentration_bc(component); res -= conc_w*component_molality(x, component); res -= conc_w*weigthed_sum_aqueous(component); if (ideq_surf() != no_equation) res -= conc_w*weigthed_sum_sorbed(component); res -= weigthed_sum_mineral(x, component); if (m_data->nb_gas() > 0) res -= weigthed_sum_gas(component); res /= total_concentration_bc(component); return res; } scalar_t AdimensionalSystem::residual_fixed_activity(const Vector& x, index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedActivity); scalar_t res = (fixed_activity_bc(component) - log_gamma_component(component) - log_component_molality(x, component) ); res /= fixed_activity_bc(component); return res; } scalar_t AdimensionalSystem::residual_fixed_molality(const Vector& x, index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedMolality); scalar_t res = (fixed_molality_bc(component) - log_component_molality(x, component) ); res /= fixed_molality_bc(component); return res; } scalar_t AdimensionalSystem::residual_fixed_fugacity(const Vector& x, index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedFugacity); index_t id_g = m_equations.fixed_activity_species[component]; scalar_t res = fixed_fugacity_bc(component) + m_data->logk_gas(id_g); for (index_t component: m_data->range_aqueous_component()) { if (m_data->nu_gas(id_g, component) == 0) continue; res -= m_data->nu_gas(id_g, component)*( log_gamma_component(component) + log_component_molality(x, component)); } res /= fixed_fugacity_bc(component); return res; } scalar_t AdimensionalSystem::residual_mineral(const Vector& x, index_t m) const { specmicp_assert(ideq_min(m) != no_equation); scalar_t res = m_data->logk_mineral(m); for (index_t i: m_data->range_aqueous_component()) { if (m_data->nu_mineral(m, i) != 0) { const auto log_activity_i = log_component_molality(x, i) + log_gamma_component(i); res -= m_data->nu_mineral(m, i)*log_activity_i; } } if (ideq_electron() != no_equation and m_data->is_mineral_half_cell_reaction(m)) res -= m_data->nu_mineral(m, m_data->electron_index())*log_activity_electron(x); return res; } scalar_t AdimensionalSystem::residual_charge_conservation(const Vector& x) const { scalar_t res = 0.0; for (index_t i: m_data->range_aqueous_component()) { if (m_data->charge_component(i) != 0 and ideq_paq(i) != no_equation) res += m_data->charge_component(i)*component_molality(x, i); } for (index_t j: m_data->range_aqueous()) { if (m_data->charge_aqueous(j) == 0 and not is_aqueous_active(j)) continue; res += m_data->charge_aqueous(j)*secondary_molality(j); } return res; } scalar_t AdimensionalSystem::residual_surface(const Vector &x) const { specmicp_assert(ideq_surf() != no_equation); const scalar_t conc_w = density_water()*volume_fraction_water(x); scalar_t res = surface_total_concentration() - conc_w*free_sorption_site_concentration(x); for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; res -= conc_w*m_data->nb_sorption_sites(s)*sorbed_species_concentration(s); } return res/surface_total_concentration(); } scalar_t AdimensionalSystem::residual_electron(const Vector &x) const { specmicp_assert(electron_equation_type() == ElectronEquationType::Equilibrium); const scalar_t conc_w = density_water()*volume_fraction_water(x); scalar_t res = 0.0; res -= conc_w * weigthed_sum_aqueous(m_data->electron_index()); if (ideq_surf() != no_equation) res -= conc_w * weigthed_sum_sorbed(m_data->electron_index()); res -= weigthed_sum_mineral(x, m_data->electron_index()); if (m_data->nb_gas() > 0) res -= weigthed_sum_gas(m_data->electron_index()); return res/get_options().scaling_electron; } void AdimensionalSystem::get_residuals(const Vector& x, Vector& residual) { residual.resize(total_variables()); // initialisation of 'main' secondary variables // They are especially nessary if the finite difference jacobian is used // and for the linesearch m_second.porosity = 1 - sum_volume_fraction_minerals(x); set_volume_fraction_gas_phase(x); set_secondary_concentration(x); set_sorbed_concentrations(x); // // water if (ideq_w() != no_equation) { switch (water_equation_type()) { case WaterEquationType::MassConservation: residual(ideq_w()) = residual_water_conservation(x); break; case WaterEquationType::SaturatedSystem: residual(ideq_w()) = residual_water_saturation(x); case WaterEquationType::NoEquation: break; } } // aqueous component for (index_t i: m_data->range_aqueous_component()) { switch (aqueous_component_equation_type(i)) { case AqueousComponentEquationType::NoEquation: break; case AqueousComponentEquationType::MassConservation: residual(ideq_paq(i)) = residual_component(x, i); break; case AqueousComponentEquationType::ChargeBalance: residual(ideq_paq(i)) = residual_charge_conservation(x); break; case AqueousComponentEquationType::FixedActivity: residual(ideq_paq(i)) = residual_fixed_activity(x, i); break; case AqueousComponentEquationType::FixedFugacity: residual(ideq_paq(i)) = residual_fixed_fugacity(x, i); break; case AqueousComponentEquationType::FixedMolality: residual(ideq_paq(i)) = residual_fixed_molality(x, i); break; } } // surface if (ideq_surf() != no_equation) residual(ideq_surf()) = residual_surface(x); // mineral for (index_t m: m_data->range_mineral()) { if (ideq_min(m) != no_equation) residual(ideq_min(m)) = residual_mineral(x, m); } // electron if (ideq_electron() != no_equation ) residual(ideq_electron()) = residual_electron(x); // std::cout << residual << std::endl; } // ================ // // // // Jacobian // // // // ================ // void AdimensionalSystem::get_jacobian(Vector& x, Matrix& jacobian) // //non-optimized Finite difference, for test only #ifdef SPECMICP_DEBUG_EQUATION_FD_JACOBIAN { finite_difference_jacobian(x, jacobian); return; } #else // analytical jacobian { analytical_jacobian(x, jacobian); return; } #endif void AdimensionalSystem::finite_difference_jacobian(Vector& x, Matrix& jacobian) { const int neq = total_variables(); Eigen::VectorXd res(neq); Eigen::VectorXd perturbed_res(neq); jacobian.setZero(neq, neq); get_residuals(x, res); for (int j=0; jmolar_mass_basis(0); tmp -= weigthed_sum_aqueous(0); tmp -= weigthed_sum_sorbed(0); tmp *= rho_w; tmp -= -weigthed_sum_gas(0); if (m_equations.use_water_pressure_model) { // The jacobian of the pressure model is // computed using finite difference const scalar_t sat_w = volume_fraction_water(x)/ porosity(x); if (sat_w >= 1.0) { // doesn't make sense to have a saturation // bigger than one in this case ERROR << "Saturation greater that one detected" << ", skip pressure model"; } else { // compute the perturbation scalar_t sp = sat_w*(1.0 + eps_jacobian); if (sp == 0.0) sp = eps_jacobian; const scalar_t h = sp - sat_w; // can we save one call here ? cache const scalar_t pv_sds = m_equations.water_pressure_model(sp); const scalar_t pv_s = m_equations.water_pressure_model(sat_w); const scalar_t diff = (pv_sds - pv_s) / h; // add the contribution tmp -= m_scaling_gas/(constants::gas_constant*temperature()) *( (1.0-sat_w)*diff - pv_s ); } } jacobian(idw, idw) = tmp/factor; const scalar_t conc_w = density_water()*volume_fraction_water(x); for (index_t k: m_data->range_aqueous_component()) { if (ideq_paq(k) == no_equation) continue; scalar_t tmp = 0.0; tmp -= diff_weigthed_sum_aqueous(k, 0); tmp -= diff_weigthed_sum_sorbed(k, 0); // fixme gas tmp *= conc_w; tmp -= diff_weigthed_sum_gas(k, 0); jacobian(idw, ideq_paq(k)) = tmp/factor; } if (ideq_electron() != no_equation) { scalar_t tmp = 0.0; tmp -= diff_weigthed_sum_aqueous(m_data->electron_index(), 0); tmp -= diff_weigthed_sum_sorbed(m_data->electron_index(), 0); tmp*= conc_w; tmp -= diff_weigthed_sum_gas(m_data->electron_index(), 0); jacobian(idw, ideq_electron()) = tmp/factor; } for (index_t m: m_data->range_mineral()) { if (ideq_min(m) == no_equation) continue; jacobian(idw, ideq_min(m)) = -m_data->nu_mineral(m, 0)/molar_volume_mineral(m)/factor; } } else if (water_equation_type() == WaterEquationType::SaturatedSystem) { const index_t idw = ideq_w(); jacobian(idw, idw) = -1; for (index_t m: m_data->range_mineral()) { if (ideq_min(m) == no_equation) continue; jacobian(idw, ideq_min(m)) = -1; } } } void AdimensionalSystem::jacobian_aqueous_components(Vector& x, Matrix& jacobian) { for (index_t i: m_data->range_aqueous_component()) { const index_t idp = ideq_paq(i); if (idp == no_equation) continue; switch (aqueous_component_equation_type(i)) { case AqueousComponentEquationType::NoEquation: continue; // Mass balance equation // ===================== case AqueousComponentEquationType::MassConservation: { const scalar_t conc_w = density_water()*volume_fraction_water(x); const scalar_t factor = total_concentration_bc(i); // Aqueous components for (index_t k: m_data->range_aqueous_component()) { if (ideq_paq(k) == no_equation) continue; scalar_t tmp_iip = 0; if (k == i) tmp_iip -= component_molality(x, i)*log10; tmp_iip -= diff_weigthed_sum_aqueous(k, i); tmp_iip -= diff_weigthed_sum_sorbed(k, i); tmp_iip *= conc_w; tmp_iip -= diff_weigthed_sum_gas(k, i); jacobian(idp, ideq_paq(k)) = tmp_iip/factor; } // Minerals for (index_t m: m_data->range_mineral()) { if (ideq_min(m) == no_equation) continue; jacobian(idp, ideq_min(m)) = - m_data->nu_mineral(m, i)/molar_volume_mineral(m)/factor; } // Water if (ideq_w() != no_equation) { scalar_t tmp_iw = -component_molality(x, i); tmp_iw -= weigthed_sum_aqueous(i); tmp_iw -= weigthed_sum_sorbed(i); tmp_iw *= density_water(); jacobian(idp, ideq_w()) = tmp_iw/factor; } // Surface if (ideq_surf() != no_equation) { scalar_t tmp_s = -conc_w*diff_surface_weigthed_sum_sorbed(i); jacobian(idp, ideq_surf()) = tmp_s/factor; } // Electron if (ideq_electron() != no_equation) { scalar_t tmp = 0.0; tmp -= diff_weigthed_sum_aqueous(m_data->electron_index(), i); tmp -= diff_weigthed_sum_sorbed(m_data->electron_index(), i); tmp*= conc_w; tmp -= diff_weigthed_sum_gas(m_data->electron_index(), i); jacobian(idp, ideq_electron()) = tmp/factor; } break; } // Charge balance equation // ======================= case AqueousComponentEquationType::ChargeBalance: { // Aqueous components for (index_t k: m_data->range_aqueous_component()) { const index_t idc = ideq_paq(k); if (idc == no_equation) continue; scalar_t tmp_drdb = 0.0; if (m_data->charge_component(k) != 0.0) tmp_drdb = m_data->charge_component(k); // Secondary species for (index_t j: m_data->range_aqueous()) { if ( not is_aqueous_active(j) or m_data->nu_aqueous(j, k) == 0.0 or m_data->charge_aqueous(j) == 0.0 ) continue; scalar_t tmp_value = m_data->nu_aqueous(j, k)*m_data->charge_aqueous(j); tmp_value *= secondary_molality(j)/component_molality(x, k); tmp_drdb += tmp_value; } jacobian(idp, idc) += component_molality(x,k)*log10*tmp_drdb; } break; } // Fixed activity equation // ======================= case AqueousComponentEquationType::FixedActivity: { jacobian(idp, idp) = -1.0/fixed_activity_bc(i); break; } // Fixed fugacity equation // ======================= case AqueousComponentEquationType::FixedFugacity: { index_t id_g = m_equations.fixed_activity_species[i]; for (index_t k: m_data->range_aqueous_component()) { if (ideq_paq(k) == no_equation or m_data->nu_gas(id_g, k) == 0.0) continue; jacobian(idp, ideq_paq(k)) = -m_data->nu_gas(id_g, k)/fixed_fugacity_bc(i); } break; } // end case // Fixed molality component // ======================== case AqueousComponentEquationType::FixedMolality: { jacobian(idp, idp) = -1.0/fixed_molality_bc(i); break; } } // end switch } } void AdimensionalSystem::jacobian_minerals(Vector& x, Matrix& jacobian) { for (index_t m: m_data->range_mineral()) { const index_t idm = ideq_min(m); if (idm == no_equation) continue; for (index_t i: m_data->range_aqueous_component()) { if (ideq_paq(i) == no_equation) continue; jacobian(idm, ideq_paq(i)) = -m_data->nu_mineral(m, i); } if (ideq_electron() != no_equation and m_data->is_mineral_half_cell_reaction(m)) jacobian(idm, ideq_electron()) = -m_data->nu_mineral(m, m_data->electron_index()); } } void AdimensionalSystem::jacobian_surface(Vector& x, Matrix& jacobian) { const index_t ids = ideq_surf(); const scalar_t factor = surface_total_concentration(); const scalar_t conc_w = density_water()*volume_fraction_water(x); specmicp_assert(ids != no_equation); scalar_t tmp_s = - free_sorption_site_concentration(x); for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; tmp_s -= m_data->nb_sorption_sites(s)* m_data->nb_sorption_sites(s)*sorbed_species_concentration(s); } jacobian(ids, ids) = conc_w*log10 * tmp_s / factor; // water const index_t idw = ideq_w(); if (idw != no_equation) { const scalar_t rho_w = density_water(); scalar_t tmp_w = - free_sorption_site_concentration(x); for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) continue; tmp_w -= m_data->nb_sorption_sites(s)*sorbed_species_concentration(s); } jacobian(ids, idw) = rho_w * tmp_w / factor; } // component for (index_t k: m_data->range_aqueous_component()) { const index_t idk = ideq_paq(k); if (idk == no_equation) continue; scalar_t tmp_k = - conc_w*diff_surface_weigthed_sum_sorbed(k); jacobian(ids, idk) = tmp_k/factor; } if (ideq_electron() != no_equation) { scalar_t tmp_e = - conc_w*diff_surface_weigthed_sum_sorbed(m_data->electron_index()); jacobian(ids, ideq_electron()) = tmp_e/factor; } // water } void AdimensionalSystem::jacobian_electron(Vector& x, Matrix& jacobian) { const auto ide = ideq_electron(); const auto dofe = m_data->electron_index(); const scalar_t conc_w = density_water()*volume_fraction_water(x); const scalar_t factor = get_options().scaling_electron; // Aqueous components for (index_t k: m_data->range_aqueous_component()) { if (ideq_paq(k) == no_equation) continue; scalar_t tmp_eip = 0; tmp_eip -= diff_weigthed_sum_aqueous(k, dofe); tmp_eip -= diff_weigthed_sum_sorbed(k, dofe); tmp_eip *= conc_w; tmp_eip -= diff_weigthed_sum_gas(k, dofe); jacobian(ide, ideq_paq(k)) = tmp_eip/factor; } // Minerals for (index_t m: m_data->range_mineral()) { if (ideq_min(m) == no_equation) continue; jacobian(ide, ideq_min(m)) = - m_data->nu_mineral(m, dofe)/molar_volume_mineral(m)/factor; } // Water if (ideq_w() != no_equation) { scalar_t tmp_iw = 0; tmp_iw -= weigthed_sum_aqueous(dofe); tmp_iw -= weigthed_sum_sorbed(dofe); tmp_iw *= density_water(); jacobian(ide, ideq_w()) = tmp_iw/factor; } // Surface if (ideq_surf() != no_equation) { scalar_t tmp_s = -conc_w*diff_surface_weigthed_sum_sorbed(dofe); jacobian(ide, ideq_surf()) = tmp_s/factor; } // Electron if (ideq_electron() != no_equation) { scalar_t tmp = 0.0; tmp -= diff_weigthed_sum_aqueous(dofe, dofe); tmp -= diff_weigthed_sum_sorbed(dofe, dofe); tmp*= conc_w; tmp -= diff_weigthed_sum_gas(dofe, dofe); jacobian(ide, ide) = tmp/factor; } } // ========================== // // // // Secondary variables // // // // ========================== // void AdimensionalSystem::set_secondary_variables(const Vector& x) { m_second.porosity = 1 - sum_volume_fraction_minerals(x) - m_inert_volume_fraction; set_volume_fraction_gas_phase(x); set_pressure_fugacity(x); if (ideq_surf() != no_equation) set_sorbed_concentrations(x); set_secondary_concentration(x); compute_log_gamma(x); } void AdimensionalSystem::set_volume_fraction_gas_phase(const Vector& x) { m_second.volume_fraction_gas = m_second.porosity - volume_fraction_water(x); } void AdimensionalSystem::set_pressure_fugacity(const Vector& x) { const auto rt = constants::gas_constant*temperature(); for (index_t k: m_data->range_gas()) { if (not is_active_gas(k)) continue; scalar_t logp = -m_data->logk_gas(k); for (index_t i: m_data->range_aqueous_component()) { if (m_data->nu_gas(k, i) == 0.0) continue; const auto log_activity_i = log_component_molality(x, i) + log_gamma_component(i); logp += m_data->nu_gas(k, i) * log_activity_i; } if (ideq_electron() != no_equation and m_data->is_gas_half_cell_reaction(k)) logp += m_data->nu_gas(k, m_data->electron_index())*log_activity_electron(x); m_second.gas_fugacity(k) = pow10(logp); const scalar_t pressure = gas_fugacity(k)*gas_total_pressure(); const scalar_t concentration = m_scaling_gas*volume_fraction_gas_phase()*pressure/rt; m_second.gas_concentration(k) = concentration; } } void AdimensionalSystem::set_secondary_concentration(const Vector& x) { for (index_t j: m_data->range_aqueous()) { if (not is_aqueous_active(j)) { m_second.secondary_molalities(j) = 0.0; continue; } scalar_t logconc = -m_data->logk_aqueous(j) - log_gamma_secondary(j); for (index_t k: m_data->range_aqueous_component()) { if (m_data->nu_aqueous(j, k) == 0) continue; const auto log_activity_k = log_component_molality(x, k) + log_gamma_component(k); logconc += m_data->nu_aqueous(j, k)*log_activity_k; } if (ideq_electron() != no_equation and m_data->is_half_cell_reaction(j)) logconc += m_data->nu_aqueous(j, m_data->electron_index())*log_activity_electron(x); m_second.secondary_molalities(j) = pow10(logconc); } } void AdimensionalSystem::set_sorbed_concentrations(const Vector& x) { for (index_t s: m_data->range_sorbed()) { if (not is_active_sorbed(s)) { m_second.sorbed_concentrations(s) = 0.0; continue; } scalar_t logconc = -m_data->logk_sorbed(s) + m_data->nb_sorption_sites(s)*(log_free_sorption_site_concentration(x)); for (index_t k: m_data->range_aqueous_component()) { if (m_data->nu_sorbed(s, k) == 0.0) continue; const auto activity_k = log_component_molality(x, k) + log_gamma_component(k); logconc += m_data->nu_sorbed(s, k)*activity_k; } if (ideq_electron() != no_equation and m_data->is_sorbed_half_cell_reaction(s)) logconc += m_data->nu_sorbed(s, m_data->electron_index())*log_activity_electron(x); m_second.sorbed_concentrations(s) = pow10(logconc); } } void AdimensionalSystem::set_ionic_strength(const Vector& x) { scalar_t ionic = 0; for (index_t i: m_data->range_aqueous_component()) { if (ideq_paq(i) == no_equation or m_data->charge_component(i) == 0) continue; ionic += component_molality(x, i)*std::pow(m_data->charge_component(i),2); } for (index_t j: m_data->range_aqueous()) { if (not is_aqueous_active(j) or m_data->charge_aqueous(j) == 0) continue; ionic += secondary_molality(j)*std::pow(m_data->charge_aqueous(j),2); } ionic_strength() = ionic/2; } void AdimensionalSystem::compute_log_gamma(const Vector& x) { set_ionic_strength(x); const scalar_t sqrti = std::sqrt(ionic_strength()); for (index_t i: m_data->range_aqueous_component()) { if (ideq_paq(i) == no_equation) { log_gamma_component(i) = 0.0; continue; } log_gamma_component(i) = laws::extended_debye_huckel( ionic_strength(), sqrti, m_data->charge_component(i), m_data->a_debye_component(i), m_data->b_debye_component(i) ); } for (index_t j: m_data->range_aqueous()) { if (not is_aqueous_active(j)) { log_gamma_secondary(j) = 0.0; continue; } log_gamma_secondary(j) = laws::extended_debye_huckel( ionic_strength(), sqrti, m_data->charge_aqueous(j), m_data->a_debye_aqueous(j), m_data->b_debye_aqueous(j) ); } } bool AdimensionalSystem::hook_start_iteration(const Vector& x, scalar_t norm_residual) { if (not get_options().non_ideality) { set_secondary_variables(x); // we still need to compute secondary species ! // if (norm_residual < nb_free_variables()*get_options().start_non_ideality_computation) // { // set_secondary_variables(x); // } return true; } not_in_linesearch = true; scalar_t previous_norm = m_second.loggamma.norm(); if (previous_norm == 0) previous_norm = 1; bool may_have_converged = false; if (norm_residual < nb_free_variables()*get_options().start_non_ideality_computation) { // Use fixed point iterations for non-ideality for (int i=0; i 1e-6) { WARNING << "Activity coefficient have not converged !" << std::endl << "output can not be trusted\n Difference : " +std::to_string(std::abs(previous_norm - m_second.loggamma.norm())); } } // Set the correct value for the water total saturation if (ideq_w() == no_equation) { xtot(dof_water()) = volume_fraction_water(x); } return AdimensionalSystemSolution(xtot, m_second.secondary_molalities, m_second.loggamma, m_second.ionic_strength, m_second.gas_fugacity, m_second.sorbed_concentrations, m_inert_volume_fraction); } // Water, saturation and density // ============================== scalar_t AdimensionalSystem::density_water() const { return laws::density_water(units::celsius(25.0), length_unit(), mass_unit()); } scalar_t AdimensionalSystem::volume_fraction_water(const Vector& x) const { if (ideq_w() != no_equation) return x(ideq_w()); else return porosity(x); } scalar_t AdimensionalSystem::volume_fraction_mineral(const Vector& x, index_t mineral) const { specmicp_assert(mineral >= 0 and mineral < m_data->nb_mineral()); if (ideq_min(mineral) == no_equation) return 0.0; else return x(ideq_min(mineral)); } scalar_t AdimensionalSystem::sum_volume_fraction_minerals(const Vector& x) const { scalar_t sum_saturations = 0.0; for (index_t mineral: m_data->range_mineral()) { sum_saturations += volume_fraction_mineral(x, mineral); } return sum_saturations; } // Starting guess // ============== void AdimensionalSystem::reasonable_starting_guess(Vector &xtot) { xtot.resize(total_dofs()); xtot(dof_water()) = 0.8; for (index_t i: m_data->range_aqueous_component()) { xtot(dof_component(i)) = -4.0; } if (ideq_surf() != no_equation) xtot(dof_surface()) = std::log10(0.8*surface_total_concentration()); else xtot(dof_surface()) = -HUGE_VAL; if (ideq_electron() != no_equation) xtot(dof_electron()) = -4; else xtot(dof_electron()) = -HUGE_VAL; xtot.segment(offset_minerals(), m_data->nb_mineral()).setZero(); m_second = SecondaryVariables(m_data); } void AdimensionalSystem::reasonable_restarting_guess(Vector& xtot) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(-2, 2); xtot(dof_water()) = 0.5; for (index_t i: m_data->range_aqueous_component()) { //if (xtot(dof_component(i)) > 0 or xtot(dof_component(i)) < -9) xtot(i) = get_options().restart_concentration + dis(gen); } if (ideq_surf() != no_equation) xtot(dof_surface()) = std::log10(0.8*surface_total_concentration()); else xtot(dof_surface()) = -HUGE_VAL; if (ideq_electron() != no_equation) xtot(dof_electron()) = -4; else xtot(dof_electron()) = -HUGE_VAL; xtot.segment(offset_minerals(), m_data->nb_mineral()).setZero(); m_second = SecondaryVariables(m_data); } } // end namespace specmicp diff --git a/src/specmicp/adimensional/adimensional_system.hpp b/src/specmicp/adimensional/adimensional_system.hpp index e866b4d..61bb038 100644 --- a/src/specmicp/adimensional/adimensional_system.hpp +++ b/src/specmicp/adimensional/adimensional_system.hpp @@ -1,782 +1,783 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_ADIMENSIONALSYSTEM_HPP #define SPECMICP_ADIMENSIONALSYSTEM_HPP //! \file adimensional_system.hpp The MiCP program to solve speciation -#include "../../types.hpp" +#include "adimensional_system_numbering.hpp" +#include "adimensional_system_structs.hpp" + +#include "specmicp_common/types.hpp" #ifndef SPECMICP_DATABASE_HPP -#include "database.hpp" +#include "specmicp_database/database.hpp" #endif -#include "../../micpsolver/micpprog.hpp" -#include "../../physics/units.hpp" -#include "../../utils/options_handler.hpp" +#include "specmicp_common/micpsolver/micpprog.hpp" -#include "adimensional_system_numbering.hpp" -#include "adimensional_system_structs.hpp" +#include "specmicp_common/physics/units.hpp" +#include "specmicp_common/options_handler.hpp" //! \namespace specmicp main namespace for the SpecMiCP solver namespace specmicp { struct AdimensionalSystemSolution; //! \brief The equilibrium speciation problem //! //! Represent the equilibrium speciation problem as a Mixed Complementarity program //! //! The main variables are : //! - \f$phi_w\f$ the volume fraction of water (total saturation) //! - \f$b_i\f$ the molality of aqueous component //! - \f$phi_m\f$ the volume fraction of mineral //! - \f$s_f\f$ the concentration (molality) of free sorption sites //! //! The solid phase equilibrium is solved by using a complementarity formulation //! \f[ S^t_m \geq 0, \; - \mathrm{SI}_m \geq 0 , \; \mathrm{and} \; - S^t_m \mathrm{SI}_m = 0 \f] //! //! The secondary variables (molalities of secondary aqueous species, gas fugacities, ionic strength, ...) //! are solved using a fixed point iteration at the beginning of each iteration. //! //! This class should only be used through the AdimensionalSystemSolver. class SPECMICP_DLL_LOCAL AdimensionalSystem : public micpsolver::MiCPProg, public AdimemsionalSystemNumbering, public OptionsHandler, private units::UnitBaseClass { public: //! \brief Create a reduced system //! //! \param ptrdata Shared pointer to the thermodynamic database //! \param constraints Constraints to apply to the system //! \param options Numerical options, mainly related to the secondary variables //! \param units_set The set of units AdimensionalSystem(RawDatabasePtr& ptrdata, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemOptions& options=AdimensionalSystemOptions(), const units::UnitsSet& units_set=units::UnitsSet() ); //! \brief Create a reduced system, initialize the system using a previous solution //! //! \param ptrdata Shared pointer to the thermodynamic database //! \param constraints Constraints to apply to the system //! \param previous_solution The previous solution to use for initialization //! \param options Numerical options, mainly related to the secondary variables //! \param units_set The set of units AdimensionalSystem( RawDatabasePtr& ptrdata, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemOptions& options=AdimensionalSystemOptions(), const units::UnitsSet& units_set=units::UnitsSet() ); // Variables // ========== //! \name Variables //! \brief How many ? //! //! @{ //! \brief Return the total number of variables index_t total_variables() const {return m_equations.nb_tot_variables;} //! \brief Return the number of 'free' variables (not subject to complementarity conditions) index_t nb_free_variables() const {return m_equations.nb_free_variables;} //! \brief Return the number of variables subjected to the complementarity conditions index_t nb_complementarity_variables() const {return m_equations.nb_complementarity_variables;} //! @} //! \brief Return a pointer to the database RawDatabasePtr get_database() {return m_data;} // The linear system // ================== //! \name Residuals and Jacobian //! \brief Compute the residuals and the jacobian //! //! @{ //! \brief Return the residuals //! //! \param x Vector of the main variables //! \param[out] residual Vector containing the residuals void get_residuals(const Vector& x, Vector& residual); //! \brief Return the jacobian //! //! \param x Vector of the main variables //! \param[out] jacobian Dense matrix representing the jacobian void get_jacobian(Vector& x, Matrix& jacobian); //! \brief Return the residual for the water conservation equation //! //! \param x Vector of the main variables scalar_t residual_water_conservation(const Vector& x) const; //! \brief Return the residual for the water saturation equation //! //! \param x Vector of the main variables scalar_t residual_water_saturation(const Vector& x) const; //! \brief Return the residual for the conservation equation of component (i) //! //! For water (i==0) use the method : 'residual_water' //! \param x Vector of the main variables //! \param i Index of the component (in the database) scalar_t residual_component(const Vector& x, index_t i) const; //! \brief Compute the residual for the surface sorption //! \param x Vector of the main variables scalar_t residual_surface(const Vector& x) const; //! \brief Equilibrium condition for the minerals //! \param x Vector of the main variables //! \param m Index of the mineral (in the database) scalar_t residual_mineral(const Vector& x, index_t m) const; //! \brief Return the residual of the charge conservation equation //! //! This equation may be used instead of a mass conservation equation to //! explicitely enforce the electroneutrality. //! The component corresponding to this equation is called the charge keeper //! \param x Vector of the main variables scalar_t residual_charge_conservation(const Vector& x) const; //! \brief Return the residual corresponding to a fixed fugacity equation //! \param x Vector of the main variables //! \param component Index of the corresponding component (in the database) scalar_t residual_fixed_fugacity(const Vector& x, index_t component) const; //! \brief Return the residual corresponding to a fixed activity equation //! //! If 'component ' is charged, then a charge keeper should be set. //! \param x Vector of the main variables //! \param component Index of the corresponding component (in the database) scalar_t residual_fixed_activity(const Vector& x, index_t component) const; //! \brief Return the residual corresponding to a fixed molality equation //! //! If 'component ' is charged, then a charge keeper should be set. //! \param x Vector of the main variables //! \param component Index of the corresponding component (in the database) scalar_t residual_fixed_molality(const Vector& x, index_t component) const; //! \brief Return the residual corresponding to the electron equation scalar_t residual_electron(const Vector &x) const; //! \brief Compute the jacobian analytically void analytical_jacobian(Vector& x, Matrix& jacobian); //! \brief Compute the jacobian using finite difference void finite_difference_jacobian(Vector& x, Matrix& jacobian); //! \brief Return a scale factor to avoid negative mass during Newton's iteration //! //! \param x Vector of the main variables //! \param update Update of the current iteration double max_lambda(const Vector &x, const Vector &update); //! @} // Getters // ####### // Equation id access // ================== //! \name Equation Id //! \brief The equation id is the row of the equation in the residuals/jacobian //! //! The degree of freedom getter function are inherited from specmicp::AdimensionalSystemNumbering //! @{ //! \brief Return the equation id //! \param i Index of the main variable (in the set of the main variables) index_t ideq(index_t i) const {return m_equations.ideq[i];} //! \brief Return the equation number for conservation of water index_t ideq_w() const {return m_equations.ideq[dof_water()];} //! \brief Return the equation number of component 'i' //! \param i Index of the component (in the database) index_t ideq_paq(index_t i) const { specmicp_assert(i < m_data->nb_component()); return m_equations.ideq[dof_component(i)]; } //! \brief Return the equation number of the electron equation index_t ideq_electron() const { return m_equations.ideq[dof_electron()]; } //! \brief Return the equation number of the free surface concentration index_t ideq_surf() const { return m_equations.ideq[dof_surface()]; } //! \brief Return the equation number of mineral 'm' //! \param m Index of the mineral (in the database) index_t ideq_min(index_t m) const { specmicp_assert(m < m_data->nb_mineral()); return m_equations.ideq[dof_mineral(m)]; } //! @} //! \name Equation type //! \brief Which equation is actually solved //! //! @{ //! \brief Return the type of equation used for the solvent //! //! Can be no equation, mass conservation, saturation of the system, ... //! //! \sa #WaterEquationType, #aqueous_component_equation_type, #AqueousComponentEquationType WaterEquationType water_equation_type() const { return static_cast(m_equations.type_equation(dof_water())); } //! \brief Return the type of equation for aqueous species 'component' //! //! For water used the method #water_equation_type //! \param component Index of the aqueous component (in the database) //! //! \sa #AqueousComponentEquationType, water_equation_type, #WaterEquationType AqueousComponentEquationType aqueous_component_equation_type(index_t component) const { specmicp_assert(component > 0 and component < m_data->nb_component()); return static_cast(m_equations.type_equation(dof_component(component))); } //! \brief Return the type of equation for the electron ElectronEquationType electron_equation_type() const { return static_cast(m_equations.type_equation(dof_electron())); } //! \brief Return true if an equation exist for this component //! //! \param component Index of the component (in the database) bool is_active_component(index_t component) const { specmicp_assert(component < m_data->nb_component() and component > no_species); return m_equations.id_equation(dof_component(component)) != no_equation; } //! \brief Return the surface model SurfaceEquationType surface_model() const { if (ideq_surf() == no_equation) return SurfaceEquationType::NoEquation; return SurfaceEquationType::Equilibrium; } //! \brief Return true if 'component' is the charge keeper //! //! The charge keeper is the component added or removed to satisfy the charge balance //! \param component Index of the aqueous component (in the database) bool is_charge_keeper(index_t component) { return (aqueous_component_equation_type(component) == AqueousComponentEquationType::ChargeBalance); } //! \brief Return the component that does not exist in the solution (inactive dof) const std::vector& get_non_active_component() {return m_equations.nonactive_component;} //! @} // Variables // ========= //! \name2 Main variables //! \brief Access to the main variables //! //! @{ //! \brief Return the volume fraction of water //! //! \param x Vector of the main variables scalar_t volume_fraction_water(const Vector& x) const; //! \brief Return the density of water scalar_t density_water() const; //! \brief Return the volume fraction of a mineral //! //! \param x Vector of the main variables //! \param mineral Index of the mineral (in the database) scalar_t volume_fraction_mineral(const Vector& x, index_t mineral) const; //! \brief Return the volume of minerals //! //! \param mineral Index of the mineral (in the database) scalar_t molar_volume_mineral(index_t mineral) const { return m_scaling_molar_volume*m_data->unsafe_molar_volume_mineral(mineral); } //! \brief Return the sum of saturation of the minerals //! //! This corresponds to the volume fraction of the solid phases (minus the inert phase) //! \param x Vector of the main variables scalar_t sum_volume_fraction_minerals(const Vector& x) const; //! \brief Return the porosity //! //! Computed 'on-the-fly' //! //! \param x Vector of the main variables scalar_t porosity(const Vector& x) const { return 1-sum_volume_fraction_minerals(x)-m_inert_volume_fraction; } //! \brief Return the log_10 of the component molality //! //! \param x Vector of the main variables //! \param component Index of the aqueous component (in the database) scalar_t log_component_molality(const Vector& x, index_t component) const { specmicp_assert(ideq_paq(component) != no_equation and component < m_data->nb_component()); return x(ideq_paq(component)); } //! \brief Return the molality of 'component' //! //! \param x Vector of the main variables //! \param component Index of the aqueous component (in the database) scalar_t component_molality(const Vector& x, index_t component) const { return pow10(log_component_molality(x, component)); } //! \brief Return the concentration of free sorption site scalar_t free_sorption_site_concentration(const Vector& x) const { return pow10(log_free_sorption_site_concentration(x)); } //! \brief Return the log_10 of the free sorption site concentration scalar_t log_free_sorption_site_concentration(const Vector& x) const { specmicp_assert(ideq_surf() != no_equation); return x(ideq_surf()); } //! \brief log activity of the electron scalar_t log_activity_electron(const Vector& x) const { specmicp_assert(ideq_electron() != no_equation); return x(ideq_electron()); } //! \brief return the activity of the electro scalar_t activity_electron(const Vector& x) const { return pow10(log_activity_electron(x)); } //! @} //! \name Secondary variables //! \brief Access to the secondary variables //! //! @{ //! \brief Return the concentration of secondary species 'aqueous' //! //! \param aqueous Index of the secondary aqueous species (in the database) scalar_t secondary_molality(index_t aqueous) const { if (not is_aqueous_active(aqueous)) return 0.0; return m_second.secondary_molalities(dof_aqueous(aqueous)); } //! \brief Return true if 'aqueous' is active //! //! i.e. Return true if all its component are present in the system //! \param aqueous Index of the secondary aqueous species (in the database) bool is_aqueous_active(index_t aqueous) const { return m_equations.active_aqueous[dof_aqueous(aqueous)]; } //! \brief Return log_10(γ_i) where i is a component //! //! γ is the activity coefficient //! \param component Index of the aqueous component (in the database) scalar_t log_gamma_component(index_t component) const { return m_second.loggamma(dof_component_gamma(component)); } //! \brief Return log_10(γ_j) where j is a secondary aqueous species //! //! γ is the activity coefficient //! \param aqueous Index of the secondary aqueous species (in the database) scalar_t log_gamma_secondary(index_t aqueous) const { return m_second.loggamma(dof_aqueous_gamma(aqueous)); } //! \brief Return the ionic strength scalar_t ionic_strength() const noexcept { return m_second.ionic_strength; } //! \brief Return the total concentration of 'component' //! //! Only use this method if the mass is conserved for 'component' //! \param component Index of the aqueous component (in the database) scalar_t total_concentration_bc(index_t component) const { specmicp_assert((component > 0 and aqueous_component_equation_type(component) == AqueousComponentEquationType::MassConservation) or ( water_equation_type() == WaterEquationType::MassConservation)); return m_fixed_values(component); } //! \brief Return the fixed activity of 'component' //! //! Only use this method if 'component' has a fixed activity constraint //! \param component Index of the aqueous component (in the database) scalar_t fixed_activity_bc(index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedActivity); return m_fixed_values(component); } //! \brief Return the fixed molality of 'component' //! //! Only use this method if 'component' has a fixed molality constraint //! \param component Index of the aqueous component (in the database) scalar_t fixed_molality_bc(index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedMolality); return m_fixed_values(component); } //! \brief Return the fixed fugacity value for 'component' scalar_t fixed_fugacity_bc(index_t component) const { specmicp_assert(aqueous_component_equation_type(component) == AqueousComponentEquationType::FixedFugacity); return m_fixed_values(component); } //! \brief Return the total concentration for the electron scalar_t electron_total_concentration() const { return 0.0; } // Gas // --- //! \brief Return the fugacity of 'gas' //! //! \param gas Index of the gas (in the database) scalar_t gas_fugacity(index_t gas) const { return m_second.gas_fugacity(gas); } //! \brief Return the concentration of 'gas' in the system //! //! \param gas Index of the gas (in the database) scalar_t gas_concentration(index_t gas) const { return m_second.gas_concentration(gas); } //! \brief Return true if gas is active //! //! \param gas Index of the gas (in the database) bool is_active_gas(index_t gas) const { return m_equations.active_gas[gas]; } //! \brief Return the volume fraction (total saturation) of the gas phase scalar_t volume_fraction_gas_phase() const noexcept { return m_second.volume_fraction_gas; } // Sorbed species // -------------- //! \brief Return the surface total concentration const scalar_t& surface_total_concentration() const { return m_fixed_values(dof_surface()); } //! \brief Return true if 'sorbed' is an active species //! \param sorbed Index of the sorbed species (in the database) bool is_active_sorbed(index_t sorbed) const { return m_equations.active_sorbed[sorbed]; } //! \brief Return the molality of the sorbed species 'sorbed' //! \param sorbed Index of the sorbed species (in the database) const scalar_t& sorbed_species_concentration(index_t sorbed) const { return m_second.sorbed_concentrations(sorbed); } // Pressure and temperature // ------------------------ //! \brief Return the gas pressure //! //! This is a fixed value (1 atm) scalar_t gas_total_pressure() const { return 1.01325e5; // Note : all pressure are in pascal, unit conversion is done for the concentration } //! \brief Return the temperature //! //! This is a fixed value (25°C) scalar_t temperature() const noexcept { return 273.16+25; } //! @} // Solution // ================= //! \brief Return the equilibrium state of the system, the Solution of the speciation problem //! //! \param xtot the complete set of main variables //! \param x the reduced set of main variables, only the variables with an equation AdimensionalSystemSolution get_solution(Vector& xtot, const Vector& x); //! \name Secondary variables computation //! \brief Algorithms to compute the secondary variables //! //! @{ //! \brief Compute the ionic strength //! //! \param x Vector of the main variables void set_ionic_strength(const Vector& x); //! \brief Compute the activity coefficients //! //! \param x Vector of the main variables void compute_log_gamma(const Vector& x); //! \brief Compute the secondary aqueous species molalities //! //! \param x Vector of the main variables void set_secondary_concentration(const Vector& x); //! \brief Compute the secondary variables //! //! \param x Vector of the main variables void set_secondary_variables(const Vector& x); //! \brief Compute the gas phase volume fraction //! //! \param x Vector of the main variables void set_volume_fraction_gas_phase(const Vector& x); //! \brief Compute the fugacity for all the gas //! //! \param x Vector of the main variables void set_pressure_fugacity(const Vector& x); //! \brief Compute the sorbed species concentrations //! //! \param x Vector of the main variables void set_sorbed_concentrations(const Vector& x); //! \brief This function is called at the beginning of each iteration by the solver //! //! It does the fixed-point iteration to compute the secondary variables //! \param x Vector of the main variables //! \param norm_residual Norm of the current residuals bool hook_start_iteration(const Vector &x, scalar_t norm_residual); //! @} //! \brief A reasonable (well... maybe...) starting guess //! //! \param xtot Vector of the main variables //! \sa #reasonable_starting_guess void reasonable_starting_guess(Vector& xtot); //! \brief A reasonable (maybe...) restarting guess //! //! \param xtot Vector of the main variables //! \sa #reasonable_restarting_guess void reasonable_restarting_guess(Vector& xtot); //! \brief Set the units void set_units(const units::UnitsSet& unit_set); // Private Members and attributes // ############################## private: // scaling factors void compute_scaling_factors(); //! \brief Sum of the aqueous molalities weighted by the stoichiometric coefficient of 'component' scalar_t weigthed_sum_aqueous(index_t component) const; //! \brief Sum of the sorbed concentration weighted by the stoichiometric coefficient of 'component' scalar_t weigthed_sum_sorbed(index_t component) const; //! \brief Sum of the mineral concentration weighted by the stoichiometric coefficient of 'component' scalar_t weigthed_sum_mineral(const Vector& x, index_t component) const; //! \brief Sum of the gas concentration weigthed by the stoichiometric coefficient of 'component' scalar_t weigthed_sum_gas(index_t component) const; //! \brief Derivative of the aqueous weigthed sum with respect to 'diff_component' //! \sa weigthed_sum_aqueous scalar_t diff_weigthed_sum_aqueous(index_t diff_component, index_t component) const; //! \brief Derivative of the sorbed weigthed sum with respect to 'diff_component' //! \sa weigthed_sum_sorbed scalar_t diff_weigthed_sum_sorbed(index_t diff_component, index_t component) const; //! \brief Derivative of the sorbed weigthed sum with respect to the free surface concentration //! \sa weigthed_sum_sorbed scalar_t diff_surface_weigthed_sum_sorbed(index_t component) const; //! \brief Derivative of the gas weigthed sum with respect to 'diff_component' //! \sa weigthed_sum_sorbed scalar_t diff_weigthed_sum_gas(index_t diff_component, index_t component) const; // Jacobian // ######## //! \brief Compute the water equation contribution to the Jacobian //! \param x Reduced vector of the main variables //! \param jacobian Dense matrix containing the jacobian void jacobian_water(Vector& x, Matrix& jacobian); //! \brief Compute the aqueous components equation contribution to the Jacobian //! \param x Reduced vector of the main variables //! \param jacobian Dense matrix containing the jacobian void jacobian_aqueous_components(Vector& x, Matrix& jacobian); //! \brief Compute the mineral equations contribution to the Jacobian //! \param x Reduced vector of the main variables //! \param jacobian Dense matrix containing the jacobian void jacobian_minerals(Vector& x, Matrix& jacobian); //! \brief Compute the contribution of the surface sorption equation to te Jacobian //! \param x Reduced vector of the main variables //! \param jacobian Dense matrix containing the jacobian void jacobian_surface(Vector& x, Matrix& jacobian); //! \brief Compute the contribution of the electron equation to te Jacobian //! \param x Reduced vector of the main variables //! \param jacobian Dense matrix containing the jacobian void jacobian_electron(Vector& x, Matrix& jacobian); // Equation numbering // ################## //! \brief Number the equations //! \param constraints the constraints to apply to the system //! \sa number_eq_aqueous_component void number_eq(const AdimensionalSystemConstraints& constraints); //! \brief Number the equations for the aqueous components //! \param constraints the constraints to apply to the system //! \param[in,out] neq the number of equations in the system //! \sa number_eq void number_eq_aqueous_component( const AdimensionalSystemConstraints& constraints, index_t& neq ); // Setter // ###### // main variables // -------------- // they are set by the solver // Secondary variables // ------------------- //! \brief Return a reference to log_10(γ_i) where i is a component scalar_t& log_gamma_component(index_t component) { return m_second.loggamma(dof_component_gamma(component)); } //! \brief Return a reference to log_10(γ_j) where j is a secondary aqueous species scalar_t& log_gamma_secondary(index_t aqueous) { return m_second.loggamma(dof_aqueous_gamma(aqueous)); } //! \brief Return a reference to the ionic strength scalar_t& ionic_strength() { return m_second.ionic_strength; } // Attributes // ########## //! \struct SecondaryVariables //! \brief Contains information about the secondary variables struct SecondaryVariables { scalar_t ionic_strength {0.0}; //!< The ionic Strength scalar_t volume_fraction_gas {0.0}; //!< The gas saturation scalar_t porosity {0.0}; //!< The porosity Vector secondary_molalities; //!< The secondary molalities Vector loggamma; //!< The log of activity coefficients Vector gas_fugacity; //!< The gas fugacities Vector gas_concentration; //!< The gas concentrations Vector sorbed_concentrations; //!< The sorbed concentrations //! \brief Initialization without a previous solution SecondaryVariables(const RawDatabasePtr& data); //! \brief Initialization with a previous solution SecondaryVariables(const AdimensionalSystemSolution& previous_solution); }; //! \struct IdEquations //! \brief BookKeeper for the equations id and type struct IdEquations { bool use_water_pressure_model {false}; water_partial_pressure_f water_pressure_model {nullptr}; index_t nb_tot_variables {0}; index_t nb_free_variables {0}; index_t nb_complementarity_variables {0}; std::vector ideq; std::vector component_equation_type; std::vector fixed_activity_species; std::vector nonactive_component {}; std::vector active_aqueous; std::vector active_gas; std::vector active_sorbed; //! \brief Initialize the data structure IdEquations(index_t nb_dofs, const RawDatabasePtr& data); //! \brief Return the equation id const index_t& id_equation(index_t dof) const { return ideq[dof]; } //! \brief Return a reference to the equation id index_t& id_equation(index_t dof) { return ideq[dof]; } //! \brief Return a reference to the type of equation of 'dof' index_t& type_equation(index_t dof) { return component_equation_type[dof]; } //! \brief Return the equation type for 'dof' index_t type_equation(index_t dof) const { return component_equation_type[dof]; } //! \brief Return the related species for dof //! //! The exact meaning of this relation is dependant upon the equation type index_t& related_species(index_t dof) { return fixed_activity_species[dof]; } //! \brief Add a non active component to the system void add_non_active_component(index_t id) { nonactive_component.push_back(id); } //! \brief Add an equation void add_equation(index_t id, index_t *neq) { ideq[id] = *neq; ++(*neq); } //! \brief Set the active flag of aqueous species 'id' to 'is_active' void set_aqueous_active(index_t id, bool is_active) { active_aqueous[id] = is_active; } //! \brief Set the active flag of gas 'id' to 'is_active' void set_gas_active(index_t id, bool is_active) { active_gas[id] = is_active; } //! \brief Set the active flag of sorbed species 'id' to 'is_active' void set_sorbed_active(index_t id, bool is_active) { active_sorbed[id] = is_active; } }; bool not_in_linesearch {true}; scalar_t m_inert_volume_fraction; scalar_t m_scaling_molar_volume {1.0}; scalar_t m_scaling_gas {1.0}; Vector m_fixed_values {}; SecondaryVariables m_second; IdEquations m_equations; }; } // end namespace specmicp #endif // SPECMICP_ADIMENSIONALSYSTEM_HPP diff --git a/src/specmicp/adimensional/adimensional_system_numbering.hpp b/src/specmicp/adimensional/adimensional_system_numbering.hpp index f89d75f..8bfc0a9 100644 --- a/src/specmicp/adimensional/adimensional_system_numbering.hpp +++ b/src/specmicp/adimensional/adimensional_system_numbering.hpp @@ -1,122 +1,122 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_ADIMENSIONALSYSTEMNUMBERING #define SPECMICP_ADIMENSIONALSYSTEMNUMBERING -#include "../../types.hpp" +#include "specmicp_common/types.hpp" #ifndef SPECMICP_DATABASE_HPP -#include "../../database.hpp" +#include "specmicp_database/database.hpp" #endif namespace specmicp { //! \brief The equation numbering scheme for the adimensional system //! //! This is the numbering of the main variables (the non-reduced vector) class AdimemsionalSystemNumbering { public: //! \brief Initialize the numbering scheme //! \param ptr_database shared_ptr to the database container AdimemsionalSystemNumbering(RawDatabasePtr ptr_database): m_data(ptr_database) {} //! \brief Return the dof for water conservation index_t dof_water() const noexcept {return m_data->water_index();} //! \brief Return the dof corresponding to the component conservation //! \param component Index of the component index_t dof_component(index_t component) const NOEXCEPT { specmicp_assert_component_bounds(component, m_data); return component; } //! \brief Return the dof corresponding to the electron activity index_t dof_electron() const noexcept { return m_data->electron_index(); } //! \brief Return the dof corresponding to the sorption sites conservation index_t dof_surface() const noexcept { return m_data->nb_component(); } //! \brief Return the offset for the dof of minerals //! \sa dof_mineral index_t offset_minerals() const noexcept { return m_data->nb_component()+1; } //! \brief Return the dof for mineral 'mineral' //! \param mineral Index of the mineral (in the database) index_t dof_mineral(index_t mineral) const NOEXCEPT { specmicp_assert_mineral_bounds(mineral, m_data); return offset_minerals()+mineral; } //! \brief Return the number of dofs index_t total_dofs() const noexcept { return m_data->nb_component()+m_data->nb_mineral()+1; } // secondary species //! \brief dof of the activitiy coefficient for 'component' //! \param component Index of the component (in the database) index_t dof_component_gamma(index_t component) const { specmicp_assert_component_bounds(component, m_data); return component; } //! \brief dof of a secondary aqueous species //! \param aqueous Index of the aqueous species (in the database) index_t dof_aqueous(index_t aqueous) const { specmicp_assert(aqueous < m_data->nb_aqueous()); return aqueous; } //! \brief dof of the activity coefficient for 'aqueous' //! \param aqueous Index of the aqueous species (in the database) index_t dof_aqueous_gamma(index_t aqueous) const { specmicp_assert_aqueous_bounds(aqueous, m_data); return m_data->nb_component()+aqueous; } //! \brief Return the database RawDatabasePtr get_database() { return m_data; } protected: RawDatabasePtr m_data; }; } // end namespace specmicp #endif // SPECMICP_ADIMENSIONALSYSTEMNUMBERING diff --git a/src/specmicp/adimensional/adimensional_system_pcfm.cpp b/src/specmicp/adimensional/adimensional_system_pcfm.cpp index e300bc6..1f95ad3 100644 --- a/src/specmicp/adimensional/adimensional_system_pcfm.cpp +++ b/src/specmicp/adimensional/adimensional_system_pcfm.cpp @@ -1,201 +1,202 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "adimensional_system_pcfm.hpp" #include "adimensional_system.hpp" +#include "specmicp_common/log.hpp" + #include -#include "../../utils/log.hpp" namespace specmicp { // AdimensionalSystemPCFM AdimensionalSystemPCFM::AdimensionalSystemPCFM(std::shared_ptr program): m_data(program->get_database()), m_program(program) { check_validity(); } AdimensionalSystemPCFM::AdimensionalSystemPCFM( std::shared_ptr program, const PCFMOptions& options): OptionsHandler(options), m_data(program->get_database()), m_program(program) { check_validity(); } bool AdimensionalSystemPCFM::check_validity() { bool is_valid = true; if (m_program->water_equation_type() != WaterEquationType::NoEquation) { ERROR << "The implementation of the positive continuous fraction method does not compute the conservation of water"; is_valid = false; } if (m_program->get_database()->nb_mineral() != 0.0) { WARNING << "The implementation of the positive continuous fraction method does not compute the solid phase assemblage"; is_valid = false; } return is_valid; } PCFMReturnCode AdimensionalSystemPCFM::solve(Vector &x) { index_t cnt = 0; m_errors.setZero(m_program->total_variables()); while (cnt < get_options().max_iterations) { one_iteration(x); scalar_t error = m_errors.lpNorm(); DEBUG << "PCFM error : " << error << " ?< " << get_options().tolerance; if (not std::isfinite(error)) return PCFMReturnCode::Error; if (error < get_options().tolerance) return PCFMReturnCode::Success; ++cnt; } if (cnt == get_options().max_iterations) return PCFMReturnCode::MaxIterationsReached; return PCFMReturnCode::NotConvergedYet; } void AdimensionalSystemPCFM::one_iteration(Vector &x) { m_program->set_secondary_concentration(x); m_program->set_sorbed_concentrations(x); for (index_t component: m_data->range_aqueous_component()) { if (m_program->ideq_paq(component) != no_equation) { solve_component(component, x); } } if (m_program->ideq_surf() != no_equation) { solve_surface(x); } } void AdimensionalSystemPCFM::solve_component(index_t component, Vector& x) { specmicp_assert(component > 0 and component < m_data->nb_component() and "Must be an aqueous component"); specmicp_assert(m_program->ideq_paq(component) != no_equation and "No corresponding equation for this component"); specmicp_assert(m_program->aqueous_component_equation_type(component) == AqueousComponentEquationType::MassConservation); const scalar_t total_concentration = m_program->total_concentration_bc(component); scalar_t conc_w = m_program->density_water()*m_program->volume_fraction_water(x); scalar_t sum_reac = conc_w*m_program->component_molality(x, component); scalar_t sum_prod = 0.0; // compute the lhs and rhs of the mass balance so that every contribution is positive if (total_concentration >= 0) { sum_prod += total_concentration; } else { sum_reac -= total_concentration; } for (index_t aqueous: m_data->range_aqueous()) { if (m_data->nu_aqueous(aqueous, component) > 0) sum_reac += conc_w*m_data->nu_aqueous(aqueous, component)*m_program->secondary_molality(aqueous); else sum_prod -= conc_w*m_data->nu_aqueous(aqueous, component)*m_program->secondary_molality(aqueous); } for (index_t sorbed: m_data->range_sorbed()) { if (m_data->nu_sorbed(sorbed, component) > 0) sum_reac += m_data->nu_sorbed(sorbed, component)*m_program->sorbed_species_concentration(sorbed); else sum_prod -= m_data->nu_sorbed(sorbed, component)*m_program->sorbed_species_concentration(sorbed); } specmicp_assert(std::isfinite(sum_reac)); specmicp_assert(sum_reac > 0.0 && "Sum of reactants concentration should be positive"); specmicp_assert(sum_prod > 0.0 && "Sum of products should be positive"); // compute the step size scalar_t theta; const scalar_t factor = (sum_prod/sum_reac); if (sum_reac > sum_prod) theta = 0.9 - 0.8*factor; else theta = 0.9 - 0.8/factor; // theta *= get_options().theta_factor; scalar_t new_conc = (1-theta + theta*factor)*m_program->component_molality(x, component); x(m_program->ideq_paq(component)) = std::log10(new_conc); m_errors(m_program->ideq_paq(component)) = std::abs(sum_reac - sum_prod)/(sum_reac + sum_prod); } void AdimensionalSystemPCFM::solve_surface(Vector& x) { specmicp_assert(m_program->ideq_surf() != no_equation); specmicp_assert(m_program->surface_total_concentration() > 0); scalar_t sum_prod = m_program->surface_total_concentration(); scalar_t sum_reac = m_program->free_sorption_site_concentration(x); for (index_t sorbed: m_data->range_sorbed()) { sum_reac += m_data->nb_sorption_sites(sorbed)*m_program->sorbed_species_concentration(sorbed); } specmicp_assert(std::isfinite(sum_reac)); specmicp_assert(sum_reac > 0.0 && "Sum of reactants concentration should be positive"); specmicp_assert(sum_prod > 0.0 && "Sum of products should be positive"); scalar_t theta; const scalar_t factor = (sum_prod/sum_reac); if (sum_reac > sum_prod) theta = 0.9 - 0.8*factor; else theta = 0.9 - 0.8/factor; // theta *= get_options().theta_factor; scalar_t new_conc = (1-theta + theta*factor)*m_program->free_sorption_site_concentration(x); x(m_program->ideq_surf()) = std::log10(new_conc); m_errors(m_program->ideq_surf()) = std::abs(sum_reac - sum_prod)/(sum_reac + sum_prod); } } // end namespace specmicp diff --git a/src/specmicp/adimensional/adimensional_system_pcfm.hpp b/src/specmicp/adimensional/adimensional_system_pcfm.hpp index 857b575..898f621 100644 --- a/src/specmicp/adimensional/adimensional_system_pcfm.hpp +++ b/src/specmicp/adimensional/adimensional_system_pcfm.hpp @@ -1,103 +1,103 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_ADIMENSIONALSYSTEM_PCFM_HPP #define SPECMICP_ADIMENSIONALSYSTEM_PCFM_HPP -#include "../../database.hpp" #include "adimensional_system_pcfm_structs.hpp" -#include "../../utils/options_handler.hpp" +#include "specmicp_database/database.hpp" +#include "specmicp_common/options_handler.hpp" //! \file adimensional_system_pcfm.hpp The positive continuous fraction method namespace specmicp { // forward declaration class AdimensionalSystem; //! \brief The positive continuous fraction method //! //! It is particularly well adapted for aqueous only systems //! //! References : //! - Carrayrou (2002) class SPECMICP_DLL_LOCAL AdimensionalSystemPCFM: public OptionsHandler { public: AdimensionalSystemPCFM(std::shared_ptr program); AdimensionalSystemPCFM( std::shared_ptr program, const PCFMOptions& options); //! \brief Pre-solve the problem //! \param x Vector of the main variable (should be initialized) PCFMReturnCode solve(Vector& x); //! \brief Run one iteration of the PCFM method void one_iteration(Vector& x); //! \brief Run the PCFM method for one component void solve_component(index_t component, Vector& x); //! \brief Run the PCFM method for the surface equation void solve_surface(Vector& x); //! \brief Check the validity of the problem; for now just print error message //! \return Return true if the problem is true bool check_validity(); private: RawDatabasePtr m_data; std::shared_ptr m_program; Vector m_errors {}; }; //! \brief Initialize a system using the positive continuous fraction method //! //! \param program shared_ptr to the program //! \param x Vector of the main variables (should be initialized) //! //! \sa AdimensionalSystemPCFM inline PCFMReturnCode positive_continuous_fraction_method( std::shared_ptr program, Vector& x ) { return AdimensionalSystemPCFM(program).solve(x); } } // end namespace specmicp #endif // SPECMICP_ADIMENSIONALSYSTEM_PCFM_HPP diff --git a/src/specmicp/adimensional/adimensional_system_pcfm_structs.hpp b/src/specmicp/adimensional/adimensional_system_pcfm_structs.hpp index 360954c..c306184 100644 --- a/src/specmicp/adimensional/adimensional_system_pcfm_structs.hpp +++ b/src/specmicp/adimensional/adimensional_system_pcfm_structs.hpp @@ -1,76 +1,76 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_ADIMENSIONALSYSTEMPCFMSTRUCTS_HPP #define SPECMICP_ADIMENSIONALSYSTEMPCFMSTRUCTS_HPP -#include "../../types.hpp" +#include "specmicp_common/types.hpp" //! \file adimensional_system_pcfm_structs.hpp //! \brief Enum and structs used by the pcfm solver //! \sa specmicp::AdimensionalSystemPCFM namespace specmicp { //! \enum PCFMReturnCode //! \brief The return codes for the positive continuous fraction method //! \sa specmicp::AdimensionalSystemPCFM enum class PCFMReturnCode { LolThatsNotSupposedToHappen, //! Bummer... Error, //!< Some kind of error happended MaxIterationsReached, //!< The maximum number of iterations was reached NotConvergedYet, //!< The problem has not converged yet Success //!< Success ! }; //! \struct PCFMOptions //! \brief Options for the Positive continuous fraction method //! \sa specmicp::AdimensionalSystemPCFM struct PCFMOptions { index_t max_iterations; //!< The maximum number of iterations scalar_t tolerance; //!< The tolerance (should be relatively high around 0.5) scalar_t theta_factor; //!< multiplicatif factor for theta PCFMOptions(): max_iterations(100), tolerance(0.5), theta_factor(0.25) {} }; } // end namespace specmicp #endif diff --git a/src/specmicp/adimensional/adimensional_system_solution.hpp b/src/specmicp/adimensional/adimensional_system_solution.hpp index 7d59e53..d5a0877 100644 --- a/src/specmicp/adimensional/adimensional_system_solution.hpp +++ b/src/specmicp/adimensional/adimensional_system_solution.hpp @@ -1,87 +1,87 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTION_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTION_HPP -#include "../../types.hpp" +#include "specmicp_common/types.hpp" //! \file adimensional_system_solution.hpp Structure to contain the solution returned by AdimensionalSystemSolution namespace specmicp { //! \brief Solution of an adimensional system struct SPECMICP_DLL_PUBLIC AdimensionalSystemSolution { bool is_valid {false}; //!< True if this is a valid solution scalar_t inert_volume_fraction; //!< Inert volume fraction scalar_t ionic_strength; //!< Ionic strength Vector main_variables; //!< The main variables Vector secondary_molalities; //!< Molalities of secondary aqueous Vector log_gamma; //!< Log_10 of activities coefficients Vector gas_fugacities; //!< Gas fugacities Vector sorbed_molalities; //!< Sorbed molalities //! \brief Build an empty solution AdimensionalSystemSolution(): is_valid(false) {} //! \brief Build a valid solution AdimensionalSystemSolution( const Vector& variables, const Vector& aqueous_molalities, const Vector& loggama, scalar_t the_ionic_strength, const Vector& fugacities, const Vector& sorbed_species_molalities, scalar_t the_inert_volume_fraction=0.0 ): inert_volume_fraction(the_inert_volume_fraction), ionic_strength(the_ionic_strength), main_variables(variables), secondary_molalities(aqueous_molalities), log_gamma(loggama), gas_fugacities(fugacities), sorbed_molalities(sorbed_species_molalities) { is_valid = true; } }; } // end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTION_HPP diff --git a/src/specmicp/adimensional/adimensional_system_solution_extractor.cpp b/src/specmicp/adimensional/adimensional_system_solution_extractor.cpp index 900cca1..d454967 100644 --- a/src/specmicp/adimensional/adimensional_system_solution_extractor.cpp +++ b/src/specmicp/adimensional/adimensional_system_solution_extractor.cpp @@ -1,230 +1,230 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "adimensional_system_solution_extractor.hpp" -#include "../../physics/laws.hpp" -#include "../../database/database.hpp" +#include "specmicp_common/physics/laws.hpp" +#include "specmicp_database/database.hpp" namespace specmicp { scalar_t AdimensionalSystemSolutionExtractor::density_water() const { return laws::density_water(units::celsius(25.0), length_unit(), mass_unit()); } scalar_t AdimensionalSystemSolutionExtractor::mass_concentration_water() const { return density_water()*volume_fraction_water(); } scalar_t AdimensionalSystemSolutionExtractor::pH() const { // find species responsible for pH index_t id = m_data->get_id_component("HO[-]"); if (id != no_species) { return 14+log_activity_component(id); } else { id = m_data->get_id_component("H[+]"); if (id != no_species) return -log_activity_component(id); throw std::runtime_error("No component corresponding to the dissociation of water !"); } } scalar_t AdimensionalSystemSolutionExtractor::total_saturation_minerals() const { return m_solution.main_variables.segment(offset_minerals(), m_data->nb_mineral()).sum(); } scalar_t AdimensionalSystemSolutionExtractor::mole_concentration_mineral(index_t mineral) const { return volume_fraction_mineral(mineral)/m_data->molar_volume_mineral(mineral, length_unit()); } scalar_t AdimensionalSystemSolutionExtractor::mass_concentration_mineral(index_t mineral) const { return mole_concentration_mineral(mineral)*m_data->molar_mass_mineral(mineral, mass_unit()); } //! \brief Return the total aqueous concentration scalar_t AdimensionalSystemSolutionExtractor::total_aqueous_concentration(index_t component) const { scalar_t conc = molality_component(component); for (index_t aqueous: m_data->range_aqueous()) { if (m_data->nu_aqueous(aqueous, component) != 0.0) { conc += m_data->nu_aqueous(aqueous, component)*molality_aqueous(aqueous); } } return conc; } //! \brief Return the total solid concentration scalar_t AdimensionalSystemSolutionExtractor::total_solid_concentration(index_t component) const { scalar_t conc= 0; for (index_t mineral: m_data->range_mineral()) { if (m_data->nu_mineral(mineral, component) != 0.0) { conc += m_data->nu_mineral(mineral, component) * volume_fraction_mineral(mineral) / m_data->molar_volume_mineral(mineral, length_unit()); } } return conc; } //! \brief Return the total immobile concentration scalar_t AdimensionalSystemSolutionExtractor::total_immobile_concentration(index_t component) const { scalar_t conc= total_solid_concentration(component); const scalar_t conc_w = density_water()*volume_fraction_water(); for (index_t sorbed: m_data->range_sorbed()) { if (m_data->nu_sorbed(sorbed, component) != 0.0) { conc += conc_w*m_data->nu_sorbed(sorbed, component) * molality_sorbed_species(sorbed); } } return conc; } //! Return the saturation index for 'mineral' scalar_t AdimensionalSystemSolutionExtractor::saturation_index(index_t mineral) const { scalar_t saturation_index = - m_data->logk_mineral(mineral); for (index_t component: m_data->range_aqueous_component()) { if (m_data->nu_mineral(mineral, component) == 0.0) continue; saturation_index += m_data->nu_mineral(mineral, component) * log_activity_component(component); } return saturation_index; } //! Return the saturation index for 'mineral_kinetic' scalar_t AdimensionalSystemSolutionExtractor::saturation_index_kinetic(index_t mineral_kinetic) const { scalar_t saturation_index = - m_data->logk_mineral_kinetic(mineral_kinetic); for (index_t component: m_data->range_aqueous_component()) { if (m_data->nu_mineral_kinetic(mineral_kinetic, component) == 0.0) continue; saturation_index += m_data->nu_mineral_kinetic(mineral_kinetic, component) * log_activity_component(component); } return saturation_index; } // ########################### // // // // Modificator // // // // ########################### // void AdimensionalSystemSolutionModificator::scale_total_concentration( index_t component, scalar_t new_value) { const scalar_t old_value = total_solid_concentration(component); const scalar_t factor = new_value/old_value; m_nonconst_solution.main_variables.segment(dof_mineral(0), m_data->nb_mineral()) *= factor; } void AdimensionalSystemSolutionModificator::remove_solids() { m_nonconst_solution.main_variables.segment(dof_mineral(0), m_data->nb_mineral()).setZero(); } Vector AdimensionalSystemSolutionModificator::set_minerals_kinetics(std::vector& list_species) { index_t nb_kinetics = list_species.size(); index_t nb_new_mineral = m_data->nb_mineral() - nb_kinetics; std::vector minerals_to_keep; minerals_to_keep.reserve(nb_new_mineral); std::vector new_kinetics_index(nb_kinetics, no_species); Vector saturation_kinetics(nb_kinetics); index_t new_ind_eq = offset_minerals(); index_t new_ind_kin = 0; index_t tot_ind_kin = m_data->nb_mineral_kinetic(); // ###TODO optimize for (index_t mineral: m_data->range_mineral()) { auto is_kin = std::find(list_species.begin(), list_species.end(), mineral); // If mineral is still at equilibrium if (is_kin == list_species.end()) { minerals_to_keep.push_back(mineral); m_nonconst_solution.main_variables(new_ind_eq) = volume_fraction_mineral(mineral); ++new_ind_eq; } // If mineral is governed by kinetics else { saturation_kinetics(new_ind_kin) = volume_fraction_mineral(mineral); ++new_ind_kin; // save the new index (index in the kinetics vector) // The order is conserved ! new_kinetics_index[is_kin - list_species.begin()] = tot_ind_kin; ++tot_ind_kin; } } // change the database database::Database dbhandler(m_data); dbhandler.minerals_keep_only(minerals_to_keep); specmicp_assert(new_ind_eq == total_dofs()); // update the list of species list_species.swap(new_kinetics_index); // resize m_nonconst_solution.main_variables.conservativeResize(total_dofs()); return saturation_kinetics; } } // end namespace specmicp diff --git a/src/specmicp/adimensional/adimensional_system_solution_extractor.hpp b/src/specmicp/adimensional/adimensional_system_solution_extractor.hpp index 1d4fbe4..340d3d8 100644 --- a/src/specmicp/adimensional/adimensional_system_solution_extractor.hpp +++ b/src/specmicp/adimensional/adimensional_system_solution_extractor.hpp @@ -1,358 +1,360 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONEXTRACTOR_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONEXTRACTOR_HPP -#include "../../types.hpp" -#include "../../database.hpp" #include "adimensional_system_solution.hpp" -#include "../../physics/units.hpp" -#include "../../physics/constants.hpp" #include "adimensional_system_numbering.hpp" +#include "specmicp_common/types.hpp" +#include "specmicp_database/database.hpp" + +#include "specmicp_common/physics/units.hpp" +#include "specmicp_common/physics/constants.hpp" + //! \file adimensional_system_solution_extractor.hpp Obtain data from AdimensionalSystemSolution namespace specmicp { //! \class AdimensionalSystemSolutionExtractor //! \brief Obtain physical variables from AdimensionalSystemSolution //! //! This class can't modify the solution, use specmicp::AdimensionalSystemSolutionModificator for this. //! //! \ingroup specmicp_api //! \sa specmicp::AdimensionalSystemSolutionModificator class SPECMICP_DLL_PUBLIC AdimensionalSystemSolutionExtractor: public AdimemsionalSystemNumbering, public units::UnitBaseClass { public: // public members // ############### //! \brief Constructor //! //! \param solution Reference to a solution from the AdimensionalSystemSolution //! \param thedatabase shared_ptr to the database //! \param units_set Units used by the solver AdimensionalSystemSolutionExtractor( const AdimensionalSystemSolution& solution, RawDatabasePtr thedatabase, units::UnitsSet units_set): AdimemsionalSystemNumbering(thedatabase), units::UnitBaseClass(units_set), m_solution(solution) {} // Primary variables // ================= // water // ------ //! \brief Return the total saturation of water //! //! \deprecated, use the correct name : volume fraction scalar_t total_saturation_water() const SPECMICP_DEPRECATED("Use volume_fraction_water instead") { return m_solution.main_variables(dof_water());} //! \brief Return the volume fraction of water scalar_t volume_fraction_water() const { return m_solution.main_variables(dof_water());} //! \brief Return the density of water scalar_t density_water() const; // electron // --------- //! \brief Return the log of the activity electron scalar_t log_activity_electron() const { return m_solution.main_variables(dof_electron()); } //! \brief Return the activity of the electron scalar_t activity_electron() const { return pow10(log_activity_electron()); } //! \brief Return the pE scalar_t pE() const { return -log_activity_electron(); } scalar_t Eh() const { return std::log(10.0)*constants::gas_constant*units::celsius(25.0)/constants::faraday_constant*pE(); } // component // --------- //! \brief Return the log_10 of the molality of 'component' scalar_t log_molality_component(index_t component) const { if (component == m_data->water_index()) return std::log10(1.0/m_data->molar_mass_basis(component, mass_unit())); else if (component == m_data->electron_index()) return -HUGE_VAL; return m_solution.main_variables(dof_component(component));} //! \brief Return the molality of 'component' scalar_t molality_component(index_t component) const { if (component == m_data->water_index()) return 1.0/m_data->molar_mass_basis(component, mass_unit()); else if (component == m_data->electron_index()) return 0.0; return pow10(log_molality_component(component)); } // solid phases // ------------ //! \brief Return the total saturation of 'mineral' //! //! \deprecated, use the volume fraction instead scalar_t total_saturation_mineral(index_t mineral) const SPECMICP_DEPRECATED("Use volume_fraction_mineral instead") { return m_solution.main_variables(dof_mineral(mineral)); } //! \brief Return the volume fraction of 'mineral' scalar_t volume_fraction_mineral(index_t mineral) const { return m_solution.main_variables(dof_mineral(mineral)); } // surface // ------- //! \brief Return the concentration of free surface scalar_t log_free_surface_concentration() const { return m_solution.main_variables(dof_surface()); } //! \brief Return the concentration of free surface scalar_t free_surface_concentration() const { return pow10(m_solution.main_variables(dof_surface())); } // Secondary variables // =================== //! \brief Return the ionic strength of the solution scalar_t ionic_strength() const {return m_solution.ionic_strength;} // component // --------- //! \brief Return the log_10 of the activity coefficient of 'component' scalar_t log_activity_coefficient_component(index_t component) const { return m_solution.log_gamma(dof_component_gamma(component)); } //! \brief Return the activity coefficient of 'component' scalar_t activity_coefficient_component(index_t component) const { return pow10(log_activity_coefficient_component(component)); } //! \brief Return the log_10 of the activity of 'component scalar_t log_activity_component(index_t component) const { return log_molality_component(component) + log_activity_coefficient_component(component); } //! \brief Return the activity of 'component' scalar_t activity_component(index_t component) const { return pow10(log_activity_component(component)); } // aqueous species // --------------- //! \brief Return the molality of secondary specis 'aqueous' scalar_t molality_aqueous(index_t aqueous) const { return m_solution.secondary_molalities(dof_aqueous(aqueous));} //! \brief Return the log10 of the activity ocefficient of secondary species 'aqueous' scalar_t log_activity_coefficient_aqueous(index_t aqueous) const { return m_solution.log_gamma(dof_aqueous_gamma(aqueous)); } //! \brief Return the activity coefficient of secondary species 'aqueous' scalar_t activity_coefficient_aqueous(index_t aqueous) const { return pow10(log_activity_coefficient_aqueous(aqueous)); } //! \brief Return the activity of secondary species 'aqueous' scalar_t activity_aqueous(index_t aqueous) const { return activity_coefficient_aqueous(aqueous)*molality_aqueous(aqueous); } // gas fugacity // ------------- //! \brief Return fugacity for 'gas' scalar_t fugacity_gas(index_t gas) const { return m_solution.gas_fugacities(gas); } // Sorbed species // -------------- //! \brief Return sorbed species molalities for 'sorbed' scalar_t molality_sorbed_species(index_t sorbed) const { return m_solution.sorbed_molalities(sorbed); } // Tertiary variables // ================== // Water // ----- //! \brief Return the saturation of water scalar_t saturation_water() const {return volume_fraction_water()/porosity();} //! \brief Return the mass concentration of water scalar_t mass_concentration_water() const; //! \brief Return the pH of the solution scalar_t pH() const; // Component // --------- //! \brief Return the total aqueous concentration scalar_t total_aqueous_concentration(index_t component) const; //! \brief Return the total solid concentration scalar_t total_solid_concentration(index_t component) const; //! \brief Return the total immobile concentration scalar_t total_immobile_concentration(index_t component) const; // Gas phase // --------- //! \brief Return the saturation of the gas phase scalar_t saturation_gas_phase() const {return 1-saturation_water();} //! \brief Return the total saturation of the gas phase //! \deprecated use volume fraction instead scalar_t total_saturation_gas_phase() const SPECMICP_DEPRECATED("Use volume fraction gas phase instead") { return porosity() - saturation_water(); } //! \brief Return the volume fraction occupied by the gas phase scalar_t volume_fraction_gas_phase() const { return porosity() - volume_fraction_water(); } // Component // --------- // Solid phases // ------------ //! \brief Return the concentration of 'mineral' //! //! \param mineral Index of the mineral (in the database) scalar_t mole_concentration_mineral(index_t mineral) const; //! \brief Return the mass concentration of 'mineral' //! //! \param mineral Index of the mineral (in the database) scalar_t mass_concentration_mineral(index_t mineral) const; //! \brief Return the total saturation of all minerals //! Deprecated, use the volume fraction function instead scalar_t total_saturation_minerals() const; //! \brief Return the volume fraction occupied by the minerals scalar_t volume_fraction_minerals() const {return total_saturation_minerals();} //! \brief Return the inert volume fraction scalar_t volume_fraction_inert() const {return m_solution.inert_volume_fraction;} //! \brief Return the porosity scalar_t porosity() const {return 1 - total_saturation_minerals() - volume_fraction_inert();} //! \brief Return the saturation index for 'mineral' //! //! \param mineral Index of the mineral (in the database) scalar_t saturation_index(index_t mineral) const; //! \brief Return the saturation index for 'mineral_kinetic' //! //! \param mineral_kinetic Index of the mineral governed by the kinetic (in the database) scalar_t saturation_index_kinetic(index_t mineral_kinetic) const; //! \brief Return the vector of main variables, can be used to initialize the solver Vector get_main_variables() const {return m_solution.main_variables;} //! \brief Return a copy of the solution AdimensionalSystemSolution get_solution() const {return m_solution;} protected: // private attributes // ################## const AdimensionalSystemSolution& m_solution; }; //! \class AdimensionalSystemSolutionModificator //! \brief Special class to modify (scale) the solution //! //! The set of modification are limited to straightforward modification that does not modify the equilibrium //! //! \sa AdimensionalSystemExtractor //! \ingroup specmicp_api class SPECMICP_DLL_PUBLIC AdimensionalSystemSolutionModificator: public AdimensionalSystemSolutionExtractor { public: AdimensionalSystemSolutionModificator( AdimensionalSystemSolution& solution, RawDatabasePtr thedatabase, units::UnitsSet units_set): AdimensionalSystemSolutionExtractor(solution, thedatabase, units_set), m_nonconst_solution(solution) {} //! \brief Scale the solid phases with respect to the total solid concentration of a component //! //! \param component Index of the component (in the database) //! \param new_value New value of the total solid concentration void scale_total_concentration(index_t component, scalar_t new_value); //! \brief Remove the solid phases void remove_solids(); //! \brief Set some species to be considered as governed by kinetics //! //! \param[in,out] list_species list of minerals to flag as kinetics, //! The list will contain the new indexes of the corresponding solid phases; //! \return the vector of saturation //! //! This function modify the databse Vector set_minerals_kinetics(std::vector& list_species); private: AdimensionalSystemSolution& m_nonconst_solution; }; } // end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONEXTRACTOR_HPP diff --git a/src/specmicp/adimensional/adimensional_system_solution_saver.cpp b/src/specmicp/adimensional/adimensional_system_solution_saver.cpp index fb5c765..38ad1fa 100644 --- a/src/specmicp/adimensional/adimensional_system_solution_saver.cpp +++ b/src/specmicp/adimensional/adimensional_system_solution_saver.cpp @@ -1,562 +1,560 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "adimensional_system_solution_saver.hpp" - #include "adimensional_system_solution.hpp" -#include "../../utils/log.hpp" -#include "../../utils/dateandtime.hpp" -#include "../../utils/io/yaml.hpp" +#include "specmicp_common/log.hpp" +#include "specmicp_common/dateandtime.hpp" +#include "specmicp_common/io/yaml.hpp" + #include #include - #include - #include #include #include // formatting #include "config_solution_output_format.h" namespace specmicp { //! \brief Serialize a solution as a YAML-formatted string std::string format_solution_yaml( const RawDatabasePtr& the_database, const AdimensionalSystemSolution& the_solution ) { return AdimensionalSystemSolutionSaver(the_database).format_solution(the_solution); } //! \brief Serialize a solution and save it in a file void save_solution_yaml( const std::string& filename, const RawDatabasePtr& the_database, const AdimensionalSystemSolution& the_solution ) { AdimensionalSystemSolutionSaver(the_database).save_solution(filename, the_solution); } //! \brief Read a solution from a JSON file AdimensionalSystemSolution parse_solution_yaml( const std::string& filename, const RawDatabasePtr& the_database ) { return AdimensionalSystemSolutionSaver(the_database).parse_solution(filename); } //! \brief Read a solution from a JSON file AdimensionalSystemSolution parse_solution_yaml( std::istream& input, const RawDatabasePtr& the_database ) { return AdimensionalSystemSolutionSaver(the_database).parse_solution(input); } void AdimensionalSystemSolutionSaver::save_solution( const std::string& filename, const AdimensionalSystemSolution& solution ) { YAML::Emitter solution_yaml; io::configure_yaml_emitter(solution_yaml); set_yaml_emitter(solution, solution_yaml); io::save_yaml(filename, solution_yaml); } void AdimensionalSystemSolutionSaver::set_yaml_emitter( const AdimensionalSystemSolution& solution, YAML::Emitter& yaml_tree ) { //# Fixme better str for the date yaml_tree << YAML::Comment("Solution file generated by SpecMiCP"); yaml_tree << YAML::BeginMap; yaml_tree << YAML::Key << VALUE_META_DATE << YAML::Value << dateandtime::now(); yaml_tree << YAML::Key << VALUE_META_DATABASE << YAML::Value << m_data->metadata.name; yaml_tree << YAML::Key << VALUE_META_DATABASE_VERSION << YAML::Value << m_data->metadata.version; yaml_tree << YAML::Key << SECTION_VALUES << YAML::Value; yaml_tree << YAML::BeginSeq; format_solution(solution, yaml_tree); yaml_tree << YAML::EndSeq; yaml_tree << YAML::EndMap; } std::string AdimensionalSystemSolutionSaver::format_solution( const AdimensionalSystemSolution& solution ) { YAML::Emitter solution_yaml; io::configure_yaml_emitter(solution_yaml); set_yaml_emitter(solution, solution_yaml); if (solution_yaml.good()) { throw std::runtime_error("Error in the emitter"); } return std::string(solution_yaml.c_str()); } //! \brief Save a set of solutions as a JSON file void AdimensionalSystemSolutionSaver::save_solutions( const std::string& filename, const std::vector& solutions ) { YAML::Emitter yaml_tree; yaml_tree << YAML::Comment("Solution file generated by SpecMiCP"); yaml_tree << YAML::BeginMap; yaml_tree << YAML::Key << VALUE_META_DATE << YAML::Value << dateandtime::now(); yaml_tree << YAML::Key << VALUE_META_DATABASE << YAML::Value << m_data->metadata.name; yaml_tree << YAML::Key << VALUE_META_DATABASE_VERSION << YAML::Value << m_data->metadata.version; yaml_tree << YAML::Key << SECTION_VALUES << YAML::Value; yaml_tree << YAML::BeginSeq; for (std::size_t ind=0; ind 0) { the_solution.is_valid = false; return the_solution; } io::check_mandatory_yaml_node(root, SECTION_VALUES, "__main__"); if (root[SECTION_VALUES].Type() != YAML::NodeType::Sequence and root[SECTION_VALUES].size() < 1) { throw std::runtime_error("Expecting a non-zero list for section '" SECTION_VALUES "'."); } const YAML::Node& yaml_solution = root[SECTION_VALUES][0]; parse_one_solution(yaml_solution, the_solution); return the_solution; } void AdimensionalSystemSolutionSaver::parse_one_solution( const YAML::Node& yaml_solution, AdimensionalSystemSolution& the_solution ) { read_main_variables(yaml_solution, the_solution); read_aqueous_molalities(yaml_solution, the_solution); read_loggamma(yaml_solution, the_solution); read_gas_fugacities(yaml_solution, the_solution); read_sorbed_molalities(yaml_solution, the_solution); the_solution.inert_volume_fraction = io::get_yaml_mandatory(yaml_solution, VALUE_INERT, "Solution"); the_solution.ionic_strength =io::get_yaml_mandatory(yaml_solution, VALUE_IONIC_STRENGTH, "Solution"); the_solution.is_valid = true; } //! \brief Create a solution from a JSON file void AdimensionalSystemSolutionSaver::parse_solutions( const std::string& filename, std::vector& solutions_vector ) { YAML::Node root = io::parse_yaml_file(filename); int retcode = parse_check_metadata(root); if (retcode > 0 ) return; io::check_mandatory_yaml_node(root, SECTION_VALUES, "__main__"); if (root[SECTION_VALUES].Type() != YAML::NodeType::Sequence and root[SECTION_VALUES].size() < 1) { throw std::runtime_error("Expecting a non-zero list for section '" SECTION_VALUES "'."); } int nb_sol = root[SECTION_VALUES].size(); solutions_vector.clear(); solutions_vector.reserve(nb_sol); for (int ind=0; ind() != m_data->metadata.name) { WARNING << "SolutionSaver : The name of the database does not match the record in the solution !"; return 1; } io::check_mandatory_yaml_node(root, VALUE_META_DATABASE_VERSION, "__main__"); if (root[VALUE_META_DATABASE_VERSION].as() != m_data->metadata.version) { WARNING << "SolutionSaver : The version of the databases does not match the record in the solution !"; return 2; } return 0; } AdimensionalSystemSolution AdimensionalSystemSolutionSaver::parse_solution(const std::string& filename) { std::ifstream ofile(filename); AdimensionalSystemSolution the_solution = parse_solution(ofile); ofile.close(); return the_solution; } void AdimensionalSystemSolutionSaver::format_main_variables( const AdimensionalSystemSolution& solution, YAML::Emitter& out) { out << YAML::Key << SECTION_MAIN << YAML::Value; out << YAML::BeginMap; out << YAML::Key << VALUE_COMPONENT << YAML::Value; out << YAML::BeginMap; // water out << YAML::Key << m_data->get_label_component(0) << YAML::Value << solution.main_variables(dof_component(0)) << YAML::Comment("Volume fraction solution"); // electron out << YAML::Key << m_data->get_label_component(1) << YAML::Value << solution.main_variables(dof_component(1)) << YAML::Comment("pE"); // aqueous components for (auto component: m_data->range_aqueous_component()) { out << YAML::Key << m_data->get_label_component(component) << YAML::Value << solution.main_variables(dof_component(component)) << YAML::Comment("log10 molality (mol/kgw)"); } out << YAML::EndMap; out << YAML::Key << VALUE_FREE_SURFACE << YAML::Value << solution.main_variables(dof_surface()) << YAML::Comment("mol/kgw"); out << YAML::Key << VALUE_MINERAL << YAML::Value << YAML::Comment("Volume fraction solid phases"); out << YAML::BeginMap; for (auto mineral: m_data->range_mineral()) { out << YAML::Key << m_data->get_label_mineral(mineral) << YAML::Value << solution.main_variables(dof_mineral(mineral)); } out << YAML::EndMap; out << YAML::EndMap; } void AdimensionalSystemSolutionSaver::format_aqueous_molalities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ) { out << YAML::Key << SECTION_AQUEOUS << YAML::Value << YAML::Comment("molality (mol/kg)"); out << YAML::BeginMap; for (auto aqueous: m_data->range_aqueous()) { out << YAML::Key << m_data->get_label_aqueous(aqueous) << YAML::Value << solution.secondary_molalities(aqueous); } out << YAML::EndMap; } void AdimensionalSystemSolutionSaver::format_loggamma( const AdimensionalSystemSolution& solution, YAML::Emitter& out ) { out << YAML::Key << SECTION_LOGGAMMA << YAML::Value << YAML::Comment("log10 activity coeficient"); out << YAML::BeginMap; out << YAML::Key << VALUE_COMPONENT << YAML::Value; out << YAML::BeginMap; for (auto component: m_data->range_aqueous_component()) { out << YAML::Key << m_data->get_label_component(component) << YAML::Value << solution.log_gamma(dof_component_gamma(component)); } out << YAML::EndMap; out << YAML::Key << VALUE_AQUEOUS << YAML::Value; out << YAML::BeginMap; for (auto aqueous: m_data->range_aqueous()) { out << YAML::Key << m_data->get_label_aqueous(aqueous) << YAML::Value << solution.log_gamma(dof_aqueous_gamma(aqueous)); } out << YAML::EndMap; out << YAML::EndMap; } void AdimensionalSystemSolutionSaver::format_gas_fugacities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ) { out << YAML::Key << SECTION_GAS << YAML::Value; out << YAML::BeginMap; for (auto gas: m_data->range_gas()) { out << YAML::Key << m_data->get_label_gas(gas) << YAML::Value << solution.gas_fugacities(gas); } out << YAML::EndMap; } void AdimensionalSystemSolutionSaver::format_sorbed_molalities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ) { out << YAML::Key << SECTION_SORBED << YAML::Value; out << YAML::BeginMap; for (auto sorbed: m_data->range_sorbed()) { out << YAML::Key << m_data->get_label_sorbed(sorbed) << YAML::Value << solution.sorbed_molalities(sorbed); } out << YAML::EndMap; } void AdimensionalSystemSolutionSaver::read_main_variables( const YAML::Node& root, AdimensionalSystemSolution& solution ) { // Initialization // -------------- solution.main_variables = Vector(total_dofs()); solution.main_variables(dof_water()) = 0; solution.main_variables(dof_electron()) = - INFINITY; for (auto id: m_data->range_aqueous_component()) { solution.main_variables(dof_component(id)) = - INFINITY; } solution.main_variables(dof_surface()) = - INFINITY; for (auto mineral: m_data->range_mineral()) { solution.main_variables(dof_mineral(mineral)) = 0; } // Parsing values // -------------- io::check_mandatory_yaml_node(root, SECTION_MAIN, SECTION_VALUES); const YAML::Node& main = root[SECTION_MAIN]; // components io::check_mandatory_yaml_node(main, VALUE_COMPONENT, SECTION_VALUES); const YAML::Node& components = main[VALUE_COMPONENT]; for (auto& it: components) { const std::string label = it.first.as(); const index_t id = m_data->get_id_component(label); if (id == no_species) { throw std::invalid_argument("Component '" + label + "' does not exist in the database !"); } try { solution.main_variables(dof_component(id)) = it.second.as(); } catch (YAML::BadConversion) { if (it.second.as() == "-inf") { solution.main_variables(dof_component(id)) = - INFINITY; } } } // sorption solution.main_variables(dof_surface()) = io::get_yaml_optional(main, VALUE_FREE_SURFACE, SECTION_MAIN, -INFINITY); // minerals io::check_mandatory_yaml_node(main, VALUE_MINERAL, SECTION_VALUES); const YAML::Node& minerals = main[VALUE_MINERAL]; for (auto& it: minerals) { const std::string label = it.first.as(); const index_t id = m_data->get_id_mineral(label); if (id == no_species) { throw std::invalid_argument("Mineral '" + label + "' does not exist in the database !"); } solution.main_variables(dof_mineral(id)) = it.second.as(); } } void AdimensionalSystemSolutionSaver::read_loggamma( const YAML::Node& root, AdimensionalSystemSolution& solution ) { solution.log_gamma = Vector::Zero(m_data->nb_component()+m_data->nb_aqueous()); io::check_mandatory_yaml_node(root, SECTION_LOGGAMMA, SECTION_VALUES); const YAML::Node& loggamma = root[SECTION_LOGGAMMA]; // components io::check_mandatory_yaml_node(loggamma, VALUE_COMPONENT, SECTION_LOGGAMMA); const YAML::Node& components = loggamma[VALUE_COMPONENT]; for (auto& it: components) { const std::string label = it.first.as(); const index_t id = m_data->get_id_component(label); if (id == no_species) { throw std::invalid_argument("Component '" + label + "' does not exist in the database !"); } solution.log_gamma(dof_component_gamma(id)) = it.second.as(); } // aqueous io::check_mandatory_yaml_node(loggamma, VALUE_AQUEOUS, SECTION_LOGGAMMA); const YAML::Node& aqueous = loggamma[VALUE_AQUEOUS]; for (auto& it: aqueous) { const std::string label = it.first.as(); const index_t id = m_data->get_id_aqueous(label); if (id == no_species) { throw std::invalid_argument("Aqueous '" + label + "' does not exist in the database !"); } solution.log_gamma(dof_aqueous_gamma(id)) = it.second.as(); } } void AdimensionalSystemSolutionSaver::read_aqueous_molalities( const YAML::Node& root, AdimensionalSystemSolution &solution ) { solution.secondary_molalities = Vector::Zero(m_data->nb_aqueous()); io::check_mandatory_yaml_node(root, SECTION_AQUEOUS, SECTION_VALUES); const YAML::Node& aqueous_section = root[SECTION_AQUEOUS]; for (auto& it: aqueous_section) { const std::string label = it.first.as(); const index_t id = m_data->get_id_aqueous(label); if (id == no_species) { throw std::invalid_argument("Secondary aqueous species '" + label + "' does not exist in the database !"); } solution.secondary_molalities(id) = it.second.as(); } } void AdimensionalSystemSolutionSaver::read_gas_fugacities( const YAML::Node& root, AdimensionalSystemSolution& solution ) { solution.gas_fugacities = Vector::Zero(m_data->nb_gas()); io::check_mandatory_yaml_node(root, SECTION_GAS, SECTION_VALUES); const YAML::Node& gas_section = root[SECTION_GAS]; for (auto& it: gas_section) { const std::string label = it.first.as(); const index_t id = m_data->get_id_gas(label); if (id == no_species) { throw std::invalid_argument("Gas '" + label + "' does not exist in the database !"); } solution.gas_fugacities(id) = it.second.as(); } } void AdimensionalSystemSolutionSaver::read_sorbed_molalities( const YAML::Node& root, AdimensionalSystemSolution& solution ) { solution.sorbed_molalities = Vector::Zero(m_data->nb_sorbed()); io::check_mandatory_yaml_node(root, SECTION_SORBED, SECTION_VALUES); const YAML::Node& sorbed_section = root[SECTION_SORBED]; for (auto& it: sorbed_section) { const std::string label = it.first.as(); const index_t id = m_data->get_id_sorbed(label); if (id == no_species) { throw std::invalid_argument("sorbed species '" + label + "' does not exist in the database !"); } solution.sorbed_molalities(id) = it.second.as(); } } } //end namespace specmicp diff --git a/src/specmicp/adimensional/adimensional_system_solution_saver.hpp b/src/specmicp/adimensional/adimensional_system_solution_saver.hpp index 7095340..da9a783 100644 --- a/src/specmicp/adimensional/adimensional_system_solution_saver.hpp +++ b/src/specmicp/adimensional/adimensional_system_solution_saver.hpp @@ -1,204 +1,204 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONSAVER_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONSAVER_HPP -#include "../../types.hpp" -#include "../../database.hpp" - #include "adimensional_system_numbering.hpp" +#include "specmicp_common/types.hpp" +#include "specmicp_database/database.hpp" + #include namespace YAML { class Node; class Emitter; } //end namespace YAML namespace specmicp { struct AdimensionalSystemSolution; //! \brief This is a serializer for the AdimensionalSystemSolution //! //! This class format the solution is a JSON-formatted string. class SPECMICP_DLL_PUBLIC AdimensionalSystemSolutionSaver : AdimemsionalSystemNumbering { public: AdimensionalSystemSolutionSaver(const RawDatabasePtr& the_database): AdimemsionalSystemNumbering(the_database) {} //! \brief Format the solution as a YAML-formatted string std::string format_solution(const AdimensionalSystemSolution& solution); //! \brief Save the solution as a YAML file //! //! \param filepath path the file where to solve the solution //! \param solution the solution to save void save_solution( const std::string& filename, const AdimensionalSystemSolution& solution ); //! \brief Save a set of solutions as a YAML file //! //! \param filepath path to the file where to solve the solutions //! \param solutions vector of solutions void save_solutions( const std::string& filepath, const std::vector& solutions ); //! \brief Create a solution from a YAML input AdimensionalSystemSolution parse_solution(std::istream& input); //! \brief Create a solution from a YAML file AdimensionalSystemSolution parse_solution(const std::string& filename); //! \brief Create a solution from a YAML file void parse_solutions(const std::string& filename, std::vector& solutions_vector); private: //! \brief Build the Json::Value tree corresponding to the solution (+metadata) void SPECMICP_DLL_LOCAL set_yaml_emitter( const AdimensionalSystemSolution& solution, YAML::Emitter& yaml_tree ); //! \brief Build the Json::Value tree corresponding to the solution (w/o metadata) void SPECMICP_DLL_LOCAL format_solution( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Format the main variables void SPECMICP_DLL_LOCAL format_main_variables( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Format the main variables void SPECMICP_DLL_LOCAL format_aqueous_molalities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Format the log of activity coefficients void SPECMICP_DLL_LOCAL format_loggamma( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Format the gas fugacities void SPECMICP_DLL_LOCAL format_gas_fugacities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Format the sorbed molalities void SPECMICP_DLL_LOCAL format_sorbed_molalities( const AdimensionalSystemSolution& solution, YAML::Emitter& out ); //! \brief Check the metatada : issue warnings if the metadata does not look correct int SPECMICP_DLL_LOCAL parse_check_metadata(const YAML::Node& root); //! \brief Parse one solution from the JSON tree void SPECMICP_DLL_LOCAL parse_one_solution( const YAML::Node& yaml_solution, AdimensionalSystemSolution& the_solution ); //! \brief Parse the main variables void SPECMICP_DLL_LOCAL read_main_variables( const YAML::Node& root, AdimensionalSystemSolution& solution ); //! \brief Parse the activity coefficients void SPECMICP_DLL_LOCAL read_aqueous_molalities( const YAML::Node& root, AdimensionalSystemSolution& solution ); //! \brief Parse the activity coefficients void SPECMICP_DLL_LOCAL read_loggamma( const YAML::Node& root, AdimensionalSystemSolution& solution ); //! \brief Parse the gas fugacities void SPECMICP_DLL_LOCAL read_gas_fugacities( const YAML::Node& root, AdimensionalSystemSolution& solution ); //! \brief Parsed the sorbed molalities void SPECMICP_DLL_LOCAL read_sorbed_molalities( const YAML::Node& root, AdimensionalSystemSolution& solution ); }; //! \brief Serialize a solution as a JSON-formatted string std::string SPECMICP_DLL_PUBLIC format_solution_yaml( const RawDatabasePtr& the_database, const AdimensionalSystemSolution& the_solution ); //! \brief Serialize a solution and save it in a file void SPECMICP_DLL_PUBLIC save_solution_yaml( const std::string& filename, const RawDatabasePtr& the_database, const AdimensionalSystemSolution& the_solution ); //! \brief Read a solution from a JSON file AdimensionalSystemSolution SPECMICP_DLL_PUBLIC parse_solution_yaml( const std::string& filename, const RawDatabasePtr& the_database ); //! \brief Read a solution from a JSON file AdimensionalSystemSolution SPECMICP_DLL_PUBLIC parse_solution_yaml( std::istream& input, const RawDatabasePtr& the_database ); } //end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLUTIONSAVER_HPP diff --git a/src/specmicp/adimensional/adimensional_system_solver.cpp b/src/specmicp/adimensional/adimensional_system_solver.cpp index 8f5d2ce..8e96840 100644 --- a/src/specmicp/adimensional/adimensional_system_solver.cpp +++ b/src/specmicp/adimensional/adimensional_system_solver.cpp @@ -1,447 +1,448 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "../../micpsolver/micpsolver.hpp" #include "adimensional_system_solver.hpp" #include "adimensional_system.hpp" #include "adimensional_system_solution.hpp" #include "adimensional_system_pcfm.hpp" -#include "../../utils/log.hpp" +#include "specmicp_common/micpsolver/micpsolver.hpp" + +#include "specmicp_common/log.hpp" #include namespace specmicp { AdimensionalSystemSolution solve_equilibrium( std::shared_ptr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolverOptions& options ) { AdimensionalSystemSolver solver(data, constraints, options); Vector variables; micpsolver::MiCPPerformance perf = solver.solve(variables, true); if (perf.return_code <= micpsolver::MiCPSolverReturnCode::NotConvergedYet) throw std::runtime_error("Failed to solve the problem"); return solver.get_raw_solution(variables); } AdimensionalSystemSolution SPECMICP_DLL_PUBLIC solve_equilibrium( RawDatabasePtr data_ptr, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemSolverOptions& options ) { AdimensionalSystemSolver solver(data_ptr, constraints, previous_solution, options); Vector variables; micpsolver::MiCPPerformance perf = solver.solve(variables, true); if (perf.return_code <= micpsolver::MiCPSolverReturnCode::NotConvergedYet) throw std::runtime_error("Failed to solve the problem"); return solver.get_raw_solution(variables); } // constructor // ----------- AdimensionalSystemSolver::AdimensionalSystemSolver( RawDatabasePtr data, const AdimensionalSystemConstraints& constraints ): OptionsHandler(), m_data(data), m_system(std::make_shared( data, constraints, get_options().system_options, get_options().units_set )), m_var(Vector::Zero(data->nb_component()+1+data->nb_mineral())) {} AdimensionalSystemSolver::AdimensionalSystemSolver( RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolverOptions& options ): OptionsHandler(options), m_data(data), m_system(std::make_shared( data, constraints, options.system_options, options.units_set )), m_var(Vector::Zero(data->nb_component()+1+data->nb_mineral())) {} AdimensionalSystemSolver::AdimensionalSystemSolver( RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution ): OptionsHandler(), m_data(data), m_system(std::make_shared( data, constraints, previous_solution, get_options().system_options, get_options().units_set )), m_var(Vector::Zero(data->nb_component()+1+data->nb_mineral())) {} AdimensionalSystemSolver::AdimensionalSystemSolver( RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemSolverOptions& options ): OptionsHandler(options), m_data(data), m_system(std::make_shared( data, constraints, previous_solution, options.system_options, options.units_set )), m_var(Vector::Zero(data->nb_component()+1+data->nb_mineral())) {} AdimensionalSystemSolution AdimensionalSystemSolver::get_raw_solution(Vector& x) { set_true_variable_vector(x); return m_system->get_solution(x, m_var); } // Solving the system // ------------------ micpsolver::MiCPPerformance AdimensionalSystemSolver::solve(Vector& x, bool init) { m_system->set_units(get_options().units_set); if (init) { m_system->reasonable_starting_guess(x); if (get_options().use_pcfm) { run_pcfm(x); } } else if (get_options().force_pcfm) { run_pcfm(x); } set_true_variable_vector(x); micpsolver::MiCPPerformance perf = solve_system(); int cnt = 0; while (perf.return_code < micpsolver::MiCPSolverReturnCode::Success // != micpsolver::MiCPSolverReturnCode::ResidualMinimized and get_options().allow_restart and cnt < 3) { WARNING << "Failed to solve the system ! Return code :" << (int) perf.return_code << ". We shake it up and start again"; const scalar_t save_penalization_factor = get_options().solver_options.penalization_factor; const bool save_scaling = get_options().solver_options.use_scaling; get_options().solver_options.use_scaling = true; if (save_penalization_factor == 1) get_options().solver_options.penalization_factor = 0.8; set_return_vector(x); m_system->reasonable_restarting_guess(x); if (get_options().use_pcfm or get_options().force_pcfm) run_pcfm(x); set_true_variable_vector(x); micpsolver::MiCPPerformance perf2 = solve_system(); get_options().solver_options.penalization_factor = save_penalization_factor; get_options().solver_options.use_scaling = save_scaling; perf += perf2; ++cnt; } if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) set_return_vector(x); return perf; } micpsolver::MiCPPerformance AdimensionalSystemSolver::solve_system() { micpsolver::MiCPSolver solver(m_system); solver.set_options(get_options().solver_options); solver.solve(m_var); return solver.get_perfs(); } // Variables management // --------------------- void AdimensionalSystemSolver::set_true_variable_vector(const Vector& x) { const std::vector& non_active_component = m_system->get_non_active_component(); uindex_t new_i {0}; if (m_system->is_active_component(0)) { m_var(new_i) = x(m_system->dof_water()); ++new_i; } for (index_t i: m_data->range_aqueous_component()) { const auto it = std::find(non_active_component.cbegin(), non_active_component.cend(),i); if (it != non_active_component.cend()) continue; scalar_t value = x(m_system->dof_component(i)); if (value == -HUGE_VAL) // check for previously undefined value { value = m_system->get_options().new_component_concentration; } m_var(new_i) = value; ++new_i; } if (m_system->ideq_surf() != no_equation) { m_var(new_i) = x(m_system->dof_surface()); ++new_i; } if (m_system->ideq_electron() != no_equation) { m_var(new_i) = x(m_system->dof_electron()); ++new_i; } for (index_t m: m_data->range_mineral()) { bool to_keep = true; for (auto it=non_active_component.begin(); it!=non_active_component.end(); ++it) { if (m_data->nu_mineral(m, *it) != 0) to_keep = false; } if (to_keep) { m_var(new_i) = x(m_system->dof_mineral(m)); ++new_i; } } m_var.conservativeResize(new_i); specmicp_assert(new_i == (unsigned) m_system->total_variables()); } void AdimensionalSystemSolver::set_return_vector(Vector& x) { const std::vector& non_active_component = m_system->get_non_active_component(); uindex_t new_i = 0; if (m_system->is_active_component(0)) { x(m_system->dof_water()) = m_var(new_i); ++new_i; } else { x(m_system->dof_water()) = m_system->volume_fraction_water(x); } for (index_t i: m_data->range_aqueous_component()) { const auto it = std::find(non_active_component.cbegin(), non_active_component.cend(),i); if (it != non_active_component.cend()) { x(m_system->dof_component(i)) = -HUGE_VAL; continue; } x(m_system->dof_component(i)) = m_var(new_i) ; ++new_i; } if (m_system->ideq_surf() != no_equation) { x(m_system->dof_surface()) = m_var(new_i); ++new_i; } else x(m_system->dof_surface()) = -HUGE_VAL; if (m_system->ideq_electron() != no_equation) { x(m_system->dof_electron()) = m_var(new_i); ++new_i; } else x(m_system->dof_electron()) = -HUGE_VAL; for (index_t m: m_data->range_mineral()) { bool to_keep = true; for (const index_t& k: non_active_component) { if (m_data->nu_mineral(m, k) != 0.0) to_keep = false; } if (to_keep) { x(m_system->dof_mineral(m)) =m_var(new_i); ++new_i; } else { x(m_system->dof_mineral(m)) = 0.0; } } } // PCFM // ---- void AdimensionalSystemSolver::run_pcfm(Vector &x) { DEBUG << "Start PCFM initialization."; // we set up the true variable set_true_variable_vector(x); // The residual is computed to have some point of comparison Vector residuals(m_system->total_variables()); residuals.setZero(); m_system->get_residuals(m_var, residuals); const scalar_t res_0 = residuals.norm(); // the pcfm iterations are executed AdimensionalSystemPCFM pcfm_solver(m_system); PCFMReturnCode retcode = pcfm_solver.solve(m_var); // Check the answer if (retcode < PCFMReturnCode::Success) { // small prograss is still good enough m_system->get_residuals(m_var, residuals); const scalar_t final_residual = residuals.norm(); DEBUG << "Final pcfm residuals : " << final_residual << " set_secondary_variables(m_var); } } // Initialisation of variables // --------------------------- void AdimensionalSystemSolver::initialise_variables( Vector& x, scalar_t volume_fraction_water, std::map log_molalities, std::map volume_fraction_minerals, scalar_t log_free_sorption_site_concentration ) { m_system->reasonable_starting_guess(x); if (volume_fraction_water < 0 or volume_fraction_water > 1) { WARNING << "Initial guess for the volume fraction of water is not between 0 and 1"; } x(m_system->dof_water()) = volume_fraction_water; for (auto pair: log_molalities) { index_t idc = m_data->get_id_component(pair.first); if (idc == no_species or idc == m_data->electron_index() or idc == m_data->water_index()) { throw std::invalid_argument("This is not an aqueous component : "+pair.first); } if (pair.second > 0) { WARNING << "Initial molality for : " << pair.first << "is bigger than 1 molal."; } x(m_system->dof_component(idc)) = pair.second; } for (auto pair: volume_fraction_minerals) { index_t idm = m_data->get_id_mineral(pair.first); if (idm == no_species ) { throw std::invalid_argument("This is not a mineral at equilibrium : "+pair.first); } if (pair.second < 0 or pair.second > 1) { WARNING << "Initial volume fraction for : " << pair.first << "is not between 0 and 1."; } x(m_system->dof_mineral(idm)) = pair.second; } if (log_free_sorption_site_concentration != 0.0) x(m_system->dof_surface()) = log_free_sorption_site_concentration; } void AdimensionalSystemSolver::initialise_variables(Vector& x, scalar_t volume_fraction_water, scalar_t log_molalities ) { m_system->reasonable_starting_guess(x); if (volume_fraction_water < 0 or volume_fraction_water > 1) { WARNING << "Initial guess for the volume fraction of water is not between 0 and 1"; } x(m_system->dof_water()) = volume_fraction_water; if (log_molalities > 0) { WARNING << "Initial molality for : " << log_molalities << "is bigger than 1 molal."; } x.segment(1, m_data->nb_component()-1).setConstant(log_molalities); } void AdimensionalSystemSolver::initialise_variables(Vector& x, scalar_t volume_fraction_water, scalar_t log_molalities, scalar_t log_free_sorption_site_concentration ) { initialise_variables(x, volume_fraction_water, log_molalities); x(m_system->dof_surface()) = log_free_sorption_site_concentration; } } // end namespace specmicp diff --git a/src/specmicp/adimensional/adimensional_system_solver.hpp b/src/specmicp/adimensional/adimensional_system_solver.hpp index b20b65a..80c051c 100644 --- a/src/specmicp/adimensional/adimensional_system_solver.hpp +++ b/src/specmicp/adimensional/adimensional_system_solver.hpp @@ -1,204 +1,205 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVER_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVER_HPP -#include "../../database.hpp" #include "adimensional_system_solver_structs.hpp" -#include "../../utils/options_handler.hpp" + +#include "specmicp_database/database.hpp" +#include "specmicp_common/options_handler.hpp" #include //! \file adimensional_system_solver.hpp //! \brief Solve a reduced system namespace specmicp { // forward declaration class AdimensionalSystem; struct AdimensionalSystemSolution; //! \brief Solve an adimensional system //! //! Take care of non-existing component in the system //! and restart the computation if necessary //! //! \ingroup specmicp_api class SPECMICP_DLL_PUBLIC AdimensionalSystemSolver: public OptionsHandler { public: using SystemPtr = std::shared_ptr; //! Default constructor //! //! Another constructor is necessary AdimensionalSystemSolver() {} //! \brief Initialise a solver from scratch //! //! \param data A raw database //! \param constraints The system to solver AdimensionalSystemSolver(RawDatabasePtr data, const AdimensionalSystemConstraints& constraints ); //! \brief Initialise a solver from scratch //! //! \param data A raw database //! \param constraints The system to solver //! \param options (optional) customize the behavior of the solver AdimensionalSystemSolver(RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolverOptions& options ); //! \brief Initialise a solver from a previous solution //! //! \param data A raw database //! \param constraints The system to solver //! \param previous_solution A solution of a similar system that will be used to initialize the system AdimensionalSystemSolver(RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution ); //! \brief Initialise a solver from a previous solution //! //! \param data A raw database //! \param constraints The system to solver //! \param previous_solution A solution of a similar system that will be used to initialize the system //! \param options customize the behavior of the solver AdimensionalSystemSolver(RawDatabasePtr data, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemSolverOptions& options ); //! \brief solve the problem using initial guess x //! //! \param[in,out] x in -> initial guess, out -> solution //! \param init if true, the algorithm guess a starting point micpsolver::MiCPPerformance solve(Vector& x, bool init=false); //! \brief Return the system used for the computation SystemPtr get_system() {return m_system;} //! \brief Return the solution in a manageable form //! //! \param x The solution (complete set of the main variables) AdimensionalSystemSolution get_raw_solution(Vector& x); //! \brief Initialize the problem using the Positive continuous fraction method //! //! \sa specmicp::AdimensionalSystemPCFM void run_pcfm(Vector& x); //! \brief Custom initialisation of variables //! //! The amount compon //! \param[out] x The initial guess (complete set of the main variables) //! \param volume_fraction_water volume fraction of water //! \param log_molalities log_10 of the molalities for chosen aqueous component //! \param volume_fraction_minerals volume fraction of the minerals //! \param log_free_sorption_site_concentration concentration of free sorption site void initialise_variables(Vector& x, scalar_t volume_fraction_water, std::map log_molalities, std::map volume_fraction_minerals = {}, scalar_t log_free_sorption_site_concentration = 0 ); //! \brief Custom initialisation of variables //! //! The amount compon //! \param[out] x The initial guess (complete set of the main variables) //! \param volume_fraction_water volume fraction of water //! \param log_molalities log_10 of the molalities for all aqueous components void initialise_variables(Vector& x, scalar_t volume_fraction_water, scalar_t log_molalities ); //! \brief Custom initialisation of variables //! //! The amount compon //! \param[out] x The initial guess (complete set of the main variables) //! \param volume_fraction_water volume fraction of water //! \param log_molalities log_10 of the molalities for all aqueous components //! \param log_free_sorption_site_concentration concentration of free sorption site void initialise_variables(Vector& x, scalar_t volume_fraction_water, scalar_t log_molalities, scalar_t log_free_sorption_site_concentration ); private: //! \brief set up the true variable vector //! //! \param x The solution (complete set of the main variables) void SPECMICP_DLL_LOCAL set_true_variable_vector(const Vector& x); //! \brief set up the true solution vector //! //! add zero components //! \param x The solution (complete set of the main variables) void SPECMICP_DLL_LOCAL set_return_vector(Vector& x); //! \brief solve the problem micpsolver::MiCPPerformance SPECMICP_DLL_LOCAL solve_system(); RawDatabasePtr m_data; //! The raw database SystemPtr m_system; //! The system to solve Vector m_var; //! Copy of the solution vector (necessary in case of failing) }; //! \brief Solve a reduced system, function provided for convenience //! //! \param data_ptr the database //! \param constraints Constraints applied to the system //! \param options Options for the solver AdimensionalSystemSolution SPECMICP_DLL_PUBLIC solve_equilibrium( RawDatabasePtr data_ptr, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolverOptions& options ); //! \brief Solve a reduced system, function provided for convenience //! //! \param data_ptr the database //! \param constraints constraints applied to the system //! \param previous_solution a previous solution //! \param options options for the solver AdimensionalSystemSolution SPECMICP_DLL_PUBLIC solve_equilibrium( RawDatabasePtr data_ptr, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous_solution, const AdimensionalSystemSolverOptions& options ); } // end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVER_HPP diff --git a/src/specmicp/adimensional/adimensional_system_solver_structs.hpp b/src/specmicp/adimensional/adimensional_system_solver_structs.hpp index 8d81980..29620e4 100644 --- a/src/specmicp/adimensional/adimensional_system_solver_structs.hpp +++ b/src/specmicp/adimensional/adimensional_system_solver_structs.hpp @@ -1,73 +1,74 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVERSTRUCTS_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVERSTRUCTS_HPP -#include "../../types.hpp" -#include "../../micpsolver/micpsolver_structs.hpp" #include "adimensional_system_structs.hpp" -#include "../../physics/units.hpp" + +#include "specmicp_common/types.hpp" +#include "specmicp_common/micpsolver/micpsolver_structs.hpp" +#include "specmicp_common/physics/units.hpp" //! \file adimensional_system_solver_structs.hpp Options and conditions for AdimensionalSystemSolver namespace specmicp { //! \struct AdimensionalSystemSolverOptions //! \brief Options for the Equilibrium solver //! //! Most of the options are contained in the MiCP solver options or the AdimensionalSystem options struct SPECMICP_DLL_PUBLIC AdimensionalSystemSolverOptions { //! Allow the restarting if the problem failed the first part bool allow_restart; //! If true use the pcfm method to initialize the problem bool use_pcfm; //! Use pcfm before every trial bool force_pcfm; //! Set of units units::UnitsSet units_set; //! Options of the MiCP solver micpsolver::MiCPSolverOptions solver_options {}; //! Options of the system AdimensionalSystemOptions system_options {}; AdimensionalSystemSolverOptions(); }; } // end namespace specmicp #endif //SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSOLVERSTRUCTS_HPP diff --git a/src/specmicp/adimensional/adimensional_system_structs.hpp b/src/specmicp/adimensional/adimensional_system_structs.hpp index 513eacd..130c6dc 100644 --- a/src/specmicp/adimensional/adimensional_system_structs.hpp +++ b/src/specmicp/adimensional/adimensional_system_structs.hpp @@ -1,340 +1,340 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSTRUCTS_HPP #define SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSTRUCTS_HPP -#include "../../types.hpp" +#include "specmicp_common/types.hpp" //! \file adimensional_system_structs.hpp //! \brief Options and constraints for the AdimensionalSystem namespace specmicp { //! \struct AdimensionalSystemOptions //! \brief Options for the Adimensional Systems //! //! It is mainly about the secondary variables fixed-point iterations struct SPECMICP_DLL_PUBLIC AdimensionalSystemOptions { //! Solve for non ideality bool non_ideality; //! Max iterations for the non ideality model index_t non_ideality_max_iter; //! Scaling the electron equation scalar_t scaling_electron; //! Tolerance for non ideality scalar_t non_ideality_tolerance; //! Under relaxation factor for the conservation of water scalar_t under_relaxation_factor; //! Log of the molality used to restart the computation scalar_t restart_concentration; //! Log_10 of the molality for a new component scalar_t new_component_concentration; //! Factor to start the non-ideality computation scalar_t start_non_ideality_computation; //! Cutoff for including components in the computation scalar_t cutoff_total_concentration; AdimensionalSystemOptions(); // in adimensional_system_solver_structs.cpp }; //! \enum AqueousComponentEquationType //! \brief Type of an aqueous component equation enum class AqueousComponentEquationType { NoEquation = no_equation, //!< Not an equation, component is not present in the system MassConservation, //!< Mass balance ChargeBalance, //!< M.B. replaced by charge balance FixedFugacity, //!< M.B. replaced by a fixed fugacity equation FixedActivity, //!< M.B. replaced by a fixed activity equation FixedMolality //!< The molality of the species is fixed }; //! \enum WaterEquationType //! \brief The type of the equation solved for the water enum class WaterEquationType { NoEquation = no_equation, //!< Amount of water is not solved MassConservation, //!< Water is conserved SaturatedSystem //!< System is saturated }; //! \struct FixedFugacityConstraint //! \brief Struct to contain information needed to solve a fix fugacity problem struct FixedFugacityConstraint { index_t id_gas; //!< Index of the fixed-fugacity gas index_t id_component; //!< Index of the corresponding component scalar_t log_value; //!< Log_10 of the fugacity FixedFugacityConstraint(index_t gas, index_t component, scalar_t logvalue): id_gas(gas), id_component(component), log_value(logvalue) {} }; //! \struct FixedActivityConstraint //! \brief Struct to contain information needed to solve a fix activity problem. struct FixedActivityConstraint { index_t id_component; //!< Index of the fixed-activity component scalar_t log_value; //!< Log_10 of the activity FixedActivityConstraint(index_t component, scalar_t logvalue): id_component(component), log_value(logvalue) {} }; //! \struct FixedMolalityConstraint //! \brief Contain information about a fixed molality constraint struct FixedMolalityConstraint { index_t id_component; //!< Index of the component scalar_t log_value; //!< Log_10 of the molality FixedMolalityConstraint(index_t component, scalar_t logvalue): id_component(component), log_value(logvalue) {} }; //! \enum SurfaceEquationType //! \brief The model for surface sorption enum class SurfaceEquationType { NoEquation = no_equation, //!< Do not include surface sorption Equilibrium //!< Equilibrium model }; //! \struct SurfaceConstraint //! This struct contains the information to set-up the surface sorption model struct SurfaceConstraint { SurfaceEquationType model_type; //!< The model to use scalar_t concentration; //!< The total concentration of sorption sites //! \brief By default, we don't include surface sorption in the computation SurfaceConstraint() noexcept: model_type(SurfaceEquationType::NoEquation), concentration(0.0) {} //! \brief When a concentration is supplied, the surface sorption model is equilibrium SurfaceConstraint(scalar_t surface_concentration) noexcept: model_type(SurfaceEquationType::Equilibrium), concentration(surface_concentration) {} }; //! \enum ElectronEquationType the type of the equation for the electron enum class ElectronEquationType { NoEquation = no_equation, //!< Do not compute the concentration equation of the electron Equilibrium, //!< Set the concentration of electron to be 0 FixedpE //!< Activity of the electron is fixed }; //! \struct ElectronConstraint //! \brief the constraint for the electron struct ElectronConstraint { ElectronEquationType equation_type{ElectronEquationType::NoEquation}; //!< The equation type scalar_t fixed_value; //!< The fixed value of pE if needed index_t species {no_species}; //!< In case of fixed pE, this is the reaction to use //! \brief By default we assume equilibrium ElectronConstraint(): equation_type(ElectronEquationType::Equilibrium), fixed_value(0.0) {} //! \brief When a value is provided, we assume that the pE is fixed ElectronConstraint(scalar_t pe_value, scalar_t aqueous_species): equation_type(ElectronEquationType::FixedpE), fixed_value(pe_value), species(aqueous_species) {} }; //! \brief Return the partial pressure as function of the liquid saturation //! //! This function must return the pressure in Pa using water_partial_pressure_f = std::function; //! \brief Constraints for the partial pressure model struct WaterPartialPressureConstraint { bool use_partial_pressure_model; //!< True if we use partial pressure model water_partial_pressure_f partial_pressure_model; //!< The model given by the user //! \brief By default the partial pressure constraint is disabled WaterPartialPressureConstraint(): use_partial_pressure_model(false), partial_pressure_model(nullptr) {} //! \brief Enble the water partial pressure model WaterPartialPressureConstraint(water_partial_pressure_f& model): WaterPartialPressureConstraint() { if (model != nullptr) { use_partial_pressure_model = true; partial_pressure_model = model; } } }; //! \struct AdimensionalSystemConstraints //! \brief Struct to contains the "Boundary conditions" for the AdimensionalSystem //! //! \ingroup specmicp_api struct AdimensionalSystemConstraints { Vector total_concentrations; //!< Total concentrations WaterEquationType water_equation{WaterEquationType::MassConservation}; //!< Water equation index_t charge_keeper{no_species}; //!< The equation for this component is replace by the charge balance std::vector fixed_fugacity_cs {}; //!< Contains information about fixed fugacity constraints std::vector fixed_activity_cs {}; //!< Contains information about fixed activity constraints std::vector fixed_molality_cs {}; //!< Contains information about fixed molality constraints scalar_t inert_volume_fraction{ 0.0 }; //!< Volume fraction of inert solid (inert in the equilibrium computation) SurfaceConstraint surface_model{}; //!< Surface sorption model ElectronConstraint electron_constraint{}; //!< constraint for the electron WaterPartialPressureConstraint water_partial_pressure {}; //!< The partial pressure of water (if any) AdimensionalSystemConstraints(): total_concentrations() {} AdimensionalSystemConstraints(const Vector& total_concs): total_concentrations(total_concs) {} //! \brief Set the total concentrations void set_total_concentrations(const Vector& total_concs) {total_concentrations = total_concs;} //! \brief Enable the conservation of water void enable_conservation_water() noexcept {water_equation = WaterEquationType::MassConservation;} //! \brief Disable the conservation of water void disable_conservation_water() noexcept {water_equation = WaterEquationType::NoEquation;} //! \brief The system is saturated void set_saturated_system() noexcept {water_equation = WaterEquationType::SaturatedSystem;} //! \brief Disable the surface sorption model void disable_surface_model() noexcept {surface_model.model_type = SurfaceEquationType::NoEquation;} //! \brief Enable the surface sorption model //! \param surface_sorption_model_concentration concentration of the surface sorption sites void enable_surface_model(scalar_t surface_sorption_model_concentration) noexcept { surface_model.model_type = SurfaceEquationType::Equilibrium; surface_model.concentration = surface_sorption_model_concentration; } //! \brief Set the charge keeper to 'component' //! //! \param component Index of the component (in the database) void set_charge_keeper(index_t component) noexcept { charge_keeper = component; } //! \brief Add a fixed fugacity gas condition //! //! \param constraint struct containing the information about a fixed-fugacity constraint void add_fixed_fugacity_gas(const FixedFugacityConstraint& constraint) { fixed_fugacity_cs.push_back(constraint); } //! \brief Add a fixed fugacity gas condition //! //! \param gas Index of the gas (in the database) //! \param component Index of the corresponding component (in the database) //! \param logvalue Log_10 of the fugacity void add_fixed_fugacity_gas(index_t gas, index_t component, scalar_t logvalue) { fixed_fugacity_cs.emplace_back(FixedFugacityConstraint(gas, component, logvalue)); } //! \brief Add a fixed activity component condition //! //! \param constraint struct containing the information about a fixed-activity constraint void add_fixed_activity_component(const FixedActivityConstraint& constraint) { fixed_activity_cs.push_back(constraint); } //! \brief Add a fixed activity condition for a component //! //! \param component Index of the corresponding component (in the database) //! \param log_value Log_10 of the activity void add_fixed_activity_component(index_t component, scalar_t log_value) { fixed_activity_cs.emplace_back(FixedActivityConstraint(component, log_value)); } //! \brief Add a fixed molality condition for a component //! //! \param component Index of the corresponding component (in the database) //! \param log_value Log_10 of the molality void add_fixed_molality_component(index_t component, scalar_t log_value) { fixed_molality_cs.emplace_back(FixedMolalityConstraint(component, log_value)); } //! \brief Set the inert volume fraction //! //! The volume fraction of the inert phase is used to offset the saturation. //! This inert phase may correspond to aggregates or solid phases governed by kinetics. //! //! \param value volume fraction of the inert phase void set_inert_volume_fraction(scalar_t value) noexcept { inert_volume_fraction = value; } //! \brief Set a partial pressure model for water //! //! The partial pressure model for water is a function taking the saturation //! and returning the partial pressure of water void set_water_partial_pressure_model(water_partial_pressure_f model) { water_partial_pressure.use_partial_pressure_model = true; water_partial_pressure.partial_pressure_model = model; } // //! \brief Set the system at a fixed pE // void set_fixed_pe(scalar_t pe_value, index_t aqueous_species) noexcept { // electron_constraint = ElectronConstraint(pe_value, aqueous_species); // } }; } // end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMENSIONALSYSTEMSTRUCTS_HPP diff --git a/src/specmicp/adimensional/config_default_options_solver.h b/src/specmicp/adimensional/config_default_options_solver.h index 5c8f1a4..3205591 100644 --- a/src/specmicp/adimensional/config_default_options_solver.h +++ b/src/specmicp/adimensional/config_default_options_solver.h @@ -1,89 +1,89 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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. * ============================================================================= */ //! \file config_default_options_solver.h //! \internal //! \brief Defaut options for the adimensional solver #ifndef SPECMICP_ADIM_CONFIG_DEFAULTOPTIONSSOLVER_H #define SPECMICP_ADIM_CONFIG_DEFAULTOPTIONSSOLVER_H -#include "../../micpsolver/micpsolver_structs.hpp" +#include "specmicp_common/micpsolver/micpsolver_structs.hpp" // System options #define SPECMICP_DEFAULT_ENABLE_NONIDEAL true #define SPECMICP_DEFAULT_NONIDEAL_MAX_ITER 10 #define SPECMICP_DEFAULT_SCALING_ELECTRON 0.0 #define SPECMICP_DEFAULT_NONIDEAL_TOL 1e-8 #define SPECMICP_DEFAULT_UNDERRELAX_FACTOR 0.9 #define SPECMICP_DEFAULT_RESTART_CONC -6.0 #define SPECMICP_DEFAULT_NEW_COMPONENT_CONC -4.0 #define SPECMICP_DEFAULT_TRHSOLD_START_NONIDEAL 0.1 #define SPECMICP_DEFAULT_CUTOFF_TOT_CONC 1e-12 // Micpslover options #define SPECMICP_DEFAULT_USE_CRASHING MICPSOLVER_DEFAULT_USE_CRASHING #define SPECMICP_DEFAULT_USE_SCALING MICPSOLVER_DEFAULT_USE_SCALING #define SPECMICP_DEFAULT_USE_NONMONOTONE_LSEARCH MICPSOLVER_DEFAULT_USE_NONMONOTONE_LSEARCH #define SPECMICP_DEFAULT_MAX_ITER MICPSOLVER_DEFAULT_MAX_ITER #define SPECMICP_DEFAULT_MAX_ITER_MAXSTEP MICPSOLVER_DEFAULT_MAX_ITER_MAXSTEP #define SPECMICP_DEFAULT_MAX_FACT_STEP MICPSOLVER_DEFAULT_MAX_FACT_STEP #define SPECMICP_DEFAULT_RES_TOL MICPSOLVER_DEFAULT_RES_TOL #define SPECMICP_DEFAULT_STEP_TOL MICPSOLVER_DEFAULT_STEP_TOL #define SPECMICP_DEFAULT_COND_THRESHOLD MICPSOLVER_DEFAULT_COND_THRESHOLD #define SPECMICP_DEFAULT_PENALIZATION_FACTOR MICPSOLVER_DEFAULT_PENALIZATION_FACTOR #define SPECMICP_DEFAULT_MAX_STEP 10.0 #define SPECMICP_DEFAULT_FACTOR_DESC_COND -1.0 #define SPECMICP_DEFAULT_POWER_DESC_COND MICPSOLVER_DEFAULT_POWER_DESC_COND #define SPECMICP_DEFAULT_COEFF_ACCEPT_NEWTON MICPSOLVER_DEFAULT_COEFF_ACCEPT_NEWTON #define SPECMICP_DEFAULT_FACTOR_GRADIENT MICPSOLVER_DEFAULT_FACTOR_GRADIENT #define SPECMICP_DEFAULT_PROJ_VAR MICPSOLVER_DEFAULT_PROJ_VAR #define SPECMICP_DEFAULT_TRHSOLD_STATIONARY MICPSOLVER_DEFAULT_TRHSOLD_STATIONARY #define SPECMICP_DEFAULT_TRHSOLD_CYCLING_LSEARCH MICPSOLVER_DEFAULT_TRHSOLD_CYCLING_LSEARCH // Adim solver options #define SPECMICP_DEFAULT_ALLOW_RESTART true #define SPECMICP_DEFAULT_USE_PCFM false #define SPECMICP_DEFAULT_FORCE_PCFM false #define SPECMICP_DEFAULT_LENGTH_UNIT specmicp::units::LengthUnit::meter #endif // SPECMICP_ADIM_CONFIG_DEFAULTOPTIONSSOLVER_H diff --git a/src/specmicp/adimensional/equilibrium_curve.cpp b/src/specmicp/adimensional/equilibrium_curve.cpp index 8dbd157..8faac86 100644 --- a/src/specmicp/adimensional/equilibrium_curve.cpp +++ b/src/specmicp/adimensional/equilibrium_curve.cpp @@ -1,98 +1,97 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "equilibrium_curve.hpp" - #include "adimensional_system_solver.hpp" -#include "../../utils/log.hpp" +#include "specmicp_common/log.hpp" #define INITIALIZE true #define DONT_INITIALIZE false namespace specmicp { void EquilibriumCurve::solve_first_problem() { AdimensionalSystemSolver solver(m_data, m_constraints, m_solver_options); m_current_perf = solver.solve(m_solution_vector, INITIALIZE); if (m_current_perf.return_code <= micpsolver::MiCPSolverReturnCode::NotConvergedYet) { error_handling("Failed to solve first problem, return code : " + std::to_string((int) m_current_perf.return_code) + "."); } m_current_solution = solver.get_raw_solution(m_solution_vector); } void EquilibriumCurve::solve_problem() { AdimensionalSystemSolver solver(m_data, m_constraints, m_current_solution, m_solver_options); m_current_perf = solver.solve(m_solution_vector, DONT_INITIALIZE); if (m_current_perf.return_code <= micpsolver::MiCPSolverReturnCode::NotConvergedYet) { error_handling("Failed to solve problem, return code : " + std::to_string((int) m_current_perf.return_code) + "."); } m_current_solution = solver.get_raw_solution(m_solution_vector); } void EquilibriumCurve::error_handling(std::string msg) const { ERROR << msg; throw std::runtime_error(msg); } void EquilibriumCurve::set_problem() { update_problem(); } void EquilibriumCurve::post_processing() { output(); } void EquilibriumCurve::run_step() { set_problem(); solve_problem(); post_processing(); } } // end namespace specmicp #undef INITIALIZE #undef DONT_INITIALIZE diff --git a/src/specmicp/adimensional/equilibrium_curve.hpp b/src/specmicp/adimensional/equilibrium_curve.hpp index 0ab2b42..c692fac 100644 --- a/src/specmicp/adimensional/equilibrium_curve.hpp +++ b/src/specmicp/adimensional/equilibrium_curve.hpp @@ -1,144 +1,145 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_EQUILIBRIUMCURVE_HPP #define SPECMICP_SPECMICP_EQUILIBRIUMCURVE_HPP -#include "../../types.hpp" -#include "../../database.hpp" #include "adimensional_system_solver_structs.hpp" #include "adimensional_system_solution.hpp" +#include "specmicp_common/types.hpp" +#include "specmicp_database/database.hpp" + //! \file equilibrium_curve.hpp ABC to compute a reaction path namespace specmicp { //! \brief Abstract Base Class to compute an equilibrium curve //! //! class SPECMICP_DLL_PUBLIC EquilibriumCurve { public: EquilibriumCurve() {} EquilibriumCurve(RawDatabasePtr database, AdimensionalSystemConstraints constraints ): m_data(database), m_constraints(constraints) {} //! \brief Run one step of the equilibrium curve void run_step(); //! \brief Return the raw database RawDatabasePtr database() const {return m_data;} //! \brief Return the current solution const AdimensionalSystemSolution& current_solution() const { return m_current_solution; } //! \brief Return the current solution vector Vector& solution_vector() { return m_solution_vector; } //! \brief Return the current MiCPsolver performance const micpsolver::MiCPPerformance& solver_performance() const { return m_current_perf; } //! \brief Return a const reference to the solver options const AdimensionalSystemSolverOptions& solver_options() const { return m_solver_options; } //! \brief Return a const reference to the BC of AdimensionalSystem const AdimensionalSystemConstraints& constraints() const { return m_constraints; } //! \brief update the problem; virtual void update_problem() = 0; //! \brief Output - do nothing by default virtual void output() {} //! \brief How to handle an error virtual void error_handling(std::string msg) const; //! \brief Solve the first problem void solve_first_problem(); protected: //! \brief Set the database void set_database(RawDatabasePtr the_database) { m_data =the_database; } //! \brief Read-write reference to the conditions AdimensionalSystemConstraints& constraints() { return m_constraints; } //! \brief Read-write reference to the options of AdimensionalSystemSolver AdimensionalSystemSolverOptions& solver_options() { return m_solver_options; } //! \brief Input the first solution void initialize_solution(const AdimensionalSystemSolution& init) { m_current_solution = init; } private: //! \brief Set the problem, according user input void set_problem(); //! \brief Solve the problem //! //! May fail, call error_handling void solve_problem(); //! \brief Post processing of the data void post_processing(); RawDatabasePtr m_data; AdimensionalSystemConstraints m_constraints; AdimensionalSystemSolverOptions m_solver_options; AdimensionalSystemSolution m_current_solution; micpsolver::MiCPPerformance m_current_perf; Vector m_solution_vector; }; } // end namespace specmicp #endif // SPECMICP_SPECMICP_EQUILIBRIUMCURVE_HPP diff --git a/src/specmicp/adimensional_kinetics/CMakeLists.txt b/src/specmicp/adimensional_kinetics/CMakeLists.txt new file mode 100644 index 0000000..97b030d --- /dev/null +++ b/src/specmicp/adimensional_kinetics/CMakeLists.txt @@ -0,0 +1,20 @@ +set( specmicp_adim_kin_srcs + kinetic_system.cpp + kinetic_system_euler_solver.cpp + kinetic_system_solver.cpp +) + +set( specmicp_adim_kin_headers + kinetic_model.hpp + kinetic_system.hpp + kinetic_system_solver.hpp + kinetic_system_solver_structs.hpp + kinetic_variables.hpp +) + +add_to_specmicp_srcs_list(specmicp_adim_kin_srcs) +add_to_specmicp_headers_list(specmicp_adim_kin_headers) + +INSTALL(FILES ${specmicp_adim_kin_headers} + DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/adimensional_kinetics +) diff --git a/src/specmicp/adimensional_kinetics/kinetic_model.hpp b/src/specmicp/adimensional_kinetics/kinetic_model.hpp index 175bf8d..908ac9d 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_model.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_model.hpp @@ -1,87 +1,87 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICMODEL_HPP #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICMODEL_HPP -#include "../../types.hpp" +#include "specmicp_common/types.hpp" #include "kinetic_variables.hpp" namespace specmicp { namespace kinetics { // \brief Base class for a kinetic model class AdimKineticModel { public: AdimKineticModel() {} AdimKineticModel(const std::vector& list_species): m_list_species(list_species) {} virtual ~AdimKineticModel() {} //! \brief Return the number of kinetic minerals included in the problem index_t get_neq() {return m_list_species.size();} //! \brief Compute the kinetic rates and store them in dydt virtual void compute_rate( scalar_t t, const Vector& y, AdimKineticVariables& variables, Vector& dydt ) = 0; void add_equation(index_t species) {m_list_species.push_back(species);} //! \brief Index of species corresponding to equation 'id_equation' index_t index_species(index_t id_equation) {return m_list_species[id_equation];} //! \brief Iterator over the species vector std::vector::iterator species_begin() {return m_list_species.begin();} std::vector::iterator species_end() {return m_list_species.end();} private: std::vector m_list_species; }; } // end namespace kinetics } // end namespace specmicp #endif //SPECMICP_SPECMICP_ADIMKINETICS_KINETICMODEL_HPP diff --git a/src/specmicp/adimensional_kinetics/kinetic_system.cpp b/src/specmicp/adimensional_kinetics/kinetic_system.cpp index 4f6af27..b9102b3 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system.cpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system.cpp @@ -1,112 +1,112 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "kinetic_system.hpp" #include "kinetic_model.hpp" -#include "../adimensional/adimensional_system_solver.hpp" -#include "../../utils/log.hpp" +#include "specmicp/adimensional/adimensional_system_solver.hpp" +#include "specmicp_common/log.hpp" namespace specmicp { namespace kinetics { void AdimKineticSystem::update_total_concentrations(const Vector& y) { //Vector y_temp = y.cwiseMax(Vector::Zero(y.rows())); Vector update_minerals = (y - m_variables.concentration_minerals()); m_variables.total_concentrations() = m_variables.total_concentrations_initial(); for (auto it=m_model->species_begin(); it!=m_model->species_end(); ++it) { for (index_t component: m_data->range_component()) { if (m_data->nu_mineral_kinetic(*it, component) == 0.0) continue; m_variables.total_concentration(component) -= m_data->nu_mineral_kinetic(*it, component)*update_minerals(it-m_model->species_begin()); } } } void AdimKineticSystem::update_to_new_initial_condition(const Vector& y, scalar_t dt) { update_total_concentrations(y); m_variables.rate_components() += ( m_variables.total_concentrations() - m_variables.total_concentrations_initial() )/dt; m_variables.concentration_minerals() = y; m_variables.total_concentrations_initial() = m_variables.total_concentrations(); } void AdimKineticSystem::compute_rates(scalar_t t, const Vector& y, Vector& dydt) { m_model->compute_rate(t, y, m_variables, dydt); } void AdimKineticSystem::compute_equilibrium( AdimensionalSystemConstraints& constraints, AdimensionalSystemSolverOptions& options) { AdimensionalSystemSolver solver; Vector variables; specmicp::micpsolver::MiCPPerformance perf; constraints.total_concentrations = m_variables.total_concentrations(); if (m_variables.equilibrium_solution().is_valid) { solver = AdimensionalSystemSolver(m_data, constraints, m_variables.equilibrium_solution(), options); variables = m_variables.equilibrium_solution().main_variables; perf = solver.solve(variables); } else { solver = AdimensionalSystemSolver(m_data, constraints, options); perf = solver.solve(variables, true); } //std::cout << variables << std::endl; if ((int) perf.return_code < 0) { ERROR << "Failed to solve the system ! Error code " << (int) perf.return_code; } m_variables.update_equilibrium_solution(solver.get_raw_solution(variables)); } } // end namespace kinetics } // end namespace specmicp diff --git a/src/specmicp/adimensional_kinetics/kinetic_system.hpp b/src/specmicp/adimensional_kinetics/kinetic_system.hpp index a64acb2..628e73e 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system.hpp @@ -1,126 +1,130 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEM_HPP #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEM_HPP -#include -#include "../../types.hpp" -#include "../../database.hpp" + #include "kinetic_variables.hpp" -#include "../adimensional/adimensional_system_solver_structs.hpp" + +#include "specmicp_common/types.hpp" +#include "specmicp_database/database.hpp" + +#include "specmicp/adimensional/adimensional_system_solver_structs.hpp" + +#include namespace specmicp { //! \namespace specmicp::kinetics //! \brief Solver for kinetics systems namespace kinetics { class AdimKineticModel; //! \brief Kinetics System, //! //! Wrapper for a kinetic model class AdimKineticSystem { public: AdimKineticSystem( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_concentrations, AdimensionalSystemConstraints& constraints, RawDatabasePtr database ): m_data(database), m_model(model), m_constraints(constraints), m_variables(total_concentrations, mineral_concentrations) {} AdimKineticSystem( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_concentrations, AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& equilibrium_solution, RawDatabasePtr database ): m_data(database), m_model(model), m_constraints(constraints), m_variables(total_concentrations, mineral_concentrations, equilibrium_solution) {} //! \brief Compute the kinetics rates to be solved //! //! Use the kinetic model provided by the user void compute_rates(scalar_t x, const Vector& y, Vector& dydt); //! \brief Compute the equilibrium state of the solution void compute_equilibrium( AdimensionalSystemConstraints& constraints, AdimensionalSystemSolverOptions& options ); //! \brief Update the total concentrations //! //! \param y vector of variables (mols of minerals) void update_total_concentrations(const Vector& y); void update_to_new_initial_condition(const Vector& y, scalar_t dt); //! \brief Right Hand side function for the integrator void rhs(scalar_t x, const Vector& y, Vector& dydt, AdimensionalSystemSolverOptions& options) { update_total_concentrations(y); compute_equilibrium(m_constraints, options); compute_rates(x, y, dydt); } AdimKineticVariables& variables() {return m_variables;} AdimensionalSystemConstraints& constraints() {return m_constraints;} private: RawDatabasePtr m_data; std::shared_ptr m_model; AdimensionalSystemConstraints m_constraints; AdimKineticVariables m_variables; }; } // end namespace kinetics } // end namespace specmicp #endif //SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEM_HPP diff --git a/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.cpp b/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.cpp index 248f81b..4b48a0e 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.cpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.cpp @@ -1,68 +1,68 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "kinetic_system_euler_solver.hpp" #include "kinetic_model.hpp" -#include "../../odeint/runge_kutta_step.hpp" +#include "specmicp_common/odeint/runge_kutta_step.hpp" #include namespace specmicp { namespace kinetics { void AdimKineticSystemEulerSolver::solve(scalar_t dt, scalar_t total) { double t = 0.0; Vector y = m_system.variables().concentration_minerals(); Vector dydx(y.rows()); dydx.setZero(); while (t < total) { m_system.rhs(t, y, dydx, get_options().speciation_options); y += dt*dydx; m_system.update_to_new_initial_condition(y, dt); t += dt; if (t+dt > total) dt =(total - t); } m_system.update_total_concentrations(y); m_system.compute_equilibrium(m_system.constraints(), get_options().speciation_options); } } // end namespace kinetics } // end namespace specmicp diff --git a/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.hpp b/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.hpp index 14fe5b0..6d6ea74 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system_euler_solver.hpp @@ -1,88 +1,88 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMEULERSOLVER_HPP #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMEULERSOLVER_HPP #include "kinetic_system.hpp" #include "kinetic_system_solver_structs.hpp" -#include "../../utils/options_handler.hpp" +#include "specmicp_common/options_handler.hpp" namespace specmicp { namespace kinetics { class AdimKineticModel; class SPECMICP_DLL_PUBLIC AdimKineticSystemEulerSolver: public OptionsHandler { public: AdimKineticSystemEulerSolver( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_concentration, AdimensionalSystemConstraints& constraints, RawDatabasePtr database ): m_current_dt(-1), m_system(model, total_concentrations, mineral_concentration, constraints, database) {} AdimKineticSystemEulerSolver( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_concentration, AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& equilibrium_solution, RawDatabasePtr database ): m_current_dt(-1), m_system(model, total_concentrations, mineral_concentration, constraints, equilibrium_solution, database) {} void solve(scalar_t dt, scalar_t total); scalar_t current_dt() {return m_current_dt;} AdimKineticVariables& variables() {return m_system.variables();} private: scalar_t m_current_dt; AdimKineticSystem m_system; }; } // end namespace kinetics } // end namespace specmicp #endif //SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMEULERSOLVER_HPP diff --git a/src/specmicp/adimensional_kinetics/kinetic_system_solver.cpp b/src/specmicp/adimensional_kinetics/kinetic_system_solver.cpp index 583f94d..2185742 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system_solver.cpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system_solver.cpp @@ -1,84 +1,85 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "kinetic_system_solver.hpp" #include "kinetic_model.hpp" -#include "../../odeint/runge_kutta_step.hpp" + +#include "specmicp_common/odeint/runge_kutta_step.hpp" #include namespace specmicp { namespace kinetics { void AdimKineticSystemSolver::solve(scalar_t dt, scalar_t total) { double t = 0.0; Vector y = m_system.variables().concentration_minerals(); Vector dydx(y.rows()); dydx.setZero(); m_system.rhs(t, y, dydx, get_options().speciation_options); odeint::rhs_f rhs = std::bind(std::mem_fn(&AdimKineticSystem::rhs), m_system, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, get_options().speciation_options); //odeint::CashKarpStep driver = odeint::get_cash_karp_step(rhs); odeint::DormandPrinceStep driver = odeint::get_dormand_prince_step(rhs); odeint::StepLength stepl(dt, total); int cnt = 0; while (t < total) { driver.rk_step(y, dydx, t, stepl); m_system.update_to_new_initial_condition(y, stepl.did); cnt +=1; scalar_t remaining = stepl.remaining(t); if (remaining == 0.0) break; if (stepl.next > remaining) stepl.next = remaining; stepl.get_next(); m_current_dt = stepl.test; } m_system.update_total_concentrations(y); m_system.compute_equilibrium(m_system.constraints(), get_options().speciation_options); } } // end namespace kinetics } // end namespace specmicp diff --git a/src/specmicp/adimensional_kinetics/kinetic_system_solver.hpp b/src/specmicp/adimensional_kinetics/kinetic_system_solver.hpp index ccd118c..8ad420a 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system_solver.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system_solver.hpp @@ -1,88 +1,88 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVER_HPP #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVER_HPP #include "kinetic_system.hpp" #include "kinetic_system_solver_structs.hpp" -#include "../../utils/options_handler.hpp" +#include "specmicp_common/options_handler.hpp" namespace specmicp { namespace kinetics { class AdimKineticModel; class SPECMICP_DLL_PUBLIC AdimKineticSystemSolver: public OptionsHandler { public: AdimKineticSystemSolver( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_moles, AdimensionalSystemConstraints& constraints, RawDatabasePtr database ): m_current_dt(-1), m_system(model, total_concentrations, mineral_moles, constraints, database) {} AdimKineticSystemSolver( std::shared_ptr model, const Vector& total_concentrations, const Vector& mineral_moles, AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& equilibrium_solution, RawDatabasePtr database ): m_current_dt(-1), m_system(model, total_concentrations, mineral_moles, constraints, equilibrium_solution, database) {} void solve(scalar_t dt, scalar_t total); scalar_t current_dt() {return m_current_dt;} AdimKineticVariables& variables() {return m_system.variables();} private: scalar_t m_current_dt; AdimKineticSystem m_system; }; } // end namespace kinetics } // end namespace specmicp #endif //SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVER_HPP diff --git a/src/specmicp/adimensional_kinetics/kinetic_system_solver_structs.hpp b/src/specmicp/adimensional_kinetics/kinetic_system_solver_structs.hpp index 655150d..6efefce 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_system_solver_structs.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_system_solver_structs.hpp @@ -1,54 +1,54 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVERSTRUCTS #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVERSTRUCTS -#include "../adimensional/adimensional_system_solver_structs.hpp" -#include "../../odeint/runge_kutta_step_structs.hpp" +#include "specmicp/adimensional/adimensional_system_solver_structs.hpp" +#include "specmicp_common/odeint/runge_kutta_step_structs.hpp" namespace specmicp { namespace kinetics { struct AdimKineticSystemSolverOptions { odeint::EmbeddedRungeKuttaStepOptions rk_options; AdimensionalSystemSolverOptions speciation_options; }; } // end namespace kinetics } // end namespace specmicp #endif // SPECMICP_SPECMICP_ADIMKINETICS_KINETICSYSTEMSOLVERSTRUCTS diff --git a/src/specmicp/adimensional_kinetics/kinetic_variables.hpp b/src/specmicp/adimensional_kinetics/kinetic_variables.hpp index f562cea..8abb1c7 100644 --- a/src/specmicp/adimensional_kinetics/kinetic_variables.hpp +++ b/src/specmicp/adimensional_kinetics/kinetic_variables.hpp @@ -1,141 +1,141 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_ADIMKINETICS_KINETICVARIABLES_HPP #define SPECMICP_SPECMICP_ADIMKINETICS_KINETICVARIABLES_HPP -#include "../../types.hpp" -#include "../../database.hpp" -#include "../adimensional/adimensional_system_solution.hpp" +#include "specmicp_common/types.hpp" +#include "specmicp_database/database.hpp" +#include "specmicp/adimensional/adimensional_system_solution.hpp" namespace specmicp { namespace kinetics { //! \brief Variables used in a kinetic computation class AdimKineticVariables { public: AdimKineticVariables( const Vector& total_concentrations, const Vector& mineral_concentration ): m_mineral_kinetics(mineral_concentration), m_total_concentrations_initial(total_concentrations), m_total_concentrations(total_concentrations), m_rate(Vector::Zero(total_concentrations.rows())) {} AdimKineticVariables( const Vector& total_concentrations, const Vector& mineral_concentration, const AdimensionalSystemSolution& equilibrium_solution ): m_mineral_kinetics(mineral_concentration), m_total_concentrations_initial(total_concentrations), m_total_concentrations(total_concentrations), m_rate(Vector::Zero(total_concentrations.rows())), m_equilibrium_solution(equilibrium_solution) {} //! \brief Return the number of species considered in the kinetics computation index_t get_neq() {return m_mineral_kinetics.rows();} // Concentration of minerals // --------------------------- //! \brief Return mole number of mineral "mineral kinetic" scalar_t concentration_mineral(index_t mineral_kinetic) const {return m_mineral_kinetics(mineral_kinetic);} //! \brief Return a const reference to the vector of moles number const Vector& concentration_minerals() const {return m_mineral_kinetics;} //! \brief Return a reference to the vector of moles number Vector& concentration_minerals() {return m_mineral_kinetics;} // Total concentrations // -------------------- //! \brief Return the total concentration (at equilibrium) of 'component' scalar_t total_concentration(index_t component) const {return m_total_concentrations(component);} //! \brief Return the total concentration (at equilibrium) of 'component' scalar_t& total_concentration(index_t component) {return m_total_concentrations(component);} //! \brief Return a const reference to the total concentration (at equilibrium) vector const Vector& total_concentrations() const {return m_total_concentrations;} //! \brief Return a reference to the total concentration at equilibrium vector Vector& total_concentrations() {return m_total_concentrations; } // Initial total concentrations // ---------------------------- //! \brief Return the total concentration (at equilibrium) of 'component' scalar_t total_concentration_initial(index_t component) const { return m_total_concentrations_initial(component);} //! \brief Return a const reference to the total concentration (at equilibrium) vector const Vector& total_concentrations_initial() const {return m_total_concentrations_initial;} //! \brief Return a reference to the total concentration at equilibrium vector Vector& total_concentrations_initial() {return m_total_concentrations_initial; } // Rates // ----- //! \brief Return the value of the flux for 'component' scalar_t rate_component(index_t component) const { return m_rate(component); } //! \brief Return a const reference to the flux vector const Vector& rate_components() const {return m_rate;} //! \brief Return a reference to the flux vector Vector& rate_components() {return m_rate;} //! \brief reset the rates void reset_rate() {m_rate.setZero();} // Equilibrium // ------------ //! \brief Const Reference to the equilibrium state const AdimensionalSystemSolution& equilibrium_solution() const {return m_equilibrium_solution;} //! \brief Reference to the equilibrium State AdimensionalSystemSolution& equilibrium_solution() {return m_equilibrium_solution;} //! \brief Update the equilibrium state void update_equilibrium_solution(const AdimensionalSystemSolution& other) {m_equilibrium_solution = other;} private: Vector m_mineral_kinetics; //!< Number of moles of minerals at equilibrium Vector m_total_concentrations_initial; //!< Initial total concentrations at equilibrium Vector m_total_concentrations; //!< total concentration at equilibrium, Vector m_rate; AdimensionalSystemSolution m_equilibrium_solution; //!< Current equilibrium state }; } // end namespace kinetics } // end namespace specmicp #endif // SPECMICP_SPECMICP_KINETICS_KINETICVARIABLES_HPP diff --git a/src/specmicp/io/CMakeLists.txt b/src/specmicp/io/CMakeLists.txt new file mode 100644 index 0000000..2292558 --- /dev/null +++ b/src/specmicp/io/CMakeLists.txt @@ -0,0 +1,37 @@ +set( specmicp_io_srcs + configuration.cpp + print.cpp +) +set( specmicp_io_headers + configuration.hpp + print.hpp +) +set( specmicp_io_hdf5_srcs + hdf5_adimensional.cpp +) +set( specmicp_io_hdf5_headers + hdf5_adimensional.hpp +) + + +if (HDF5_FOUND) + list(APPEND specmicp_io_srcs + ${specmicp_io_hdf5_srcs} + ) + list(APPEND specmicp_io_headers + ${specmicp_io_hdf5_headers} + ) + + include_directories(${HDF5_INCLUDE_DIR}) + set_source_files_properties( + ${specmicp_io_hdf5_srcs} + PROPERTIES COMPILE_DEFINITIONS HDF5_DEFINITIONS + ) +endif() + +add_to_specmicp_srcs_list(specmicp_io_srcs) +add_to_specmicp_headers_list(specmicp_io_headers) + +INSTALL(FILES ${specmicp_io_headers} + DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/io +) diff --git a/src/specmicp/io/configuration.cpp b/src/specmicp/io/configuration.cpp index e6ea828..c20e1cf 100644 --- a/src/specmicp/io/configuration.cpp +++ b/src/specmicp/io/configuration.cpp @@ -1,857 +1,857 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "configuration.hpp" -#include "utils/io/yaml.hpp" -#include "utils/io/safe_config.hpp" #include "specmicp/adimensional/adimensional_system_solver_structs.hpp" #include "specmicp/adimensional/config_default_options_solver.h" -#include "specmicp/problem_solver/formulation.hpp" +#include "specmicp_common/io/yaml.hpp" +#include "specmicp_common/io/safe_config.hpp" +#include "specmicp_common/physics/units.hpp" +#include "specmicp_common/physics/io/configuration.hpp" +#include "specmicp_common/physics/laws.hpp" -#include "database.hpp" +#include "specmicp/problem_solver/formulation.hpp" -#include "physics/units.hpp" -#include "physics/io/configuration.hpp" -#include "physics/laws.hpp" +#include "specmicp_database/database.hpp" #define S_UNITS "units" #define S_SPECMICP "specmicp_options" #define S_SPECMICP_A_MAX_ITER "maximum_iterations" #define S_SPECMICP_A_RES_TOL "residual_tolerance" #define S_SPECMICP_A_STEP_TOL "step_tolerance" #define S_SPECMICP_A_MAX_STEP_LENGTH "maximum_step_length" #define S_SPECMICP_A_MAX_STEP_MAX_ITER "maximum_step_maximum_iterations" #define S_SPECMICP_A_SCALING "enable_scaling" #define S_SPECMICP_A_NONMONOTONE "enable_nonmonotone_linesearch" #define S_SPECMICP_A_DESCENT_DIRECTION "factor_descent_direction" #define S_SPECMICP_A_COND_CHECK "threshold_condition_check" #define S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH "threshold_cycling_linesearch" #define S_SPECMICP_A_NONIDEAL_TOL "non_ideality_tolerance" #define S_SPECMICP_A_CUTOFF_TOT_CONC "cutoff_total_concentration" #define S_FORMULATION "formulation" #define S_FORMULATION_A_SOLUTION "solution" #define S_FORMULATION_A_AQUEOUS "aqueous" #define S_FORMULATION_A_MINERALS "solid_phases" #define S_FORMULATION_A_AMOUNT "amount" #define S_FORMULATION_A_UNIT "unit" #define S_FORMULATION_A_LABEL "label" #define S_CONSTRAINTS "constraints" #define S_CONSTRAINTS_A_CHARGEKEEPER "charge_keeper" #define S_CONSTRAINTS_A_FIXEDACTIVITY "fixed_activity" #define S_CONSTRAINTS_A_FIXEDMOLALITY "fixed_molality" #define S_CONSTRAINTS_A_FIXEDFUGACITY "fixed_fugacity" #define S_CONSTRAINTS_A_SATURATED_SYSTEM "saturated_system" #define S_CONSTRAINTS_A_CONSERVATION_WATER "conservation_water" #define S_CONSTRAINTS_A_SURFACE_MODEL "surface_model" #define S_CONSTRAINTS_A_SURFACE_CONCENTRATION "surface_site_concentration" #define S_CONSTRAINTS_A_LABEL "label" #define S_CONSTRAINTS_A_AMOUNT "amount" #define S_CONSTRAINTS_A_AMOUNTLOG "amount_log" #define S_CONSTRAINTS_A_LABEL_COMPONENT "label_component" #define S_CONSTRAINTS_A_LABEL_GAS "label_gas" namespace specmicp { namespace io { // Additional declarations // ======================= void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_activity, const RawDatabasePtr& raw_db ); void configure_specmicp_constraints_fixed_fugacity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_fugacity, const RawDatabasePtr& raw_db ); void configure_specmicp_constraints_fixed_molality( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_molality, const RawDatabasePtr& raw_db ); void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_activity ); void configure_specmicp_constraints_fixed_fugacity( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_fugacity ); void configure_specmicp_constraints_fixed_molality( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_molality ); scalar_t molar_mass_aqueous_unknown_class(const std::string& label, const RawDatabasePtr& raw_db); scalar_t molar_mass_solid_unknown_class(const std::string& label, const RawDatabasePtr& raw_db ); scalar_t molar_volume_solid_unknown_class(const std::string& label, const RawDatabasePtr& raw_db ); // Implementation // ============== void configure_specmicp_options( AdimensionalSystemSolverOptions& options, const YAML::Node& configuration ) { check_mandatory_yaml_node(configuration, S_SPECMICP, "__main__"); const YAML::Node& conf = configuration[S_SPECMICP]; // fixme units //options.units_set = the_units_set; options.solver_options.fvectol = get_yaml_optional(conf, S_SPECMICP_A_RES_TOL, S_SPECMICP, SPECMICP_DEFAULT_RES_TOL); options.solver_options.steptol = get_yaml_optional(conf, S_SPECMICP_A_STEP_TOL, S_SPECMICP, SPECMICP_DEFAULT_STEP_TOL); options.solver_options.max_iter = get_yaml_optional(conf, S_SPECMICP_A_MAX_ITER, S_SPECMICP, SPECMICP_DEFAULT_MAX_ITER); options.solver_options.maxstep = get_yaml_optional(conf, S_SPECMICP_A_MAX_STEP_LENGTH, S_SPECMICP, SPECMICP_DEFAULT_MAX_STEP); options.solver_options.maxiter_maxstep = get_yaml_optional(conf, S_SPECMICP_A_MAX_STEP_MAX_ITER, S_SPECMICP, std::min(SPECMICP_DEFAULT_MAX_ITER_MAXSTEP, options.solver_options.max_iter) ); options.solver_options.use_scaling = get_yaml_optional(conf, S_SPECMICP_A_SCALING, S_SPECMICP, SPECMICP_DEFAULT_USE_SCALING); options.solver_options.non_monotone_linesearch = get_yaml_optional(conf, S_SPECMICP_A_NONMONOTONE, S_SPECMICP, SPECMICP_DEFAULT_USE_NONMONOTONE_LSEARCH); options.solver_options.factor_descent_condition = get_yaml_optional(conf, S_SPECMICP_A_DESCENT_DIRECTION, S_SPECMICP, SPECMICP_DEFAULT_FACTOR_DESC_COND ); options.solver_options.condition_limit = get_yaml_optional(conf, S_SPECMICP_A_COND_CHECK, S_SPECMICP, SPECMICP_DEFAULT_COND_THRESHOLD); options.solver_options.threshold_cycling_linesearch = get_yaml_optional(conf, S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH, S_SPECMICP, SPECMICP_DEFAULT_TRHSOLD_CYCLING_LSEARCH ); options.system_options.non_ideality_tolerance = get_yaml_optional(conf, S_SPECMICP_A_NONIDEAL_TOL, S_SPECMICP, SPECMICP_DEFAULT_NONIDEAL_TOL); options.system_options.cutoff_total_concentration = get_yaml_optional(conf, S_SPECMICP_A_CUTOFF_TOT_CONC, S_SPECMICP, SPECMICP_DEFAULT_CUTOFF_TOT_CONC); if (configuration[S_UNITS]) { options.units_set = configure_units(configuration[S_UNITS]); } } void configure_specmicp_options( AdimensionalSystemSolverOptions& options, const units::UnitsSet& the_units, YAMLConfigHandle&& conf ) { micpsolver::MiCPSolverOptions& solver = options.solver_options; conf.set_if_attribute_exists(solver.fvectol, S_SPECMICP_A_RES_TOL); conf.set_if_attribute_exists(solver.steptol, S_SPECMICP_A_STEP_TOL); conf.set_if_attribute_exists(solver.max_iter, S_SPECMICP_A_MAX_ITER); conf.set_if_attribute_exists(solver.maxstep, S_SPECMICP_A_MAX_STEP_LENGTH); conf.set_if_attribute_exists(solver.maxiter_maxstep, S_SPECMICP_A_MAX_STEP_MAX_ITER); conf.set_if_attribute_exists(solver.use_scaling, S_SPECMICP_A_SCALING); conf.set_if_attribute_exists(solver.non_monotone_linesearch, S_SPECMICP_A_NONMONOTONE); conf.set_if_attribute_exists(solver.factor_descent_condition, S_SPECMICP_A_DESCENT_DIRECTION); conf.set_if_attribute_exists(solver.condition_limit, S_SPECMICP_A_COND_CHECK); conf.set_if_attribute_exists(solver.threshold_cycling_linesearch, S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH); AdimensionalSystemOptions& system = options.system_options; conf.set_if_attribute_exists(system.non_ideality_tolerance, S_SPECMICP_A_NONIDEAL_TOL); conf.set_if_attribute_exists(system.cutoff_total_concentration, S_SPECMICP_A_CUTOFF_TOT_CONC); options.units_set = the_units; } void configure_specmicp_formulation( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& units_sets ) { configure_specmicp_formulation_solution(formulation, conf_formulation, units_sets); configure_specmicp_formulation_aqueous(formulation, conf_formulation, raw_db, units_sets); configure_specmicp_formulation_solid(formulation, conf_formulation, raw_db, units_sets); } void configure_specmicp_formulation_solution( Formulation& formulation, const YAML::Node& conf_formulation, const units::UnitsSet& _) { check_mandatory_yaml_node(conf_formulation, S_FORMULATION_A_SOLUTION, S_FORMULATION); const YAML::Node& sol_conf = conf_formulation[S_FORMULATION_A_SOLUTION]; check_mandatory_yaml_node(sol_conf, S_FORMULATION_A_AMOUNT, S_FORMULATION_A_SOLUTION); scalar_t factor=1.0; if (sol_conf[S_FORMULATION_A_UNIT]) { units::AmountUnit unit; units::parse_amount_unit(sol_conf[S_FORMULATION_A_UNIT].as(), unit); if (unit.type != units::AmountUnitType::Mass ) { throw std::invalid_argument("'" + sol_conf[S_FORMULATION_A_UNIT].as() + "'' is not a mass unit (in the solution configuration) !"); } factor = unit.factor_si; } formulation.set_mass_solution(sol_conf[S_FORMULATION_A_AMOUNT].as()*factor); } void configure_specmicp_formulation_aqueous( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& _) { check_mandatory_yaml_node(conf_formulation, S_FORMULATION_A_AQUEOUS, S_FORMULATION); const YAML::Node& aq_conf = conf_formulation[S_FORMULATION_A_AQUEOUS]; for (auto aqueous_conf: aq_conf) { check_mandatory_yaml_node(aqueous_conf, S_FORMULATION_A_LABEL, S_FORMULATION_A_AQUEOUS); check_mandatory_yaml_node(aqueous_conf, S_FORMULATION_A_AMOUNT, S_FORMULATION_A_AQUEOUS); check_mandatory_yaml_node(aqueous_conf, S_FORMULATION_A_UNIT, S_FORMULATION_A_AQUEOUS); std::string label = aqueous_conf[S_FORMULATION_A_LABEL].as(); scalar_t factor = 1.0; units::AmountUnit unit; units::parse_amount_unit(aqueous_conf[S_FORMULATION_A_UNIT].as(), unit); if (unit.type == units::AmountUnitType::Molality) { factor = unit.factor_si; } else if (unit.type == units::AmountUnitType::MoleConcentration) { factor = unit.factor_si/laws::density_water(units::celsius(25.0), units::LengthUnit::meter, units::MassUnit::kilogram); } else if (unit.type == units::AmountUnitType::MassConcentration) { const scalar_t molar_mass = molar_mass_aqueous_unknown_class(label, raw_db); factor = unit.factor_si / molar_mass / laws::density_water(units::celsius(25.0), units::LengthUnit::meter, units::MassUnit::kilogram); } else if (unit.type == units::AmountUnitType::Mass) { const scalar_t molar_mass = molar_mass_aqueous_unknown_class(label, raw_db); factor = unit.factor_si / molar_mass / formulation.mass_solution; } else if (unit.type == units::AmountUnitType::NumberOfMoles) { factor = unit.factor_si / formulation.mass_solution; } else { throw std::invalid_argument("'"+aqueous_conf[S_FORMULATION_A_UNIT].as() + "' is not a valid unit for an aqueous species"); } const scalar_t molality = factor*aqueous_conf[S_FORMULATION_A_AMOUNT].as(); formulation.add_aqueous_species(label, molality); } } void configure_specmicp_formulation_solid( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& unit_set) { check_mandatory_yaml_node(conf_formulation, S_FORMULATION_A_MINERALS, S_FORMULATION); const YAML::Node& minerals_conf = conf_formulation[S_FORMULATION_A_MINERALS]; for (auto solid_conf : minerals_conf) { check_mandatory_yaml_node(solid_conf, S_FORMULATION_A_LABEL, S_FORMULATION_A_AQUEOUS); check_mandatory_yaml_node(solid_conf, S_FORMULATION_A_AMOUNT, S_FORMULATION_A_AQUEOUS); check_mandatory_yaml_node(solid_conf, S_FORMULATION_A_UNIT, S_FORMULATION_A_AQUEOUS); const std::string label = solid_conf[S_FORMULATION_A_LABEL].as(); scalar_t factor = 1.0; units::AmountUnit unit; units::parse_amount_unit(solid_conf[S_FORMULATION_A_UNIT].as(), unit); // convert to mole concentration is SI if (unit.type == units::AmountUnitType::MoleConcentration or unit.type == units::AmountUnitType::NumberOfMoles) { factor = unit.factor_si; } else if (unit.type == units::AmountUnitType::MassConcentration or unit.type == units::AmountUnitType::Mass ) { const scalar_t molar_mass = molar_mass_solid_unknown_class(label, raw_db); factor = unit.factor_si / molar_mass; } else if (unit.type == units::AmountUnitType::Volume) { const scalar_t molar_volume = molar_volume_solid_unknown_class(label, raw_db); // may raise an error factor = unit.factor_si / molar_volume; } else { throw std::invalid_argument("'"+solid_conf[S_FORMULATION_A_UNIT].as() + "' is not a valid unit for a solid phase"); } // set to the correct units switch (unit_set.length) { case units::LengthUnit::decimeter: factor *= 1e-3; break; case units::LengthUnit::centimeter: factor *= 1e-6; break; case units::LengthUnit::meter: break; // just to remove warning } const scalar_t mole_conc = factor*solid_conf[S_FORMULATION_A_AMOUNT].as(); formulation.add_mineral(label, mole_conc); } } // Constraints // =========== // new interface // ------------- void configure_specmicp_constraints( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints ) { // equation for water // ================== // enable conservation of water if (conf_constraints.has_attribute(S_CONSTRAINTS_A_CONSERVATION_WATER)) { auto tmp_water = conf_constraints.get_attribute( S_CONSTRAINTS_A_CONSERVATION_WATER); if (tmp_water) { // only one constraints if (conf_constraints.get_optional_attribute(S_CONSTRAINTS_A_SATURATED_SYSTEM, false)) { conf_constraints.report_error(YAMLConfigError::InvalidArgument, "Attributes " S_CONSTRAINTS_A_CONSERVATION_WATER "and " S_CONSTRAINTS_A_SATURATED_SYSTEM " cannot be set to true at the same time"); } constraints.enable_conservation_water(); } else constraints.disable_conservation_water(); } // or enable saturated system if (conf_constraints.has_attribute(S_CONSTRAINTS_A_SATURATED_SYSTEM)) { if (conf_constraints.get_attribute(S_CONSTRAINTS_A_SATURATED_SYSTEM)) { constraints.set_saturated_system(); } } // Aqueous components // ================== // charge keeper // ------------- if (conf_constraints.has_attribute(S_CONSTRAINTS_A_CHARGEKEEPER)) { const auto label = conf_constraints.get_attribute( S_CONSTRAINTS_A_CHARGEKEEPER); const index_t id = raw_db->get_id_component(label); if (id == no_species) { conf_constraints.report_error( YAMLConfigError::InvalidArgument, "Species " + label + " does not exist in the database." " It can't be the charge keeper." ); } constraints.set_charge_keeper(id); } // Fixed activity // --------------- if (conf_constraints.has_section(S_CONSTRAINTS_A_FIXEDACTIVITY)) { configure_specmicp_constraints_fixed_activity( constraints, raw_db, conf_constraints.get_section(S_CONSTRAINTS_A_FIXEDACTIVITY) ); } // Fixed fugacity // -------------- if (conf_constraints.has_section(S_CONSTRAINTS_A_FIXEDFUGACITY)) { configure_specmicp_constraints_fixed_fugacity( constraints, raw_db, conf_constraints.get_section(S_CONSTRAINTS_A_FIXEDFUGACITY) ); } // Fixed activity // --------------- if (conf_constraints.has_section(S_CONSTRAINTS_A_FIXEDMOLALITY)) { configure_specmicp_constraints_fixed_molality( constraints, raw_db, conf_constraints.get_section(S_CONSTRAINTS_A_FIXEDMOLALITY) ); } // Surface model // ============= if (conf_constraints.has_attribute(S_CONSTRAINTS_A_SURFACE_MODEL)) { const bool is_enabled = conf_constraints.get_attribute( S_CONSTRAINTS_A_SURFACE_MODEL ); if (is_enabled) { const auto value = conf_constraints.get_required_attribute( S_CONSTRAINTS_A_SURFACE_CONCENTRATION); constraints.enable_surface_model(value); } else { constraints.disable_surface_model(); } } } // helpers functions index_t get_id_component( const database::DataContainer* const raw_db, YAMLConfigHandle& conf, const std::string& attr_key=S_CONSTRAINTS_A_LABEL ) { const auto label = conf.get_required_attribute(attr_key); const index_t id = raw_db->get_id_component(label); if (id == no_species) { conf.report_error( YAMLConfigError::InvalidArgument, "'" + label + "' is not a valid component." ); } return id; } index_t get_id_gas( const database::DataContainer* const raw_db, YAMLConfigHandle& conf, const std::string& attr_key=S_CONSTRAINTS_A_LABEL_GAS ) { const auto label = conf.get_required_attribute(attr_key); const index_t id = raw_db->get_id_gas(label); if (id == no_species) { conf.report_error( YAMLConfigError::InvalidArgument, "'" + label + "' is not a valid gas." ); } return id; } scalar_t get_value(YAMLConfigHandle& conf) { scalar_t value; if (conf.has_attribute(S_CONSTRAINTS_A_AMOUNT)) { value = std::log10(conf.get_attribute(S_CONSTRAINTS_A_AMOUNT)); } else if (conf.has_attribute(S_CONSTRAINTS_A_AMOUNTLOG)) { value = conf.get_attribute(S_CONSTRAINTS_A_AMOUNTLOG); } else { conf.report_error( YAMLConfigError::InvalidArgument, "Expected one of '" S_CONSTRAINTS_A_AMOUNT "' or '" S_CONSTRAINTS_A_AMOUNTLOG "'." ); } return value; } void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_activity ) { for (uindex_t ind=0; ind(); const index_t id = raw_db->get_id_component(label); if (id == no_species) { throw std::invalid_argument("'"+label+"' is not a valid component (charge_keeper)."); } constraints.set_charge_keeper(id); } // Fixed activity // -------------- if (conf_constraints[S_CONSTRAINTS_A_FIXEDACTIVITY]) { configure_specmicp_constraints_fixed_activity(constraints, conf_constraints[S_CONSTRAINTS_A_FIXEDACTIVITY], raw_db); } // Fixed fugacity // -------------- if (conf_constraints[S_CONSTRAINTS_A_FIXEDFUGACITY]) { configure_specmicp_constraints_fixed_fugacity(constraints, conf_constraints[S_CONSTRAINTS_A_FIXEDFUGACITY], raw_db); } // Fixed molality // -------------- if (conf_constraints[S_CONSTRAINTS_A_FIXEDMOLALITY]) { configure_specmicp_constraints_fixed_molality(constraints, conf_constraints[S_CONSTRAINTS_A_FIXEDMOLALITY], raw_db); } // Water // ===== if (conf_constraints[S_CONSTRAINTS_A_CONSERVATION_WATER]) { if (conf_constraints[S_CONSTRAINTS_A_SATURATED_SYSTEM] and conf_constraints[S_CONSTRAINTS_A_SATURATED_SYSTEM].as()) { throw std::invalid_argument("Options '" S_CONSTRAINTS_A_CONSERVATION_WATER "' and '" S_CONSTRAINTS_A_SATURATED_SYSTEM "' cannot be used at the same time"); } const bool value = conf_constraints[S_CONSTRAINTS_A_CONSERVATION_WATER].as(); if (value) constraints.enable_conservation_water(); else constraints.disable_conservation_water(); } if (conf_constraints[S_CONSTRAINTS_A_SATURATED_SYSTEM]) { const bool value = conf_constraints[S_CONSTRAINTS_A_SATURATED_SYSTEM].as(); if (value) constraints.set_saturated_system(); } // Surface model // ============= if (conf_constraints[S_CONSTRAINTS_A_SURFACE_MODEL]) { const bool is_enabled = conf_constraints[S_CONSTRAINTS_A_SURFACE_MODEL].as(); if (is_enabled) { check_mandatory_yaml_node(conf_constraints, S_CONSTRAINTS_A_SURFACE_CONCENTRATION, S_CONSTRAINTS); const scalar_t value = conf_constraints[S_CONSTRAINTS_A_SURFACE_CONCENTRATION].as(); constraints.enable_surface_model(value); } else { constraints.disable_surface_model(); } } } void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_activity, const RawDatabasePtr& raw_db ) { for (auto fixact_conf: conf_constraints_fixed_activity) { check_mandatory_yaml_node(fixact_conf, S_CONSTRAINTS_A_LABEL, S_CONSTRAINTS_A_FIXEDACTIVITY); const std::string label = fixact_conf[S_CONSTRAINTS_A_LABEL].as(); const index_t id = raw_db->get_id_component(label); if (id == no_species) { throw std::invalid_argument("'"+label+"' is not a valid component (fixed_activity)."); } scalar_t value; if (fixact_conf[S_CONSTRAINTS_A_AMOUNT]) { value = std::log10(fixact_conf[S_CONSTRAINTS_A_AMOUNT].as()); } else if (fixact_conf[S_CONSTRAINTS_A_AMOUNTLOG]) { value = fixact_conf[S_CONSTRAINTS_A_AMOUNTLOG].as(); } else { throw std::invalid_argument("Either '" S_CONSTRAINTS_A_AMOUNT "' or '" S_CONSTRAINTS_A_AMOUNTLOG "' are required in section '" S_CONSTRAINTS_A_FIXEDACTIVITY "'."); } constraints.add_fixed_activity_component(id, value); } } void configure_specmicp_constraints_fixed_fugacity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_fugacity, const RawDatabasePtr& raw_db ) { for(auto fixfug_conf: conf_constraints_fixed_fugacity) { check_mandatory_yaml_node(fixfug_conf, S_CONSTRAINTS_A_LABEL_COMPONENT, S_CONSTRAINTS_A_FIXEDFUGACITY); check_mandatory_yaml_node(fixfug_conf, S_CONSTRAINTS_A_LABEL_GAS, S_CONSTRAINTS_A_FIXEDFUGACITY); const std::string& label_c = fixfug_conf[S_CONSTRAINTS_A_LABEL_COMPONENT].as(); const index_t id_c = raw_db->get_id_component(label_c); if (id_c == no_species) { throw std::invalid_argument("'"+label_c+"' is not a valid component (fixed_fugacity)"); } const std::string& label_g = fixfug_conf[S_CONSTRAINTS_A_LABEL_GAS].as(); const index_t id_g = raw_db->get_id_gas(label_g); if (id_g == no_species) { throw std::invalid_argument("'"+label_g+"' is not a valid gas (fixed_fugacity)"); } scalar_t value; if (fixfug_conf[S_CONSTRAINTS_A_AMOUNT]) { value = std::log10(fixfug_conf[S_CONSTRAINTS_A_AMOUNT].as()); } else if (fixfug_conf[S_CONSTRAINTS_A_AMOUNTLOG]) { value = fixfug_conf[S_CONSTRAINTS_A_AMOUNTLOG].as(); } else { throw std::invalid_argument("Either '" S_CONSTRAINTS_A_AMOUNT "' or '" S_CONSTRAINTS_A_AMOUNTLOG "' are required in section '" S_CONSTRAINTS_A_FIXEDFUGACITY "'."); } constraints.add_fixed_fugacity_gas(id_g, id_c, value); } } void configure_specmicp_constraints_fixed_molality( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_molality, const RawDatabasePtr& raw_db ) { for (auto fixmol_conf: conf_constraints_fixed_molality) { check_mandatory_yaml_node(fixmol_conf, S_CONSTRAINTS_A_LABEL, S_CONSTRAINTS_A_FIXEDMOLALITY); const std::string& label = fixmol_conf[S_CONSTRAINTS_A_LABEL].as(); const index_t id = raw_db->get_id_component(label); if (id == no_species) { throw std::invalid_argument("'"+label+"' is not a valid component (fixed_molality)"); } scalar_t value; if (fixmol_conf[S_CONSTRAINTS_A_AMOUNT]) { value = std::log10(fixmol_conf[S_CONSTRAINTS_A_AMOUNT].as()); } else if (fixmol_conf[S_CONSTRAINTS_A_AMOUNTLOG]) { value = fixmol_conf[S_CONSTRAINTS_A_AMOUNTLOG].as(); } else { throw std::invalid_argument("Either '" S_CONSTRAINTS_A_AMOUNT "' or '" S_CONSTRAINTS_A_AMOUNTLOG "' are required in section '" S_CONSTRAINTS_A_FIXEDMOLALITY "'."); } constraints.add_fixed_molality_component(id, value); } } scalar_t molar_mass_aqueous_unknown_class(const std::string& label, const RawDatabasePtr& raw_db) { scalar_t molar_mass; if (raw_db->get_id_component(label) != no_species) { molar_mass = raw_db->molar_mass_basis(raw_db->get_id_component(label), units::MassUnit::kilogram); } else if (raw_db->get_id_aqueous(label) != no_species) { molar_mass = raw_db->molar_mass_aqueous(raw_db->get_id_aqueous(label), units::MassUnit::kilogram); } else if (raw_db->get_id_compound(label) != no_species) { molar_mass = raw_db->molar_mass_compound(raw_db->get_id_compound(label), units::MassUnit::kilogram); } else { throw std::invalid_argument("'"+label+"is not a valid species"); } return molar_mass; } scalar_t molar_mass_solid_unknown_class(const std::string& label, const RawDatabasePtr& raw_db ) { scalar_t molar_mass; if (raw_db->get_id_mineral(label) != no_species) { molar_mass = raw_db->molar_mass_mineral(raw_db->get_id_mineral(label), units::MassUnit::kilogram); } else if (raw_db->get_id_mineral_kinetic(label) != no_species) { molar_mass = raw_db->molar_mass_mineral_kinetic(raw_db->get_id_mineral_kinetic(label), units::MassUnit::kilogram); } else { throw std::invalid_argument("'"+label+"' is not a valid solid phase."); } return molar_mass; } scalar_t molar_volume_solid_unknown_class(const std::string& label, const RawDatabasePtr& raw_db ) { scalar_t molar_volume = -1; if (raw_db->get_id_mineral(label) != no_species) { molar_volume = raw_db->molar_volume_mineral(raw_db->get_id_mineral(label), units::LengthUnit::meter); } else if (raw_db->get_id_mineral_kinetic(label) != no_species) { molar_volume = raw_db->molar_volume_mineral_kinetic(raw_db->get_id_mineral_kinetic(label), units::LengthUnit::meter); } else { throw std::invalid_argument("'"+label+"' is not a valid solid phase."); } if (molar_volume < 0) { throw std::runtime_error("The molar volume for solid phase '"+label+"' is not defined"); } return molar_volume; } } //end namespace io } //end namespace specmicp diff --git a/src/specmicp/io/configuration.hpp b/src/specmicp/io/configuration.hpp index bb7ea9b..3daed20 100644 --- a/src/specmicp/io/configuration.hpp +++ b/src/specmicp/io/configuration.hpp @@ -1,161 +1,161 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_IO_HPP #define SPECMICP_SPECMICP_IO_HPP -#include "types.hpp" +#include "specmicp_common/types.hpp" #include //! \file specmicp/io/configuration.hpp //! \brief configuration for specmicp adim system namespace YAML { class Node; } //end namespace YAML namespace specmicp { struct AdimensionalSystemSolverOptions; struct Formulation; struct AdimensionalSystemConstraints; namespace database { struct DataContainer; } using RawDatabasePtr = std::shared_ptr; namespace units { struct UnitsSet; } //end namespace units namespace io { class YAMLConfigHandle; /*! \brief Configure specmicp solver options Erase values with default if not given Configuration must be of the form : \code specmicp_options: opts1: val1 opts2: val2 .... [units]: ... \endcode */ void SPECMICP_DLL_PUBLIC configure_specmicp_options( AdimensionalSystemSolverOptions& options, const YAML::Node& configuration ) SPECMICP_DEPRECATED("Use new safer config"); /*! \brief Configure specmicp solver options Conserve previous values if not given. Configuration must be of the form : \code opts1: val1 opts2: val2 .... \endcode \warning units must be set elsewhere ! */ void SPECMICP_DLL_PUBLIC configure_specmicp_options( AdimensionalSystemSolverOptions& options, const units::UnitsSet& the_units, YAMLConfigHandle&& configuration ); void SPECMICP_DLL_PUBLIC configure_specmicp_formulation( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& units_sets ); void SPECMICP_DLL_PUBLIC configure_specmicp_formulation_solution( Formulation& formulation, const YAML::Node& conf_formulation, const units::UnitsSet& units_set ); void SPECMICP_DLL_PUBLIC configure_specmicp_formulation_aqueous( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& _ ); void SPECMICP_DLL_PUBLIC configure_specmicp_formulation_solid( Formulation& formulation, const YAML::Node& conf_formulation, const RawDatabasePtr& raw_db, const units::UnitsSet& _ ); //! \brief Configure specmicp constraints void SPECMICP_DLL_PUBLIC configure_specmicp_constraints( AdimensionalSystemConstraints& constraints, const database::DataContainer* const raw_db, YAMLConfigHandle&& conf_constraints ); //! \brief Configure specmicp constraints //! //! \deprecated void SPECMICP_DLL_PUBLIC configure_specmicp_constraints( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints, const RawDatabasePtr& raw_db ) SPECMICP_DEPRECATED("Use new safer config"); } //end namespace io } //end namespace specmicp #endif // SPECMICP_SPECMICP_IO_HPP diff --git a/src/specmicp/io/hdf5_adimensional.cpp b/src/specmicp/io/hdf5_adimensional.cpp index 17872d1..8dde0f5 100644 --- a/src/specmicp/io/hdf5_adimensional.cpp +++ b/src/specmicp/io/hdf5_adimensional.cpp @@ -1,354 +1,355 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "hdf5_adimensional.hpp" -#include "../adimensional/adimensional_system_solution.hpp" -#include "../../utils/io/specmicp_hdf5.hpp" -#include "../../utils/io/hdf5_eigen.hpp" +#include "specmicp/adimensional/adimensional_system_solution.hpp" + +#include "specmicp_common/io/specmicp_hdf5.hpp" +#include "specmicp_common/io/hdf5_eigen.hpp" #include "H5Exception.h" #include namespace specmicp { namespace io { #define MAIN_VARIABLES_DS "main_variables" #define SECONDARY_MOLALITIES_DS "secondary_molalities" #define LOG_GAMMA_DS "log_gamma" #define GAS_FUGACITIES_DS "gas_fugacities" #define SORBED_MOLALITIES_DS "sorbed_molalities" #define SCALAR_VARS_ATT "scalar_vars" //! \brief Implementation class to save a HDF5 file //! //! \internal class SPECMICP_DLL_LOCAL AdimensionalSystemSolutionHDF5Saver { public: AdimensionalSystemSolutionHDF5Saver( const AdimensionalSystemSolution& solution): m_solution(solution) {} //! \brief Save the solution into file (in subgroup section/name) void save( HDF5File& file, const std::string& name, const std::string& section ); private: void save_main_variables(HDF5File& file, const std::string& section); void save_molalities(HDF5File& file, const std::string& section); void save_loggamma(HDF5File& file, const std::string& section); void save_gas_fugacities(HDF5File& file, const std::string& section); void save_sorbed_molalities(HDF5File& file, const std::string& section); const AdimensionalSystemSolution& m_solution; }; //! \brief Implementation : read a HDF5 file //! //! \internal namespace AdimensionalSystemSolutionHDF5Reader { //! \brief Save the solution into file (in subgroup section/name) static void read( const HDF5File& file, const std::string& name, const std::string& section, AdimensionalSystemSolution& solution); //! \brief Save the solution into file (in subgroup section/name) static void read( const H5::Group& group, AdimensionalSystemSolution& solution ); static void read_main_variables( const H5::Group& group, AdimensionalSystemSolution& solution); static void read_molalities( const H5::Group& group, AdimensionalSystemSolution& solution); static void read_loggamma( const H5::Group& group, AdimensionalSystemSolution& solution); static void read_gas_fugacities( const H5::Group& group, AdimensionalSystemSolution& solution); static void read_sorbed_molalities( const H5::Group& group, AdimensionalSystemSolution& solution); } // end namespace AdimensionalSystemSolutionHDF5Reader void save_adimensional_system_solution( HDF5File& file, const std::string& name, const std::string& section, const AdimensionalSystemSolution& solution ) { AdimensionalSystemSolutionHDF5Saver saver(solution); saver.save(file, name, section); } void read_adimensional_system_solution( const HDF5File& file, const std::string& name, const std::string& section, AdimensionalSystemSolution& solution ) { AdimensionalSystemSolutionHDF5Reader::read(file, name, section, solution); } void read_adimensional_system_solution( const H5::Group& group, AdimensionalSystemSolution& solution ) { AdimensionalSystemSolutionHDF5Reader::read(group, solution); } // Implementation // ============== // Save // ----- void AdimensionalSystemSolutionHDF5Saver::save( HDF5File& file, const std::string& name, const std::string& section ) { auto complete_sec = file.complete_name(name, section); auto adim_group = file.create_group(complete_sec); save_main_variables(file, complete_sec); save_molalities(file, complete_sec); save_loggamma(file, complete_sec); if (m_solution.gas_fugacities.rows() > 0) { save_gas_fugacities(file, complete_sec); } if (m_solution.sorbed_molalities.rows() > 0) { save_sorbed_molalities(file, complete_sec); } } void AdimensionalSystemSolutionHDF5Saver::save_main_variables( HDF5File& file, const std::string §ion ) { auto dataset = save_eigen_matrix(file, MAIN_VARIABLES_DS, section, m_solution.main_variables ); hsize_t dims[] = {2}; auto dspace = H5::DataSpace(1, dims); auto attribute = dataset->createAttribute(SCALAR_VARS_ATT, H5::PredType::NATIVE_DOUBLE, dspace); scalar_t attribute_values[] = {m_solution.inert_volume_fraction, m_solution.ionic_strength}; attribute.write(H5::PredType::NATIVE_DOUBLE, attribute_values); } void AdimensionalSystemSolutionHDF5Saver::save_molalities( HDF5File& file, const std::string §ion ) { save_eigen_matrix(file, SECONDARY_MOLALITIES_DS, section, m_solution.secondary_molalities ); } void AdimensionalSystemSolutionHDF5Saver::save_loggamma( HDF5File& file, const std::string §ion ) { save_eigen_matrix(file, LOG_GAMMA_DS, section, m_solution.log_gamma ); } void AdimensionalSystemSolutionHDF5Saver::save_gas_fugacities( HDF5File& file, const std::string §ion ) { save_eigen_matrix(file, GAS_FUGACITIES_DS, section, m_solution.gas_fugacities ); } void AdimensionalSystemSolutionHDF5Saver::save_sorbed_molalities( HDF5File& file, const std::string §ion ) { save_eigen_matrix(file, SORBED_MOLALITIES_DS, section, m_solution.sorbed_molalities ); } // read // ---- namespace AdimensionalSystemSolutionHDF5Reader { void read( const HDF5File& file, const std::string& name, const std::string& section, AdimensionalSystemSolution& solution ) { auto group = file.open_group(file.complete_name(name, section)); read(*group.get(), solution); } void read( const H5::Group& group, AdimensionalSystemSolution& solution ) { solution.is_valid = false; read_main_variables(group, solution); read_molalities(group, solution); read_loggamma(group, solution); H5E_auto_t func_p; void* client_data = nullptr; H5::Exception::getAutoPrint(func_p, &client_data); { // gas bool has_gas = true; H5::Exception::dontPrint(); try { group.openDataSet(GAS_FUGACITIES_DS); } catch (H5::GroupIException _) { has_gas = false; } H5::Exception::setAutoPrint(func_p, client_data); if (has_gas) { read_gas_fugacities(group, solution); } } { // sorbed bool has_sorbed = true; H5::Exception::dontPrint(); try { group.openDataSet(SORBED_MOLALITIES_DS); } catch (H5::GroupIException _) { has_sorbed = false; } H5::Exception::setAutoPrint(func_p, client_data); if (has_sorbed) { read_sorbed_molalities(group, solution); } } solution.is_valid = true; } void read_main_variables( const H5::Group& group, AdimensionalSystemSolution &solution ) { read_eigen_matrix(group, MAIN_VARIABLES_DS, solution.main_variables); auto dataset = group.openDataSet(MAIN_VARIABLES_DS); H5::Attribute attr = dataset.openAttribute(SCALAR_VARS_ATT); double attr_out[2]; attr.read(H5::PredType::NATIVE_DOUBLE, attr_out); solution.inert_volume_fraction = attr_out[0]; solution.ionic_strength = attr_out[1]; } void read_molalities( const H5::Group& group, AdimensionalSystemSolution &solution ) { read_eigen_matrix(group, SECONDARY_MOLALITIES_DS, solution.secondary_molalities); } void read_loggamma( const H5::Group& group, AdimensionalSystemSolution &solution ) { read_eigen_matrix(group, LOG_GAMMA_DS, solution.log_gamma); } void read_gas_fugacities( const H5::Group& group, AdimensionalSystemSolution &solution ) { read_eigen_matrix(group, GAS_FUGACITIES_DS, solution.gas_fugacities); } void read_sorbed_molalities( const H5::Group& group, AdimensionalSystemSolution &solution ) { read_eigen_matrix(group, SORBED_MOLALITIES_DS, solution.sorbed_molalities); } } // end namespace AdimensionalSystemSolutionHDF5Reader } //end namespace io } //end namespace specmicp diff --git a/src/specmicp/io/hdf5_adimensional.hpp b/src/specmicp/io/hdf5_adimensional.hpp index 72f05a6..3409858 100644 --- a/src/specmicp/io/hdf5_adimensional.hpp +++ b/src/specmicp/io/hdf5_adimensional.hpp @@ -1,80 +1,80 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_IO_HDF5_ADIMENSIONAL_HPP #define SPECMICP_IO_HDF5_ADIMENSIONAL_HPP -#include "../../macros.hpp" +#include "specmicp_common/macros.hpp" #include //! \file specmicp/io/hdf5_adimensional.hpp //! \brief Read and write AdimSolution to HDF5 format namespace H5 { class Group; } //end namespace H5 namespace specmicp { struct AdimensionalSystemSolution; namespace io { class HDF5File; //! \brief Save a solution to a HDF5 file void SPECMICP_DLL_PUBLIC save_adimensional_system_solution( HDF5File& file, const std::string& name, const std::string& section, const AdimensionalSystemSolution& solution ); //! \brief Read a solution from an HDF5 file void SPECMICP_DLL_PUBLIC read_adimensional_system_solution( const HDF5File& file, const std::string& name, const std::string& section, AdimensionalSystemSolution& solution ); //! \brief Read a solution from an HDF5 file void SPECMICP_DLL_PUBLIC read_adimensional_system_solution( const H5::Group& group, AdimensionalSystemSolution& solution ); } //end namespace io } //end namespace specmicp #endif // SPECMICP_IO_HDF5_ADIMENSIONAL_HPP diff --git a/src/specmicp/io/print.cpp b/src/specmicp/io/print.cpp index fa7d4c7..9b8ecc1 100644 --- a/src/specmicp/io/print.cpp +++ b/src/specmicp/io/print.cpp @@ -1,86 +1,86 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "print.hpp" -#include "../adimensional/adimensional_system_structs.hpp" +#include "specmicp/adimensional/adimensional_system_structs.hpp" #include namespace specmicp { namespace io { void print_specmicp_constraints( const RawDatabasePtr& the_database, const AdimensionalSystemConstraints& constraints ) { std::cout << "Constraints \n ---------- \n"; std::cout << "Total concentrations :\n"; for (auto comp: the_database->range_component()) std::cout << the_database->get_label_component(comp) << " - " << constraints.total_concentrations(comp) << std::endl; if (constraints.charge_keeper != no_species) { std::cout << "Charge keeper : " << the_database->get_label_component(constraints.charge_keeper) << std::endl; } if (constraints.fixed_molality_cs.size()) { std::cout << "Fixed molality :\n"; for (auto comp: constraints.fixed_molality_cs) { std::cout << " - " << the_database->get_label_component(comp.id_component) << ": " << comp.log_value << "\n"; } } if (constraints.fixed_activity_cs.size()) { std::cout << "Fixed activity :\n"; for (auto comp: constraints.fixed_activity_cs) { std::cout << " - " << the_database->get_label_component(comp.id_component) << " - " << comp.log_value << "\n"; } } if (constraints.fixed_fugacity_cs.size()) { std::cout << "Fixed fugacity :\n"; for (auto comp: constraints.fixed_fugacity_cs) { std::cout << the_database->get_label_component(comp.id_component) << " - " << comp.log_value << "\n"; } } std::cout << "Inert volume fraction : " << constraints.inert_volume_fraction << "\n"; std::cout << "-------------" << std::endl; } } //end namespace io } //end namespace specmicp diff --git a/src/specmicp/io/print.hpp b/src/specmicp/io/print.hpp index 8a90847..ecd7378 100644 --- a/src/specmicp/io/print.hpp +++ b/src/specmicp/io/print.hpp @@ -1,56 +1,56 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_IO_PRINT_HPP #define SPECMICP_SPECMICP_IO_PRINT_HPP -#include "../../database.hpp" +#include "specmicp_database/database.hpp" namespace specmicp { struct AdimensionalSystemConstraints; namespace io { //! \brief Print the constraints in a user-friendly fashion, for inspection void print_specmicp_constraints( const RawDatabasePtr& the_database, const AdimensionalSystemConstraints& constraints ); } //end namespace io } //end namespace specmicp #endif // SPECMICP_SPECMICP_IO_PRINT_HPP diff --git a/src/specmicp/problem_solver/CMakeLists.txt b/src/specmicp/problem_solver/CMakeLists.txt new file mode 100644 index 0000000..c7670ee --- /dev/null +++ b/src/specmicp/problem_solver/CMakeLists.txt @@ -0,0 +1,15 @@ +set( specmicp_problem_srcs + dissolver.cpp +) + +set( specmicp_problem_headers + dissolver.hpp + formulation.hpp +) + +add_to_specmicp_srcs_list(specmicp_problem_srcs) +add_to_specmicp_headers_list(specmicp_problem_headers) + +INSTALL(FILES ${specmicp_problem_headers} + DESTINATION ${INCLUDE_INSTALL_DIR}/specmicp/problem_solver +) diff --git a/src/specmicp/problem_solver/dissolver.cpp b/src/specmicp/problem_solver/dissolver.cpp index 2a2c23c..41f1dd6 100644 --- a/src/specmicp/problem_solver/dissolver.cpp +++ b/src/specmicp/problem_solver/dissolver.cpp @@ -1,203 +1,202 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 "dissolver.hpp" #include "formulation.hpp" -#include "../../database/database.hpp" +#include "specmicp_database/database.hpp" -#include namespace specmicp { Vector Dissolver::dissolve(const Formulation &the_problem) { return dissolve(the_problem, true); } Vector Dissolver::dissolve(const Formulation& the_problem, bool modify_db) { m_components_to_keep[0] = true; m_components_to_keep[1] = true; for (index_t component: m_database->range_aqueous_component()) { m_components_to_keep[component] = false; } m_total_concentration.setZero(); set_solvent(the_problem.mass_solution); for (auto it=the_problem.concentration_aqueous.begin(); it!=the_problem.concentration_aqueous.end(); ++it) { dissolve_aqueous(it->first, it->second, the_problem.mass_solution); } for (auto it=the_problem.amount_minerals.begin(); it!=the_problem.amount_minerals.end(); ++it) { dissolve_mineral(it->first, it->second); } if (modify_db) { keep_extra_components(the_problem.extra_components_to_keep); reduce_problem(); } // Remove minerals if needed if (the_problem.minerals_to_keep.size() > 0) { database::Database(m_database).minerals_keep_only(the_problem.minerals_to_keep); } return m_total_concentration; } void Dissolver::set_solvent(scalar_t amount) { index_t idw = m_database->water_index(); specmicp_assert(idw != no_species and idw == 0); m_total_concentration(0) += amount/m_database->molar_mass_basis(idw, mass_unit()); m_components_to_keep[0] = true; } void Dissolver::dissolve_aqueous(std::string label, scalar_t concentration, scalar_t mass_solution) { index_t idaq = m_database->get_id_component(label); // component if (idaq != no_species) { m_total_concentration(idaq) += mass_solution*concentration; m_components_to_keep[idaq] = true; } // aqueous else { index_t idsaq = m_database->get_id_aqueous(label); if (idsaq != no_species) { for (index_t component: m_database->range_component()) { if (m_database->nu_aqueous(idsaq, component) == 0.0) continue; m_total_concentration(component) += m_database->nu_aqueous(idsaq, component)*mass_solution*concentration; m_components_to_keep[component] = true; } } // compounds else { index_t idcomp = m_database->get_id_compound(label); if (idcomp == no_species) { throw std::invalid_argument("Unknown species : " + label + "."); } for (index_t component: m_database->range_component()) { if (m_database->nu_compound(idcomp, component) == 0.0) continue; m_total_concentration(component) += m_database->nu_compound(idcomp, component)*mass_solution*concentration; m_components_to_keep[component] = true; } } } } void Dissolver::dissolve_mineral(std::string label, scalar_t amount) { index_t id_mineral = m_database->get_id_mineral(label); if (id_mineral != no_species) { for (index_t component: m_database->range_component()) { if (m_database->nu_mineral(id_mineral, component) == 0.0) continue; m_total_concentration(component) += m_database->nu_mineral(id_mineral, component)*amount; m_components_to_keep[component] = true; } } else { index_t id_minkin = m_database->get_id_mineral_kinetic(label); if (id_minkin == no_species) { throw std::invalid_argument("Unknown mineral : " + label + "."); } for (index_t component: m_database->range_component()) { if (m_database->nu_mineral_kinetic(id_minkin, component) == 0.0) continue; m_total_concentration(component) += m_database->nu_mineral_kinetic(id_minkin, component)*amount; m_components_to_keep[component] = true; } } } void Dissolver::keep_extra_components(const std::vector& list_component_to_keep) { for (auto it:list_component_to_keep) { index_t idc = m_database->get_id_component(it); if (idc == no_species) { throw std::invalid_argument("Species '" + it + "' is not a component"); } m_components_to_keep[idc] = true; } } void Dissolver::reduce_problem() { std::vector to_remove; to_remove.reserve(m_database->nb_component()); index_t new_id = 0; Vector reduced_tot_conc(m_database->nb_component()); const index_t h_component = m_database->get_id_component_from_element("H"); m_components_to_keep[h_component] = true; // do not remove H[+]/HO[-] // Copy information for (index_t component: m_database->range_component()) { if (m_components_to_keep[component]) { reduced_tot_conc(new_id) = m_total_concentration(component); ++new_id; } else { to_remove.push_back(component); } } // Remove components from database database::Database(m_database).remove_components(to_remove); // Resize total concentrations reduced_tot_conc.conservativeResize(m_database->nb_component()); m_total_concentration = reduced_tot_conc; } } // end namespace specmicp diff --git a/src/specmicp/problem_solver/dissolver.hpp b/src/specmicp/problem_solver/dissolver.hpp index 104c916..e12bab0 100644 --- a/src/specmicp/problem_solver/dissolver.hpp +++ b/src/specmicp/problem_solver/dissolver.hpp @@ -1,84 +1,84 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_PROBLEMSOLVER_DISSOLVER_HPP #define SPECMICP_SPECMICP_PROBLEMSOLVER_DISSOLVER_HPP -#include "../../types.hpp" -#include "../../database.hpp" -#include "../../physics/units.hpp" +#include "specmicp_common/types.hpp" +#include "specmicp_common/physics/units.hpp" +#include "specmicp_database/database.hpp" namespace specmicp { struct Formulation; //! \brief Return the total concentration of a given formulation class SPECMICP_DLL_PUBLIC Dissolver: public units::UnitBaseClass { public: Dissolver(RawDatabasePtr the_database): m_database(the_database), m_total_concentration(Vector::Zero(the_database->nb_component())), m_components_to_keep(the_database->nb_component()) {} //! \brief Dissolve the problem into the components //! //! Also simplify the database Vector dissolve(const Formulation& the_problem); //! \brief Dissolve the sytem into components //! //! If modify db is true then the database is simplified Vector dissolve(const Formulation& the_problem, bool modify_db); //! \brief Return the total concentration vector Vector get_total_concentration() {return m_total_concentration;} private: void SPECMICP_DLL_LOCAL set_solvent(scalar_t amount); void SPECMICP_DLL_LOCAL dissolve_aqueous(std::string label, scalar_t concentration, scalar_t mass_solution); void SPECMICP_DLL_LOCAL dissolve_mineral(std::string label, scalar_t amount); void SPECMICP_DLL_LOCAL keep_extra_components(const std::vector& list_component_to_keep); void SPECMICP_DLL_LOCAL reduce_problem(); RawDatabasePtr m_database; Vector m_total_concentration; std::vector m_components_to_keep; }; } // end namespace specmicp #endif //SPECMICP_SPECMICP_PROBLEMSOLVER_DISSOLVER_HPP diff --git a/src/specmicp/problem_solver/formulation.hpp b/src/specmicp/problem_solver/formulation.hpp index 24a37cc..b0aedf7 100644 --- a/src/specmicp/problem_solver/formulation.hpp +++ b/src/specmicp/problem_solver/formulation.hpp @@ -1,114 +1,115 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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 SPECMICP_SPECMICP_PROBLEMSOLVER_FORMULATION_HPP #define SPECMICP_SPECMICP_PROBLEMSOLVER_FORMULATION_HPP -#include "../../types.hpp" +#include "specmicp_common/types.hpp" + #include #include #include namespace specmicp { struct Formulation { //! \brief Mass of the solvent scalar_t mass_solution; //! \brief Contains the amount of each solid phase std::map concentration_aqueous; //! \brief Contains the amount of each solid phase //! included in the problem std::map amount_minerals; //! \brief Extra component to leave in the database //! //! All components not present in this list and with //! a total concentration of zero will be removed. std::vector extra_components_to_keep; //! \brief List of solid phase at equilibrium to keep in the computation //! //! Leave empty to keep all solid phases present in the database std::vector minerals_to_keep; //! \brief Set the mass of the solution void set_mass_solution(scalar_t mass) { mass_solution = mass; } //! \brief Add an aqueous species to the system //! //! \param label label of a component or a secondary species or a compounds //! \param concentration in moles per mass unit of water void add_aqueous_species(const std::string& label, scalar_t concentration) { concentration_aqueous.insert(std::pair(label, concentration)); } //! \brief Add an aqueous species to the system //! //! \param label label of a component or a secondary species //! \param concentration in moles per mass unit of water void add_aqueous_species(std::string&& label, scalar_t concentration) { concentration_aqueous.emplace(std::move(label), concentration); } //! \brief Add a mineral to the system //! //! \param label label of a mineral at equilibrium or governed by kinetics //! \param concentration in moles per volume unit void add_mineral(const std::string& label, scalar_t concentration) { amount_minerals.insert(std::pair(label, concentration)); } //! \brief Add a mineral to the system //! //! \param label label of a mineral at equilibrium or governed by kinetics //! \param concentration in moles per volume unit void add_mineral(std::string&& label, scalar_t concentration) { amount_minerals.emplace(std::move(label), concentration); } //! \brief Keep a component in the database even if it doesn't exist in the initial composition void keep_component(const std::string& label) { extra_components_to_keep.push_back(label); } //! \brief Set the list of minerals at equilibrium void set_minerals_list(const std::vector& list_minerals_equilibrium) { minerals_to_keep = std::vector(list_minerals_equilibrium); } //! \brief Set the list of minerals at equilibrium void set_minerals_list(std::vector&& list_minerals_equilibrium) { minerals_to_keep = std::vector(std::move(list_minerals_equilibrium)); } }; } // end namespace specmicp #endif // SPECMICP_SPECMICP_PROBLEMSOLVER_FORMULATION_HPP diff --git a/src/specmicp.hpp b/src/specmicp/specmicp.hpp similarity index 95% rename from src/specmicp.hpp rename to src/specmicp/specmicp.hpp index 8cfa251..5606cfc 100644 --- a/src/specmicp.hpp +++ b/src/specmicp/specmicp.hpp @@ -1,55 +1,55 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. 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 following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDER 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. * ============================================================================= */ //! \file specmicp.hpp //! \brief Include this file to use SpecMiCP API //! //! This file include the main headers from the SpecMiCP solver -#include "types.hpp" +#include "specmicp_common/types.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include "specmicp/adimensional/adimensional_system_solution_saver.hpp" #include "specmicp/adimensional/equilibrium_curve.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/io/configuration.hpp" -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" diff --git a/tests/specmicp/adim/adimensional_system.cpp b/tests/specmicp/adim/adimensional_system.cpp index e224ebd..9a87f47 100644 --- a/tests/specmicp/adim/adimensional_system.cpp +++ b/tests/specmicp/adim/adimensional_system.cpp @@ -1,258 +1,258 @@ #include "catch.hpp" #include "specmicp/adimensional/adimensional_system.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" #include specmicp::RawDatabasePtr get_test_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); return thedatabase.get_database(); } specmicp::RawDatabasePtr get_test_oxydoreduc_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); return thedatabase.get_database(); } using namespace specmicp; TEST_CASE("Adimensional system", "[specmicp][MiCP program][adimensional]") { RawDatabasePtr thedatabase = get_test_database(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); SECTION("initialization") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; AdimensionalSystemConstraints constraints(total_concentration); AdimensionalSystem system(thedatabase, constraints); REQUIRE(system.ideq_w() != no_equation); REQUIRE(system.ideq_paq(id_oh) != no_equation); REQUIRE(system.ideq_paq(id_ca) != no_equation); REQUIRE(system.ideq_min(id_ch) != no_equation); Vector x = Vector::Zero(system.total_dofs()); x(id_h2o) = 1.0; x(id_oh) = std::log10(2e-3); x(id_ca) = -3; system.set_secondary_concentration(x); scalar_t molality_h = system.secondary_molality(0); REQUIRE(std::isfinite(molality_h)); REQUIRE(molality_h == Approx(5e-12)); system.compute_log_gamma(x); molality_h = system.secondary_molality(0); REQUIRE(std::isfinite(molality_h)); system.hook_start_iteration(x, 1.0); molality_h = system.secondary_molality(0); REQUIRE(std::isfinite(molality_h)); system.hook_start_iteration(x, 1.0); REQUIRE(molality_h == Approx(system.secondary_molality(0))); } SECTION("Residuals") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::AdimensionalSystem system(thedatabase, constraints); Vector x = Vector::Zero(system.total_dofs()); x(system.ideq_w()) = 1.0; x(system.ideq_paq(id_oh)) = std::log10(2e-3); x(system.ideq_paq(id_ca)) = -3; specmicp::scalar_t res = system.residual_water_conservation(x); REQUIRE(std::isfinite(res)); REQUIRE(res == Approx((55.5 - system.density_water()/thedatabase->molar_mass_basis(id_h2o))/55.5)); } SECTION("Equation numbering") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; specmicp::AdimensionalSystemConstraints constraints(total_concentration); constraints.disable_conservation_water(); constraints.enable_surface_model(1.23456); constraints.total_concentrations(2) = 0; specmicp::AdimensionalSystem system(thedatabase, constraints); CHECK(system.water_equation_type() == specmicp::WaterEquationType::NoEquation); CHECK(system.surface_model() == specmicp::SurfaceEquationType::Equilibrium); CHECK(system.surface_total_concentration() == 1.23456); CHECK((int) system.aqueous_component_equation_type(2) == (int) specmicp::AqueousComponentEquationType::NoEquation); constraints = specmicp::AdimensionalSystemConstraints(total_concentration); constraints.enable_conservation_water(); constraints.disable_surface_model(); constraints.add_fixed_activity_component(2, -2.0); system = specmicp::AdimensionalSystem(thedatabase, constraints); CHECK(system.water_equation_type() == specmicp::WaterEquationType::MassConservation); CHECK(system.surface_model() == specmicp::SurfaceEquationType::NoEquation); CHECK(system.aqueous_component_equation_type(2) == specmicp::AqueousComponentEquationType::FixedActivity); CHECK(system.fixed_activity_bc(2) == -2.0); } SECTION("Jacobian") { // add a sorbed species, just to see if it works thedatabase->sorbed.resize(1); specmicp::database::SorbedValues values; values.label = "SorbedSpecies"; values.logk = -1; values.sorption_site_occupied = 1; thedatabase->sorbed.set_values(0, std::move(values)); specmicp::Matrix numatrix(1, thedatabase->nb_component()); numatrix << 0, 0, 2, 1; thedatabase->sorbed.set_nu_matrix(numatrix); thedatabase->sorbed.set_valid(); Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; specmicp::AdimensionalSystemConstraints constraints(total_concentration); constraints.enable_conservation_water(); constraints.enable_surface_model(1.23456); specmicp::AdimensionalSystem system (thedatabase, constraints); Vector x = Vector::Zero(system.total_variables()); x(system.ideq_w()) = 1.0; x(system.ideq_paq(id_oh)) = std::log10(2e-3); x(system.ideq_paq(id_ca)) = -3; specmicp::Vector residual; system.get_residuals(x, residual); std::cout << residual << std::endl; specmicp::Matrix analytical_jacobian; system.analytical_jacobian(x, analytical_jacobian); specmicp::Matrix fd_jacobian; system.finite_difference_jacobian(x, fd_jacobian); std::cout << analytical_jacobian - fd_jacobian << std::endl; REQUIRE((analytical_jacobian - fd_jacobian).isMuchSmallerThan(1.0, 1e-3)); } } TEST_CASE("Adimensional system - oxydoreduc", "[specmicp][MiCP program][adimensional]") { RawDatabasePtr thedatabase = get_test_oxydoreduc_database(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); SECTION("Jacobian - oxydoreduc") { // add a sorbed species, just to see if it works thedatabase->sorbed.resize(1); specmicp::database::SorbedValues values; values.label = "SorbedSpecies"; values.logk = -1; values.sorption_site_occupied = 1; thedatabase->sorbed.set_values(0, std::move(values)); specmicp::Matrix numatrix(1, thedatabase->nb_component()); numatrix << 0, 0, 2, 1; thedatabase->sorbed.set_nu_matrix(numatrix); thedatabase->sorbed.set_valid(); Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; specmicp::AdimensionalSystemConstraints constraints(total_concentration); constraints.enable_conservation_water(); constraints.enable_surface_model(1.23456); specmicp::AdimensionalSystem system (thedatabase, constraints); Vector x = Vector::Zero(system.total_variables()); x(system.ideq_w()) = 1.0; x(system.ideq_paq(id_oh)) = std::log10(2e-3); x(system.ideq_paq(id_ca)) = -3.0; x(system.ideq_electron()) = -2.0; for (index_t j: thedatabase->range_aqueous()){ std::cout << "\t" << thedatabase->get_label_aqueous(j); } std::cout << "\n"; std::cout << thedatabase->aqueous.get_nu_matrix() << std::endl; specmicp::Vector residual; system.get_residuals(x, residual); std::cout << residual << std::endl; specmicp::Matrix analytical_jacobian; system.analytical_jacobian(x, analytical_jacobian); specmicp::Matrix fd_jacobian; system.finite_difference_jacobian(x, fd_jacobian); std::cout << analytical_jacobian - fd_jacobian << std::endl; REQUIRE((analytical_jacobian - fd_jacobian).isMuchSmallerThan(1.0, 1e-3)); } } diff --git a/tests/specmicp/adim/adimensional_system_carboalu.cpp b/tests/specmicp/adim/adimensional_system_carboalu.cpp index da379be..afcac5e 100644 --- a/tests/specmicp/adim/adimensional_system_carboalu.cpp +++ b/tests/specmicp/adim/adimensional_system_carboalu.cpp @@ -1,157 +1,159 @@ -#include #include "catch.hpp" -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" -#include "utils/timer.hpp" +#include "specmicp_common/timer.hpp" + +#include "specmicp_database/database.hpp" + +#include -#include "database/database.hpp" TEST_CASE("Carboalu - using adimensional system ", "[Adimensional, Carbonation, carboalu]") { std::cerr.flush(); specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Warning; SECTION("Carboalu") { std::cout << "---------------------\n Carboalu\n ---------------------" << std::endl; specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"}, {"Al[3+]","Al(OH)4[-]"}, }); thedatabase.swap_components(swapping); //thedatabase.remove_half_cell_reactions(); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); thedatabase.remove_half_cell_reactions({"H2O", "HO[-]", "HCO3[-]"}); specmicp::Formulation formulation; specmicp::scalar_t mult = 5e3; specmicp::scalar_t m_c3s = mult*0.6; specmicp::scalar_t m_c2s = mult*0.2; specmicp::scalar_t m_c3a = mult*0.10; specmicp::scalar_t m_gypsum = mult*0.10; specmicp::scalar_t wc = 0.8; specmicp::scalar_t m_water = wc*1e-3*( m_c3s*(3*56.08+60.08) + m_c2s*(2*56.06+60.08) + m_c3a*(3*56.08+101.96) + m_gypsum*(56.08+80.06+2*18.02) ); formulation.mass_solution = m_water; formulation.amount_minerals = { {"C3S", m_c3s}, {"C2S", m_c2s}, {"C3A", m_c3a}, {"Gypsum", m_gypsum} }; formulation.extra_components_to_keep = {"HCO3[-]", }; formulation.minerals_to_keep = { "Portlandite", "CSH,jennite", "CSH,tobermorite", "SiO2(am)", "Calcite", "Al(OH)3(mic)", "Monosulfoaluminate", "Tricarboaluminate", "Monocarboaluminate", "Hemicarboaluminate", "Straetlingite", "Gypsum", "Ettringite", "Thaumasite" }; thedatabase.remove_gas_phases(); std::string co2_gas = R"plop( [ { "label": "CO2(g)", "composition": "CO2", "log_k": -1.468 } ] )plop"; thedatabase.add_gas_phases(co2_gas); specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); specmicp::index_t id_h2o = raw_data->get_id_component("H2O"); specmicp::index_t id_ho = raw_data->get_id_component("HO[-]"); specmicp::index_t id_co2 = raw_data->get_id_component("HCO3[-]"); specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = 1; specmicp::AdimensionalSystemSolverOptions options; options.solver_options.maxstep = 20.0; options.solver_options.max_iter = 100; options.solver_options.maxiter_maxstep = 100; options.solver_options.use_crashing = false; options.solver_options.use_scaling = true; options.solver_options.factor_descent_condition = -1; options.solver_options.factor_gradient_search_direction = 100; options.solver_options.projection_min_variable = 1e-9; options.solver_options.fvectol = 1e-6; options.solver_options.steptol = 1e-10; options.system_options.non_ideality_tolerance = 1e-12; specmicp::Vector x; specmicp::scalar_t dh2co3 = mult*0.01; specmicp::uindex_t tot_iter = 0; specmicp::Timer timer_carboalu; timer_carboalu.start(); specmicp::AdimensionalSystemSolver solver(raw_data, constraints, options); solver.initialise_variables(x, 0.5, -3.0); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x, false); REQUIRE((int) perf.return_code > (int) specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet); std::cout << "First point number of iterations : " << perf.nb_iterations << " (Ref: 38)"<< std::endl; tot_iter += perf.nb_iterations; specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); for (int k=0; k<250; ++k) { constraints.total_concentrations(id_h2o) += dh2co3; constraints.total_concentrations(id_ho) -= dh2co3; constraints.total_concentrations(id_co2) += dh2co3; solver = specmicp::AdimensionalSystemSolver (raw_data, constraints, solution, options); //std::cout << solver.get_options().solver_options.factor_descent_condition << std::endl; specmicp::micpsolver::MiCPPerformance perf = solver.solve(x, false); //std::cout << x << std::endl; REQUIRE((int) perf.return_code > (int) specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet); //std::cout << perf.nb_iterations << std::endl; tot_iter += perf.nb_iterations; solution = solver.get_raw_solution(x); //specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); //std::cout << solution.main_variables(0) << std::endl; //std::cout << 14+solution.main_variables(1) << std::endl; } std::cout << "Number of iterations : " << tot_iter << " (Ref: 443)"<< std::endl; timer_carboalu.stop(); std::cout << "Elapsed time : " << timer_carboalu.elapsed_time() << "s (Ref: 0.022s)" << std::endl; } } diff --git a/tests/specmicp/adim/adimensional_system_conditions.cpp b/tests/specmicp/adim/adimensional_system_conditions.cpp index ff70d9b..ba2596a 100644 --- a/tests/specmicp/adim/adimensional_system_conditions.cpp +++ b/tests/specmicp/adim/adimensional_system_conditions.cpp @@ -1,375 +1,376 @@ -#include #include "catch.hpp" -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" + +#include TEST_CASE("Fixed activity", "[specmicp, MiCP, program, adimensional, solver, conditions]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Error; SECTION("Thermocarbo - saturated ") { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"} }); thedatabase.swap_components(swapping); thedatabase.remove_gas_phases(); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); thedatabase.remove_half_cell_reactions({"H2O", "HO[-]", "HCO3[-]"}); specmicp::Formulation formulation; specmicp::scalar_t mult = 1.0; formulation.mass_solution = mult*0.156; formulation.amount_minerals = {{"C3S", mult*0.7}, {"C2S", mult*0.3}}; formulation.extra_components_to_keep = {"HCO3[-]", }; specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = 1; constraints.water_equation = specmicp::WaterEquationType::SaturatedSystem; specmicp::index_t id_h2o = raw_data->water_index(); specmicp::index_t id_ho = raw_data->get_id_component("HO[-]"); specmicp::index_t id_co2 = raw_data->get_id_component("HCO3[-]"); specmicp::AdimensionalSystemSolverOptions options; options.solver_options.maxstep = 20.0; options.solver_options.maxiter_maxstep = 100; options.solver_options.use_crashing = false; options.solver_options.use_scaling = true; options.solver_options.factor_descent_condition = -1; options.solver_options.factor_gradient_search_direction = 200; options.solver_options.condition_limit = -1; options.solver_options.set_tolerance(1e-10,1e-12); options.system_options.cutoff_total_concentration = 1e-16; specmicp::AdimensionalSystemSolver solver(raw_data, constraints, options); specmicp::Vector x; solver.initialise_variables(x, 0.8, 2); specmicp::scalar_t dh2co3 = 0.1; for (int k=0; k<40; ++k) { specmicp::AdimensionalSystemSolver solver(raw_data, constraints, options); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x); REQUIRE(perf.return_code >= specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, raw_data, options.units_set); //std::cout << solution.main_variables(0) << std::endl; std::cout << sol.pH() << std::endl; constraints.total_concentrations(id_h2o) += mult*dh2co3; constraints.total_concentrations(id_ho) -= mult*dh2co3; constraints.total_concentrations(id_co2) += mult*dh2co3; } } SECTION("Carbonate speciation - fixed activity") { specmicp::database::Database dbhandler(TEST_CEMDATA_PATH); std::vector to_keep = {"H2O", "H[+]", "HCO3[-]", "Na[+]"}; dbhandler.remove_gas_phases(); std::string co2_gas = R"plop( [ { "label": "CO2(g)", "composition": "CO2", "log_k": -1.468 } ] )plop"; dbhandler.remove_half_cell_reactions({"H2O", "H[+]", "HCO3[-]"}); dbhandler.add_gas_phases(co2_gas); dbhandler.keep_only_components(to_keep); specmicp::RawDatabasePtr thedatabase = dbhandler.get_database(); specmicp::index_t id_h = dbhandler.component_label_to_id("H[+]"); specmicp::index_t id_na = dbhandler.component_label_to_id("Na[+]"); specmicp::index_t id_hco3 = dbhandler.component_label_to_id("HCO3[-]"); specmicp::index_t id_co2 = dbhandler.aqueous_label_to_id("CO2"); specmicp::index_t id_co3 = dbhandler.aqueous_label_to_id("CO3[2-]"); specmicp::index_t id_naco3 = dbhandler.aqueous_label_to_id("NaCO3[-]"); specmicp::index_t id_nahco3 = dbhandler.aqueous_label_to_id("NaHCO3"); specmicp::index_t id_co2g = dbhandler.gas_label_to_id("CO2(g)"); specmicp::Vector total_concentrations(thedatabase->nb_component()); total_concentrations(0) = 55; total_concentrations(id_hco3) = 0.1; specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = id_na; constraints.enable_conservation_water(); specmicp::AdimensionalSystemSolverOptions options; options.system_options.non_ideality = true; options.units_set.length = specmicp::units::LengthUnit::decimeter; options.solver_options.max_iter = 50; options.solver_options.maxstep = 10; options.solver_options.maxiter_maxstep = 100; options.solver_options.use_crashing = false; options.solver_options.use_scaling = false; options.solver_options.non_monotone_linesearch = false; options.solver_options.penalization_factor = 0.8; options.solver_options.factor_descent_condition = -1; options.solver_options.factor_gradient_search_direction = 10; options.solver_options.fvectol = 1e-6; specmicp::Vector x; constraints.add_fixed_activity_component(id_h, -4.0); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints, options); solver.initialise_variables(x, 0.8, -2); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); std::cout << 4.0 << " \t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << " \t" << sol.molality_aqueous(id_naco3) << " \t" << sol.molality_aqueous(id_nahco3) << "\t" << sol.fugacity_gas(id_co2g) << std::endl; for (double ph=4.5; ph<13; ph+=0.5) { constraints.fixed_activity_cs[0].log_value = -ph; solver = specmicp::AdimensionalSystemSolver( thedatabase, constraints, options); perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); std::cout << ph << "\t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << "\t" << sol.molality_aqueous(id_naco3) << "\t" << sol.molality_aqueous(id_nahco3) << "\t" << sol.fugacity_gas(id_co2g) << std::endl; } //std::cout << x << std::endl; } SECTION("Carbonate speciation - fixed molality") { specmicp::database::Database dbhandler(TEST_CEMDATA_PATH); std::vector to_keep = {"H2O", "H[+]", "HCO3[-]", "Na[+]", "Cl[-]"}; dbhandler.keep_only_components(to_keep); dbhandler.remove_gas_phases(); dbhandler.remove_half_cell_reactions({"H2O", "H[+]", "HCO3[-]"}); std::string co2_gas = R"plop( [ { "label": "CO2(g)", "composition": "CO2", "log_k": -1.468 } ] )plop"; dbhandler.add_gas_phases(co2_gas); specmicp::RawDatabasePtr thedatabase = dbhandler.get_database(); specmicp::index_t id_h = dbhandler.component_label_to_id("H[+]"); specmicp::index_t id_na = dbhandler.component_label_to_id("Na[+]"); specmicp::index_t id_cl = dbhandler.component_label_to_id("Cl[-]"); specmicp::index_t id_hco3 = dbhandler.component_label_to_id("HCO3[-]"); specmicp::index_t id_co2 = dbhandler.aqueous_label_to_id("CO2"); specmicp::index_t id_co3 = dbhandler.aqueous_label_to_id("CO3[2-]"); specmicp::index_t id_naco3 = dbhandler.aqueous_label_to_id("NaCO3[-]"); specmicp::index_t id_nahco3 = dbhandler.aqueous_label_to_id("NaHCO3"); specmicp::index_t id_co2g = dbhandler.gas_label_to_id("CO2(g)"); specmicp::Vector total_concentrations(thedatabase->nb_component()); total_concentrations(0) = 55; total_concentrations(id_hco3) = 0.1; specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.enable_conservation_water(); specmicp::AdimensionalSystemSolverOptions options; options.system_options.non_ideality = true; options.units_set.length = specmicp::units::LengthUnit::decimeter; options.solver_options.max_iter = 50; options.solver_options.maxstep = 10; options.solver_options.maxiter_maxstep = 100; options.solver_options.use_crashing = false; options.solver_options.use_scaling = true; options.solver_options.non_monotone_linesearch = true; options.solver_options.penalization_factor = 0.8; options.solver_options.factor_descent_condition = -1; options.solver_options.factor_gradient_search_direction = 10; options.solver_options.fvectol = 1e-6; specmicp::Vector x; constraints.add_fixed_activity_component(id_h, -4.0); constraints.add_fixed_molality_component(id_na, -0.301); constraints.add_fixed_molality_component(id_cl, -0.301); //constraints.set_charge_keeper(id_h); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints, options); solver.initialise_variables(x, 0.8, -2); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); REQUIRE(sol.log_molality_component(id_na) == Approx(-0.301)); REQUIRE(sol.log_molality_component(id_cl) == Approx(-0.301)); std::cout << 4.0 << " \t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << " \t" << sol.molality_aqueous(id_naco3) << " \t" << sol.molality_aqueous(id_nahco3) << "\t" << sol.fugacity_gas(id_co2g) << std::endl; for (double ph=4.5; ph<13; ph+=0.5) { constraints.fixed_activity_cs[0].log_value = -ph; solver = specmicp::AdimensionalSystemSolver( thedatabase, constraints, options); perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); std::cout << ph << "\t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << "\t" << sol.molality_aqueous(id_naco3) << "\t" << sol.molality_aqueous(id_nahco3) << "\t" << sol.fugacity_gas(id_co2g) << std::endl; } //std::cout << x << std::endl; } SECTION("Carbonate speciation - fixed fugacity") { specmicp::database::Database dbhandler(TEST_CEMDATA_PATH); std::vector to_keep = {"H2O", "H[+]", "HCO3[-]"}; dbhandler.keep_only_components(to_keep); dbhandler.remove_gas_phases(); dbhandler.remove_half_cell_reactions({"H2O", "H[+]", "HCO3[-]"}); std::string co2_gas = R"plop( [ { "label": "CO2(g)", "composition": "CO2", "log_k": -1.468 } ] )plop"; dbhandler.add_gas_phases(co2_gas); specmicp::RawDatabasePtr thedatabase = dbhandler.get_database(); specmicp::index_t id_h = thedatabase->get_id_component("H[+]"); specmicp::index_t id_hco3 = thedatabase->get_id_component("HCO3[-]"); specmicp::index_t id_co2 = thedatabase->get_id_aqueous("CO2"); specmicp::index_t id_co3 = thedatabase->get_id_aqueous("CO3[2-]");; specmicp::index_t id_co2g = thedatabase->get_id_gas("CO2(g)"); CHECK(id_co2g == 0); specmicp::Vector total_concentrations(thedatabase->nb_component()); total_concentrations(0) = 55; total_concentrations(id_hco3) = 0.1; specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = id_h; constraints.enable_conservation_water(); specmicp::AdimensionalSystemSolverOptions options; options.system_options.non_ideality = true; options.units_set.length = specmicp::units::LengthUnit::decimeter; options.solver_options.max_iter = 50; options.solver_options.maxstep = 20; options.solver_options.maxiter_maxstep = 10; options.solver_options.use_crashing = false; options.solver_options.use_scaling = true; options.solver_options.enable_non_monotone_linesearch(); options.solver_options.penalization_factor = 0.8; options.solver_options.factor_descent_condition = -1; options.solver_options.fvectol = 1e-8; options.solver_options.condition_limit = -1; specmicp::Vector x; constraints.add_fixed_fugacity_gas(id_co2g, id_hco3, -5); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints, options); solver.initialise_variables(x, 0.8, -2.0); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); std::cout << -5 << " \t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << " \t" << sol.fugacity_gas(id_co2g) << std::endl; for (double lfug=-4.5; lfug<=-0.5; lfug+=0.25) { constraints.fixed_fugacity_cs[0].log_value = lfug; solver = specmicp::AdimensionalSystemSolver( thedatabase, constraints, options); perf = solver.solve(x); REQUIRE((int) perf.return_code > 0 ); solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor sol(solution, thedatabase, options.units_set); std::cout << lfug << "\t" << sol.pH() << " \t" << sol.molality_component(id_hco3) << " \t" << sol.molality_aqueous(id_co2) << " \t" << sol.molality_aqueous(id_co3) << "\t" << sol.fugacity_gas(id_co2g) << std::endl; } //std::cout << x << std::endl; } } diff --git a/tests/specmicp/adim/adimensional_system_configuration.cpp b/tests/specmicp/adim/adimensional_system_configuration.cpp index 8733b32..31fac05 100644 --- a/tests/specmicp/adim/adimensional_system_configuration.cpp +++ b/tests/specmicp/adim/adimensional_system_configuration.cpp @@ -1,54 +1,54 @@ #include "catch.hpp" -#include "utils/io/yaml.hpp" +#include "specmicp_common/io/yaml.hpp" #include -#include "database.hpp" +#include "specmicp_database/database.hpp" -#include "database/io/configuration.hpp" +#include "specmicp_database/io/configuration.hpp" #include "specmicp/io/configuration.hpp" #include "specmicp/adimensional/adimensional_system_structs.hpp" #include "specmicp/adimensional/adimensional_system_solver_structs.hpp" #include "specmicp/problem_solver/formulation.hpp" TEST_CASE("Configuration", "[SpecMiCP],[io],[configuration]") { YAML::Node conf = specmicp::io::parse_yaml_file("specmicp_conf_test.yaml"); specmicp::RawDatabasePtr raw_db = specmicp::io::configure_database(conf); specmicp::AdimensionalSystemSolverOptions opts; specmicp::io::configure_specmicp_options(opts, conf); SECTION("Formulation") { specmicp::Formulation formulation; specmicp::io::check_mandatory_yaml_node(conf, "speciation", "__main__"); specmicp::io::check_mandatory_yaml_node(conf["speciation"][0], "formulation", "speciation 0"); specmicp::io::configure_specmicp_formulation(formulation, conf["speciation"][0]["formulation"], raw_db, opts.units_set); CHECK(formulation.mass_solution == 0.5); CHECK(formulation.concentration_aqueous["H2CO3"] == 0.01); CHECK(formulation.concentration_aqueous["NaOH"] == Approx(0.1/(22.99+16.00+1.008)/0.5)); CHECK(formulation.amount_minerals["C3S"] == 5.0); CHECK(formulation.amount_minerals["C3A"] == 0.1); } SECTION("Constraints") { specmicp::AdimensionalSystemConstraints constraints; specmicp::io::check_mandatory_yaml_node(conf, "speciation", "__main__"); specmicp::io::check_mandatory_yaml_node(conf["speciation"][0], "constraints", "speciation 0"); specmicp::io::configure_specmicp_constraints(constraints, conf["speciation"][0]["constraints"], raw_db); CHECK(constraints.charge_keeper == raw_db->get_id_component("HO[-]")); REQUIRE(constraints.fixed_molality_cs.size() == 1); CHECK(constraints.fixed_molality_cs[0].id_component == raw_db->get_id_component("Cl[-]")); CHECK(constraints.fixed_molality_cs[0].log_value == std::log10(0.2)); CHECK(constraints.water_equation == specmicp::WaterEquationType::SaturatedSystem); } } diff --git a/tests/specmicp/adim/adimensional_system_oxydoreduc.cpp b/tests/specmicp/adim/adimensional_system_oxydoreduc.cpp index c2b0ae0..a4bbff1 100644 --- a/tests/specmicp/adim/adimensional_system_oxydoreduc.cpp +++ b/tests/specmicp/adim/adimensional_system_oxydoreduc.cpp @@ -1,80 +1,80 @@ #include "catch.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" specmicp::RawDatabasePtr get_test_s_oxydoreduc_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); return thedatabase.get_database(); } using namespace specmicp; TEST_CASE("Oxydoreduction solver", "[Adimensional][OxydoReduc],[Solver]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Error; specmicp::RawDatabasePtr thedatabase = get_test_s_oxydoreduc_database(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); SECTION("Automatic solver") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 0.03; total_concentration(id_oh) = 0.02; total_concentration(id_ca) = 0.01; specmicp::Vector x; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints); solver.initialise_variables(x, 0.8, -2.0); //x(solver.dof_surface()) = -HUGE_VAL; solver.get_options().units_set.length = specmicp::units::LengthUnit::centimeter; solver.get_options().solver_options.maxstep = 10.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.disable_descent_direction(); solver.get_options().solver_options.factor_gradient_search_direction = 100; solver.get_options().solver_options.set_tolerance(1e-8, 1e-14); solver.get_options().system_options.scaling_electron = 1e-8; solver.solve(x); AdimensionalSystemSolution solution = solver.get_raw_solution(x); AdimensionalSystemSolutionExtractor extr(solution, thedatabase, solver.get_options().units_set); CHECK(extr.volume_fraction_water() == Approx(0.542049).epsilon(1e-3)); CHECK(extr.log_molality_component(id_oh) == Approx(-1.46214).epsilon(1e-3)); CHECK(extr.log_molality_component(id_ca) == Approx(-1.82093).epsilon(1e-3)); //CHECK(extr.free_surface_concentration() == -HUGE_VAL); CHECK(extr.volume_fraction_mineral(id_ch) == Approx(0.32966).epsilon(1e-3)); CHECK(extr.pE() == Approx(1.32169).epsilon(1e-3)); CHECK(extr.Eh() == Approx(0.0775594).epsilon(1e-3)); } } diff --git a/tests/specmicp/adim/adimensional_system_pcfm.cpp b/tests/specmicp/adim/adimensional_system_pcfm.cpp index c0d5e2a..238ca59 100644 --- a/tests/specmicp/adim/adimensional_system_pcfm.cpp +++ b/tests/specmicp/adim/adimensional_system_pcfm.cpp @@ -1,61 +1,61 @@ #include "catch.hpp" #include "specmicp/adimensional/adimensional_system.hpp" #include "specmicp/adimensional/adimensional_system_pcfm.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" specmicp::RawDatabasePtr get_pcfm_test_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"H2O", "HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); thedatabase.remove_solid_phases(); return thedatabase.get_database(); } // Test the Positive continuous fraction method TEST_CASE("Positive continuous fraction method", "[specmicp, MiCP, program, adimensional, PCFM]") { specmicp::RawDatabasePtr the_database = get_pcfm_test_database(); auto id_h2o = the_database->water_index(); auto id_oh = the_database->get_id_component("HO[-]"); auto id_ca = the_database->get_id_component("Ca[2+]"); SECTION("PCFM") { specmicp::Vector total_concentration = specmicp::Vector::Zero(the_database->nb_component()); total_concentration(id_h2o) = 55.5; total_concentration(id_oh) = 2e-3; total_concentration(id_ca) = 1e-3; specmicp::AdimensionalSystemConstraints constraints(total_concentration); constraints.disable_conservation_water(); constraints.enable_surface_model(1.23456); //constraints.total_concentrations(2) = 0; std::shared_ptr ptrsystem = std::make_shared(the_database, constraints); specmicp::AdimensionalSystemSolver solver(the_database, constraints); specmicp::AdimensionalSystemPCFM pcfm_solver(ptrsystem); specmicp::Vector x; solver.initialise_variables(x, 1.0, -3.0, 0.0); specmicp::PCFMReturnCode retcode = pcfm_solver.solve(x); REQUIRE(retcode == specmicp::PCFMReturnCode::Success); } } diff --git a/tests/specmicp/adim/adimensional_system_problem_solver.cpp b/tests/specmicp/adim/adimensional_system_problem_solver.cpp index e1aa9ab..fac4b2f 100644 --- a/tests/specmicp/adim/adimensional_system_problem_solver.cpp +++ b/tests/specmicp/adim/adimensional_system_problem_solver.cpp @@ -1,107 +1,108 @@ #include "catch.hpp" -#include -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" + +#include TEST_CASE("Solving adimensinal system with problem setting", "[specmicp, MiCP, program, adimensional, solver]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Warning; SECTION("Solving problem with dissolver - simpler problem") { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"} }); thedatabase.swap_components(swapping); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); specmicp::Formulation formulation; formulation.mass_solution = 0.8; formulation.amount_minerals = {{"Portlandite", 10.0}}; specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); total_concentrations *= 1e3; specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = raw_data->get_id_component("HO[-]"); specmicp::AdimensionalSystemSolver solver(raw_data, constraints); solver.get_options().solver_options.maxstep = 50.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.factor_descent_condition = 1e-10; solver.get_options().solver_options.factor_gradient_search_direction = 200; specmicp::Vector x; solver.initialise_variables(x, 0.8, -2); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x); REQUIRE(perf.return_code >= specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor extr(solution, raw_data, solver.get_options().units_set); CHECK(extr.volume_fraction_water() == Approx(0.802367).epsilon(1e-4)); CHECK(extr.volume_fraction_mineral(raw_data->get_id_mineral("Portlandite")) == Approx(0.32947).epsilon(1e-2)); } SECTION("Solving problem with dissolver") { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"} }); thedatabase.swap_components(swapping); thedatabase.remove_gas_phases(); thedatabase.remove_half_cell_reactions({"H2O", "HO[-]", "HCO3[-]"}); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); specmicp::Formulation formulation; formulation.mass_solution = 0.8; formulation.amount_minerals = {{"C3S", 0.7}, {"C2S", 0.3}}; formulation.extra_components_to_keep = {"HCO3[-]", }; specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); total_concentrations *= 1e3; total_concentrations(2) = 500; specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = raw_data->get_id_component("HO[-]"); specmicp::AdimensionalSystemSolver solver(raw_data, constraints); solver.get_options().solver_options.maxstep = 20.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.factor_descent_condition = 1e-10; solver.get_options().solver_options.factor_gradient_search_direction = 200; specmicp::Vector x(raw_data->nb_component()+raw_data->nb_mineral()); specmicp::micpsolver::MiCPPerformance perf = solver.solve(x, true); REQUIRE(perf.return_code >= specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet); specmicp::AdimensionalSystemSolution solution = solver.get_raw_solution(x); } } diff --git a/tests/specmicp/adim/adimensional_system_solver.cpp b/tests/specmicp/adim/adimensional_system_solver.cpp index 0af06b8..ad94788 100644 --- a/tests/specmicp/adim/adimensional_system_solver.cpp +++ b/tests/specmicp/adim/adimensional_system_solver.cpp @@ -1,176 +1,179 @@ #include "catch.hpp" -#include -#include "utils/log.hpp" + +#include "specmicp_common/log.hpp" + #include "specmicp/adimensional/adimensional_system.hpp" -#include "micpsolver/micpsolver.hpp" +#include "specmicp_common/micpsolver/micpsolver.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include "specmicp/adimensional/adimensional_system_solution_saver.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" + +#include specmicp::RawDatabasePtr get_test_simple_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); thedatabase.remove_half_cell_reactions(std::vector({"H2O", "HO[-]",})) ; return thedatabase.get_database(); } using namespace specmicp; TEST_CASE("Solving adimensional system", "[specmicp, MiCP, program, adimensional, solver]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Error; specmicp::RawDatabasePtr thedatabase = get_test_simple_database(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); SECTION("Solving simple case") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 3.0e4; total_concentration(id_oh) = 2.0e4; total_concentration(id_ca) = 1.0e4; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::micpsolver::MiCPSolverOptions options; options.maxstep = 100; options.maxiter_maxstep = 100; options.disable_crashing(); options.enable_scaling(); options.disable_descent_direction(); std::shared_ptr system= std::make_shared(thedatabase, constraints); specmicp::micpsolver::MiCPSolver solver(system); solver.set_options(options); Vector x = Vector::Zero(system->total_variables()); x(system->ideq_w()) = 0.8; x(system->ideq_paq(id_oh)) = -2.0; x(system->ideq_paq(id_ca)) = -2.3; system->compute_log_gamma(x); system->set_secondary_concentration(x); solver.solve(x); CHECK(x(system->ideq_w()) == Approx(0.542049).epsilon(1e-4)); CHECK(x(system->ideq_paq(id_oh)) == Approx(-1.462142).epsilon(1e-4)); CHECK(x(system->ideq_paq(id_ca)) == Approx(-1.820933).epsilon(1e-4)); CHECK(x(system->ideq_min(id_ch)) == Approx(0.32966).epsilon(1e-4)); } SECTION("Solving simple case - volume in cm^3") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 0.03; total_concentration(id_oh) = 0.02; total_concentration(id_ca) = 0.01; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::micpsolver::MiCPSolverOptions options; options.maxstep = 10; options.maxiter_maxstep = 100; options.disable_crashing(); options.enable_scaling(); options.disable_descent_direction(); std::shared_ptr system= std::make_shared(thedatabase, constraints); units::UnitsSet unit_set; unit_set.length = units::LengthUnit::centimeter; system->set_units(unit_set); specmicp::micpsolver::MiCPSolver solver(system); solver.set_options(options); Vector x = Vector::Zero(system->total_variables()); x(system->ideq_w()) = 0.8; x(system->ideq_paq(id_oh)) = -2.0; x(system->ideq_paq(id_ca)) = -2.3; system->compute_log_gamma(x); system->set_secondary_concentration(x); solver.solve(x); CHECK(x(system->ideq_w()) == Approx( 0.54205).epsilon(1e-4)); CHECK(x(system->ideq_paq(id_oh)) == Approx(-1.46214).epsilon(1e-4)); CHECK(x(system->ideq_paq(id_ca)) == Approx(-1.82093).epsilon(1e-4)); CHECK(x(system->ideq_min(id_ch)) == Approx( 0.32966).epsilon(1e-4)); } SECTION("Automatic solver") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 0.03; total_concentration(id_oh) = 0.02; total_concentration(id_ca) = 0.01; specmicp::Vector x; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints); solver.initialise_variables(x, 0.8, -2.0); //x(solver.dof_surface()) = -HUGE_VAL; solver.get_options().units_set.length = specmicp::units::LengthUnit::centimeter; solver.get_options().solver_options.maxstep = 10.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.disable_descent_direction(); solver.get_options().solver_options.factor_gradient_search_direction = 100; solver.solve(x); AdimensionalSystemSolution solution = solver.get_raw_solution(x); AdimensionalSystemSolutionExtractor extr(solution, thedatabase, solver.get_options().units_set); CHECK(extr.volume_fraction_water() == Approx(0.542049).epsilon(1e-4)); CHECK(extr.log_molality_component(id_oh) == Approx(-1.46214).epsilon(1e-4)); CHECK(extr.log_molality_component(id_ca) == Approx(-1.82093).epsilon(1e-4)); //CHECK(extr.free_surface_concentration() == -HUGE_VAL); CHECK(extr.volume_fraction_mineral(id_ch) == Approx(0.32966).epsilon(1e-4)); AdimensionalSystemSolutionSaver saver(thedatabase); saver.save_solution("test_solution.yaml", solution); AdimensionalSystemSolution solution2 = saver.parse_solution("test_solution.yaml"); REQUIRE(solution.main_variables(0) == Approx(solution2.main_variables(0))); REQUIRE(solution.main_variables(id_oh) == Approx(solution2.main_variables(id_oh))); REQUIRE(solution.main_variables(id_ca) == Approx(solution2.main_variables(id_ca))); REQUIRE(extr.volume_fraction_mineral(id_ch) == Approx(solution2.main_variables(extr.dof_mineral(id_ch)))); REQUIRE(solution.log_gamma.norm() == Approx(solution2.log_gamma.norm())); } } diff --git a/tests/specmicp/adim/adimensional_system_thermocarbo.cpp b/tests/specmicp/adim/adimensional_system_thermocarbo.cpp index 564dcfe..4f6d6fd 100644 --- a/tests/specmicp/adim/adimensional_system_thermocarbo.cpp +++ b/tests/specmicp/adim/adimensional_system_thermocarbo.cpp @@ -1,156 +1,156 @@ #include "catch.hpp" -#include - -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" -#include "utils/timer.hpp" +#include "specmicp_common/timer.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" + +#include TEST_CASE("thermocarbo - using adimensional system ", "[Adimensional, Thermocarbo]") { std::cerr.flush(); std::cout.flush(); specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Warning; SECTION("Thermocarbo") { std::cout << "\n-------------------\n Thermocarbo\n -------------------" << std::endl; specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"} }); thedatabase.swap_components(swapping); thedatabase.remove_gas_phases(); specmicp::Formulation formulation; specmicp::scalar_t mult = 2.0; formulation.mass_solution = mult*0.156; formulation.amount_minerals = {{"C3S", mult*0.7}, {"C2S", mult*0.3}}; formulation.extra_components_to_keep = {"HCO3[-]", }; specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = raw_data->get_id_component("HO[-]"); specmicp::index_t id_h2o = raw_data->water_index(); specmicp::index_t id_ho = raw_data->get_id_component("HO[-]"); specmicp::index_t id_co2 = raw_data->get_id_component("HCO3[-]"); specmicp::AdimensionalSystemSolverOptions options; options.solver_options.maxstep = 20.0; options.solver_options.maxiter_maxstep = 100; options.solver_options.disable_descent_direction(); options.solver_options.set_tolerance(1e-8, 1e-12); //options.solver_options.disable_non_monotone_linesearch(); options.solver_options.disable_condition_check(); options.solver_options.disable_crashing(); options.solver_options.enable_scaling(); options.units_set.length = specmicp::units::LengthUnit::decimeter; specmicp::Timer tot_timer; tot_timer.start(); specmicp::Vector x; specmicp::AdimensionalSystemSolver solver(raw_data, constraints, options); solver.initialise_variables(x, 0.8, -4.0); specmicp::scalar_t dh2co3 = 0.1; specmicp::index_t nb_iter {0}; constexpr specmicp::index_t nb_step = 40; specmicp::Vector solution(nb_step); for (int k=0; k(perf.return_code) >= static_cast(specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet)); nb_iter += perf.nb_iterations; constraints.total_concentrations(id_h2o) += mult*dh2co3; constraints.total_concentrations(id_ho) -= mult*dh2co3; constraints.total_concentrations(id_co2) += mult*dh2co3; specmicp::AdimensionalSystemSolution adim_solution = solver.get_raw_solution(x); solution(k) = 14+adim_solution.main_variables(id_ho); specmicp::AdimensionalSystemSolutionExtractor extr(adim_solution, raw_data, options.units_set); //std::cout << "Saturation water : " << extr.volume_fraction_water() << " - " // << extr.porosity() << " - " // << extr.saturation_water() << std::endl; } tot_timer.stop(); std::cout << "Total time : " << tot_timer.elapsed_time() << "s (Ref 0.01s)" << std::endl; std::cout << "Nb iter : " << nb_iter << " (Ref 560)" << std::endl; specmicp::Vector reference_solution(nb_step); reference_solution << 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.53785, 12.15348, 12.15348, 12.15348, 12.15348, 12.15348, 12.15348, 12.15348, 12.15348, 9.8575, 9.8575, 9.8575, 9.8575, 9.8575, 9.8575, 9.8575, 9.8575, 8.97554, 5.35498, 5.16745, 5.05979, 4.98442, 4.92662, 4.87987, 4.84073, 4.80714, 4.77778, 4.75174, 4.72839, 4.70725; for (int k=0;k -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" -#include "utils/timer.hpp" +#include "specmicp_common/timer.hpp" + +#include "specmicp_database/database.hpp" -#include "database/database.hpp" +#include using namespace specmicp; scalar_t water_pressure(scalar_t sat) { const scalar_t tmp = std::pow(sat, -2.1684) -1; const scalar_t exp = 1.0-1.0/2.1684; scalar_t ln_hr = - 3.67 * std::pow(tmp, exp); const scalar_t p = 31.7e2*std::exp(ln_hr); return p; // in Pa } TEST_CASE("thermocarbo - with water pressure", "[Adimensional],[Thermocarbo],[Water pressure]") { std::cerr.flush(); std::cout.flush(); specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Warning; SECTION("Thermocarbo") { std::cout << "\n-------------------\n Thermocarbo - with water pressure\n -------------------" << std::endl; specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); specmicp::RawDatabasePtr raw_data = thedatabase.get_database(); std::map swapping ({ {"H[+]","HO[-]"}, {"Si(OH)4", "SiO(OH)3[-]"} }); thedatabase.swap_components(swapping); thedatabase.remove_gas_phases(); specmicp::Formulation formulation; specmicp::scalar_t mult = 3.0; formulation.mass_solution = mult*0.156; formulation.amount_minerals = {{"C3S", mult*0.7}, {"C2S", mult*0.3}}; formulation.extra_components_to_keep = {"HCO3[-]", }; specmicp::Vector total_concentrations = specmicp::Dissolver(raw_data).dissolve(formulation); specmicp::AdimensionalSystemConstraints constraints(total_concentrations); constraints.charge_keeper = raw_data->get_id_component("HO[-]"); constraints.set_water_partial_pressure_model((water_partial_pressure_f) water_pressure); specmicp::index_t id_h2o = raw_data->water_index(); specmicp::index_t id_ho = raw_data->get_id_component("HO[-]"); specmicp::index_t id_co2 = raw_data->get_id_component("HCO3[-]"); specmicp::AdimensionalSystemSolverOptions options; options.solver_options.maxstep = 20.0; options.solver_options.maxiter_maxstep = 100; options.solver_options.disable_descent_direction(); options.solver_options.set_tolerance(1e-10, 1e-12); //options.solver_options.disable_non_monotone_linesearch(); options.solver_options.disable_condition_check(); options.solver_options.disable_crashing(); options.solver_options.enable_scaling(); options.units_set.length = specmicp::units::LengthUnit::decimeter; specmicp::Timer tot_timer; tot_timer.start(); specmicp::Vector x; specmicp::AdimensionalSystemSolver solver(raw_data, constraints, options); solver.initialise_variables(x, 0.8, -4.0); specmicp::scalar_t dh2co3 = 0.1; specmicp::index_t nb_iter {0}; constexpr specmicp::index_t nb_step = 20; for (int k=0; k(perf.return_code) >= static_cast(specmicp::micpsolver::MiCPSolverReturnCode::NotConvergedYet)); nb_iter += perf.nb_iterations; specmicp::AdimensionalSystemSolution adim_solution = solver.get_raw_solution(x); specmicp::AdimensionalSystemSolutionExtractor extr(adim_solution, raw_data, options.units_set); //std::cout << "Saturation water : " << extr.volume_fraction_water() << " / " << x(0) << " = " << extr.volume_fraction_water() / x(0) // << " - " // << extr.porosity() << " - " // << extr.saturation_water() << std::endl; const scalar_t tot_conc_w = extr.total_solid_concentration(0) + extr.mass_concentration_water()*extr.total_aqueous_concentration(0); const scalar_t tot_conc_g = extr.volume_fraction_gas_phase()*1e-3*( water_pressure(extr.saturation_water())/ (constants::gas_constant*(273.16+25.0)) ); const scalar_t tot_conc_w_pg = tot_conc_w + tot_conc_g; //std::cout << "Vol frac gas : " << extr.volume_fraction_gas_phase() << std::endl; //std::cout << "hop : " << extr.total_solid_concentration(0) << " + " // << extr.mass_concentration_water()*extr.total_aqueous_concentration(0) << " + " // << tot_conc_g << std::endl; //std::cout << "tot_conc : " << tot_conc_w0 << " - " << tot_conc_w << " = " << tot_conc_w0 - tot_conc_w << std::endl; //std::cout << "tot_conc : " << tot_conc_w0 << " - " << tot_conc_w_pg << " = " << tot_conc_w0 - tot_conc_w_pg << std::endl; CHECK(tot_conc_w0 == Approx(tot_conc_w_pg).epsilon(1e-10)); //std::cout << "diff tot " << tot_conc_w - tot_conc_w_pg << std::endl; //std::cout << "p_v(" << extr.saturation_water() << ") = " << water_pressure(extr.saturation_water()) << " Pa"<< std::endl; constraints.total_concentrations(id_h2o) += mult*dh2co3; constraints.total_concentrations(id_ho) -= mult*dh2co3; constraints.total_concentrations(id_co2) += mult*dh2co3; } tot_timer.stop(); std::cout << "Total time : " << tot_timer.elapsed_time() << "s (Ref 0.007s)" << std::endl; std::cout << "Nb iter : " << nb_iter << " (Ref 360)" << std::endl; } } diff --git a/tests/specmicp/adim/io_hdf5_adimensional.cpp b/tests/specmicp/adim/io_hdf5_adimensional.cpp index b9ad044..3e5d41f 100644 --- a/tests/specmicp/adim/io_hdf5_adimensional.cpp +++ b/tests/specmicp/adim/io_hdf5_adimensional.cpp @@ -1,109 +1,109 @@ #include "catch.hpp" #include -#include "utils/log.hpp" +#include "specmicp_common/log.hpp" #include "specmicp/adimensional/adimensional_system.hpp" -#include "micpsolver/micpsolver.hpp" +#include "specmicp_common/micpsolver/micpsolver.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" #include "specmicp/adimensional/adimensional_system_solution_saver.hpp" -#include "database/database.hpp" +#include "specmicp_database/database.hpp" -#include "utils/io/specmicp_hdf5.hpp" +#include "specmicp_common/io/specmicp_hdf5.hpp" #include "specmicp/io/hdf5_adimensional.hpp" #include using namespace specmicp; using namespace specmicp::io; static specmicp::RawDatabasePtr get_test_simple_database() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]"}; thedatabase.keep_only_components(to_keep); thedatabase.remove_half_cell_reactions(std::vector({"H2O", "HO[-]",})) ; return thedatabase.get_database(); } TEST_CASE("Adimensional_hdf5_saver", "[Adimensional],[io],[hdf5]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Error; specmicp::RawDatabasePtr thedatabase = get_test_simple_database(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); SECTION("Save and read solution") { Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 0.03; total_concentration(id_oh) = 0.02; total_concentration(id_ca) = 0.01; specmicp::Vector x; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints); solver.initialise_variables(x, 0.8, -2.0); solver.get_options().units_set.length = specmicp::units::LengthUnit::centimeter; solver.get_options().solver_options.maxstep = 10.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.disable_descent_direction(); solver.get_options().solver_options.factor_gradient_search_direction = 100; solver.solve(x); AdimensionalSystemSolution solution = solver.get_raw_solution(x); HDF5File file("test_specmicp_adim_solution", HDF5_OpenMode::CreateTruncate); save_adimensional_system_solution( file, "test_solution", "/", solution ); file.close(); file.open("test_specmicp_adim_solution", HDF5_OpenMode::OpenReadOnly); AdimensionalSystemSolution readto; read_adimensional_system_solution(file, "test_solution", "/", readto); file.close(); CHECK(readto.is_valid); CHECK(readto.main_variables.rows() == solution.main_variables.rows()); CHECK(readto.main_variables(0) == Approx(solution.main_variables(0))); CHECK(readto.main_variables(3) == Approx(solution.main_variables(3))); CHECK(readto.log_gamma.rows() == solution.log_gamma.rows()); CHECK(readto.log_gamma(2) == Approx(solution.log_gamma(2))); CHECK(readto.secondary_molalities.rows() == solution.secondary_molalities.rows()); CHECK(readto.secondary_molalities(1) == Approx(solution.secondary_molalities(1))); CHECK(readto.ionic_strength == Approx(solution.ionic_strength)); CHECK(readto.inert_volume_fraction == Approx(solution.inert_volume_fraction)); } } diff --git a/tests/specmicp/adim_kinetics/set_mineral_kinetic.cpp b/tests/specmicp/adim_kinetics/set_mineral_kinetic.cpp index 379bfda..0f49328 100644 --- a/tests/specmicp/adim_kinetics/set_mineral_kinetic.cpp +++ b/tests/specmicp/adim_kinetics/set_mineral_kinetic.cpp @@ -1,91 +1,91 @@ #include "catch.hpp" -#include -#include "utils/log.hpp" -#include "specmicp/adimensional/adimensional_system.hpp" -#include "micpsolver/micpsolver.hpp" +#include "specmicp_common/log.hpp" +#include "specmicp_common/micpsolver/micpsolver.hpp" -#include "specmicp/adimensional/adimensional_system_solver.hpp" +#include "specmicp_database/database.hpp" +#include "specmicp/adimensional/adimensional_system.hpp" +#include "specmicp/adimensional/adimensional_system_solver.hpp" #include "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/dissolver.hpp" #include "specmicp/adimensional/adimensional_system_solution_extractor.hpp" - #include "specmicp/adimensional/adimensional_system_solution_saver.hpp" -#include "database/database.hpp" + +#include specmicp::RawDatabasePtr get_test_database_set_kinetic() { specmicp::database::Database thedatabase(TEST_CEMDATA_PATH); std::map swapping ({ {"H[+]","HO[-]"}, }); thedatabase.swap_components(swapping); std::vector to_keep = {"HO[-]", "Ca[2+]", "HCO3[-]"}; thedatabase.keep_only_components(to_keep); thedatabase.remove_half_cell_reactions(std::vector({"H2O", "HO[-]",})) ; return thedatabase.get_database(); } using namespace specmicp; TEST_CASE("Set a mineral to be governed by kinetics", "[adimensional],[solver],[modificator],[kinetics]") { specmicp::logger::ErrFile::stream() = &std::cerr; specmicp::stdlog::ReportLevel() = specmicp::logger::Error; SECTION("Set kinetics") { specmicp::RawDatabasePtr thedatabase = get_test_database_set_kinetic(); auto id_h2o = database::DataContainer::water_index(); auto id_oh = thedatabase->get_id_component("HO[-]"); auto id_ca = thedatabase->get_id_component("Ca[2+]"); auto id_hco3 = thedatabase->get_id_component("HCO3[-]"); auto id_ch = thedatabase->get_id_mineral("Portlandite"); auto id_calcite_eq = thedatabase->get_id_mineral("Calcite"); Vector total_concentration = Vector::Zero(thedatabase->nb_component()); total_concentration(id_h2o) = 0.03; total_concentration(id_oh) = 0.015; total_concentration(id_ca) = 0.01; total_concentration(id_hco3) = 0.005; specmicp::Vector x; specmicp::AdimensionalSystemConstraints constraints(total_concentration); specmicp::AdimensionalSystemSolver solver(thedatabase, constraints); solver.initialise_variables(x, 0.8, -2.0); //x(solver.dof_surface()) = -HUGE_VAL; solver.get_options().units_set.length = specmicp::units::LengthUnit::centimeter; solver.get_options().solver_options.maxstep = 10.0; solver.get_options().solver_options.maxiter_maxstep = 100; solver.get_options().solver_options.use_crashing = false; solver.get_options().solver_options.use_scaling = true; solver.get_options().solver_options.disable_descent_direction(); solver.get_options().solver_options.factor_gradient_search_direction = 100; solver.solve(x); AdimensionalSystemSolution solution = solver.get_raw_solution(x); AdimensionalSystemSolutionExtractor extr(solution, thedatabase, solver.get_options().units_set); scalar_t volume_fraction_calcite = extr.volume_fraction_mineral(id_calcite_eq); AdimensionalSystemSolutionModificator modor(solution, thedatabase, solver.get_options().units_set); std::vector to_kinetic {id_calcite_eq}; Vector vol_fracs = modor.set_minerals_kinetics(to_kinetic); CHECK(vol_fracs(0) == volume_fraction_calcite); CHECK(thedatabase->get_id_mineral_kinetic("Calcite") != no_species); CHECK(thedatabase->get_id_mineral("Calcite") == no_species); CHECK(thedatabase->get_id_mineral_kinetic("Calcite") == to_kinetic[0]); } }