diff --git a/src/reactmicp/systems/chloride/equilibrium_stagger.cpp b/src/reactmicp/systems/chloride/equilibrium_stagger.cpp index 3356615..df2a41a 100644 --- a/src/reactmicp/systems/chloride/equilibrium_stagger.cpp +++ b/src/reactmicp/systems/chloride/equilibrium_stagger.cpp @@ -1,364 +1,397 @@ /* ============================================================================= Copyright (c) 2014-2017 F. Georget Princeton University Copyright (c) 2017 F. Georget EPFL 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_stagger.hpp" #include "specmicp_common/cached_vector.hpp" #include "specmicp/adimensional/adimensional_system_solution.hpp" #include "specmicp/adimensional/adimensional_system_structs.hpp" #include "specmicp/adimensional/adimensional_system_solver_structs.hpp" #include "specmicp/adimensional/adimensional_system_solver.hpp" #include "reactmicp/solver/staggers_base/stagger_structs.hpp" #include "variables.hpp" #include "boundary_conditions.hpp" #include "reactmicp/systems/common/equilibrium_options_vector.hpp" #include "specmicp_common/log.hpp" #include #include "specmicp/io/print.hpp" namespace specmicp { namespace reactmicp { namespace systems { namespace chloride { struct ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl { std::shared_ptr m_options; std::shared_ptr m_bcs; RawDatabasePtr m_data; scalar_t m_dt; ChlorideEquilibriumStaggerImpl( std::shared_ptr var, std::shared_ptr bcs, std::shared_ptr opts ): m_options(opts), m_bcs(bcs), m_data(var->get_database()) {} void initialize(ChlorideVariables * const var); void initialize_timestep(scalar_t dt, ChlorideVariables * const var); AdimensionalSystemConstraints pre_filled_constraints( index_t node, ChlorideVariables * const vars ); micpsolver::MiCPSolverReturnCode solve_one_node( index_t node, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous, AdimensionalSystemSolution& output ); AdimensionalSystemConstraints pre_filled_constraints_perturb( index_t node, ChlorideVariables * const vars, const Eigen::Ref& nodal_displacement ); int compute_one_node( index_t node, ChlorideVariables* const vars, AdimensionalSystemSolution& out ); void analyse_solution( index_t node, const AdimensionalSystemSolution& solution, ChlorideVariables* const vars ); ChlorideVariables * const cast_to_true_var(VariablesBase * const var) { return static_cast(var); } }; // Main interface // ============== ChlorideEquilibriumStagger::ChlorideEquilibriumStagger( std::shared_ptr var, std::shared_ptr bcs, std::shared_ptr opts ): m_impl(utils::make_pimpl(var, bcs, opts)) { } ChlorideEquilibriumStagger::~ChlorideEquilibriumStagger() = default; void ChlorideEquilibriumStagger::initialize(VariablesBase * const var) { return m_impl->initialize(m_impl->cast_to_true_var(var)); } void ChlorideEquilibriumStagger::initialize_timestep(scalar_t dt, VariablesBase * const var) { return m_impl->initialize_timestep(dt, m_impl->cast_to_true_var(var)); } solver::StaggerReturnCode ChlorideEquilibriumStagger::restart_timestep(VariablesBase * const var) { std::cout << "Restart eq timestep" << std::endl; ChlorideVariables * const true_var = m_impl->cast_to_true_var(var); int sum_retcode = 0; #ifdef SPECMICP_HAVE_OPENMP #pragma omp parallel for\ schedule(dynamic, 5)\ reduction(+: sum_retcode) #endif // SPECMICP_HAVE_OPENMP for (index_t n=0; nget_mesh()->nb_nodes();++n) { AdimensionalSystemSolution sol; if (static_cast(solve_equilibrium_at_node(n, true_var, sol)) <= 0) { sum_retcode += -1; } } if (sum_retcode != 0) { return solver::StaggerReturnCode::UnknownError; } return solver::StaggerReturnCode::ResidualMinimized; } ReturnCode ChlorideEquilibriumStagger::solve_equilibrium_at_node( index_t node, solver::VariablesBase* const var, AdimensionalSystemSolution& out) { ChlorideVariables * const vars = m_impl->cast_to_true_var(var); AdimensionalSystemConstraints constraints = m_impl->pre_filled_constraints(node, vars); micpsolver::MiCPSolverReturnCode retcode = m_impl->solve_one_node(node, constraints, vars->adim_solution(node), out); if (retcode <= micpsolver::MiCPSolverReturnCode::Success) { + io::print_specmicp_constraints(m_impl->m_data, constraints); + scalar_t sum = 0.0; + for (auto idc: m_impl->m_data->range_aqueous_component()) { + sum += m_impl->m_data->charge_component(idc)*constraints.total_concentrations(idc); + } + std::cout << "Charge balance : " << sum << "\n"; ERROR << "Failed to solve equilibrium at node : " << node; } else { m_impl->analyse_solution(node, out, vars); } return micpsolver::to_generic_return_code(retcode); } // Implementation // ============== void ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::initialize(ChlorideVariables * const var) { } void ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::initialize_timestep(scalar_t dt, ChlorideVariables * const var) { m_dt = dt; for (auto node: var->get_mesh()->range_nodes()) { auto sol = var->adim_solution(node); if (not sol.is_valid) { throw std::runtime_error("Invalid adim solution for node " +std::to_string(node) + ". Whyyyyyyyy ?"); } } } ReturnCode ChlorideEquilibriumStagger::perturb_adim_solution( index_t node, ChlorideVariables * const var, const Eigen::Ref& nodal_displacement, const AdimensionalSystemSolution& previous, AdimensionalSystemSolution& out ) { AdimensionalSystemConstraints constraints = m_impl->pre_filled_constraints_perturb( node, var, nodal_displacement); //AdimensionalSystemSolutionExtractor extr(var->adim_solution(node), var->get_database(), var->get_units()); //std::cout << extr.volume_fraction_mineral(var->get_database()->get_id_mineral("Portlandite")) << std::endl; //if (node == 1) { //std::cout << node << "\n"; //io::print_specmicp_constraints(var->get_database(), constraints); //} AdimensionalSystemSolverOptions opts = (*m_impl->m_options)[node]; opts.system_options.non_ideality = false; //opts.solver_options.set_maximum_step_length(0.1); //opts.solver_options.set_tolerance(1e-2); AdimensionalSystemSolver solver(m_impl->m_data, constraints, previous, opts); auto x = previous.main_variables; auto perf = solver.solve(x, false); if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) { out = solver.merge_raw_solution_with_immobile_species(x, previous); } else if (perf.return_code <= micpsolver::MiCPSolverReturnCode::Success) { + io::print_specmicp_constraints(m_impl->m_data, constraints); + io::print_specmicp_options(std::cout, opts); ERROR << "Failed to solve equilibrium at node : " << node; } return micpsolver::to_generic_return_code(perf.return_code); } // ### int ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::compute_one_node( index_t node, ChlorideVariables* const vars, AdimensionalSystemSolution& out ) { AdimensionalSystemConstraints constraints = pre_filled_constraints(node, vars); micpsolver::MiCPSolverReturnCode retcode = solve_one_node(node, constraints, vars->adim_solution(node), out); if (retcode < micpsolver::MiCPSolverReturnCode::Success) { return -1; } analyse_solution(node, out, vars); return 0; } void ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::analyse_solution( index_t node, const AdimensionalSystemSolution& solution, ChlorideVariables* const vars ) { vars->adim_solution(node) = solution; AdimensionalSystemSolutionExtractor extr(solution, m_data, vars->get_units() ); if (node == 1) { std::cout << extr.volume_fraction_mineral(m_data->get_id_mineral("Friedels_salt")) << "\n"; } for (auto i: m_data->range_aqueous_component()) { auto dof = vars->dof_component(node, i); auto conc_aq = extr.total_aqueous_concentration(i)*extr.density_water(); auto conc_aq_vel = (conc_aq - vars->predictors()(dof))/m_dt; vars->velocities()(dof) = conc_aq_vel; vars->main_variables()(dof) = conc_aq; auto conc_immobile = extr.total_immobile_concentration(i); auto conc_immobile_vel = (conc_immobile - vars->predictor_immobile_concentrations()(dof))/m_dt; vars->total_immobile_concentrations()(dof) = conc_immobile; vars->velocity_immobile_concentrations()(dof) = conc_immobile_vel; vars->chemistry_rates()(dof) = -conc_immobile_vel; } { auto porosity = extr.porosity(); vars->velocities_porosity()(node) = (porosity - vars->predictors_porosity()(node))/m_dt; vars->porosity()(node) = porosity; } } micpsolver::MiCPSolverReturnCode ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::solve_one_node( index_t node, const AdimensionalSystemConstraints& constraints, const AdimensionalSystemSolution& previous, AdimensionalSystemSolution& output ) { - AdimensionalSystemSolver solver(m_data, constraints, previous, (*m_options)[node]); + auto& opts = (*m_options)[node]; + if (opts.system_options.solve_solid_solutions == false) { - output.main_variables = previous.main_variables; - auto perf = solver.solve(output.main_variables, false); - if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) { - output = solver.get_raw_solution(output.main_variables); + std::cout << "Without solid solution \n"; + AdimensionalSystemSolver solver(m_data, constraints, previous, opts); + + output.main_variables = previous.main_variables; + auto perf = solver.solve(output.main_variables, false); + if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) { + output = solver.get_raw_solution(output.main_variables); + } + return perf.return_code; + } else { + + std::cout << "With solid solution \n"; + opts.system_options.solve_solid_solutions = false; + AdimensionalSystemSolver solver(m_data, constraints, opts); + + output.main_variables = previous.main_variables; + auto perf = solver.solve(output.main_variables, false); + if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) { + std::cout << "Solve no solid solution \n"; + output = solver.get_raw_solution(output.main_variables); + opts.system_options.solve_solid_solutions = true; + AdimensionalSystemSolver solver(m_data, constraints, output, opts); + perf = solver.solve(output.main_variables, false); + if (perf.return_code > micpsolver::MiCPSolverReturnCode::NotConvergedYet) { + std::cout << "Solve solid solution \n"; + output = solver.get_raw_solution(output.main_variables); + } + } + return perf.return_code; } - return perf.return_code; } // ### implementation AdimensionalSystemConstraints ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::pre_filled_constraints( index_t node, ChlorideVariables * const vars ) { AdimensionalSystemConstraints constraints = m_bcs->get_constraint(node); constraints.inert_volume_fraction = vars->inert_volume_fraction(node); Vector tot_concs = Vector::Zero(m_data->nb_component()); for (auto aqc: m_data->range_aqueous_component()) { tot_concs(aqc) = vars->porosity(node)*vars->total_mobile_concentration(node, aqc)+vars->total_immobile_concentration(node, aqc); } constraints.set_total_concentrations(tot_concs); return constraints; } AdimensionalSystemConstraints ChlorideEquilibriumStagger::ChlorideEquilibriumStaggerImpl::pre_filled_constraints_perturb( index_t node, ChlorideVariables * const vars, const Eigen::Ref& nodal_displacement ) { AdimensionalSystemConstraints constraints = m_bcs->get_constraint(node); //constraints.inert_volume_fraction = vars->inert_volume_fraction(node); Vector tot_concs = Vector::Zero(m_data->nb_component()); for (auto aqc: m_data->range_aqueous_component()) { const index_t ndof = vars->ndof_component(aqc); //tot_concs(aqc) = vars->porosity(node)*nodal_displacement(ndof); tot_concs(aqc) = nodal_displacement(ndof); } constraints.set_total_concentrations(tot_concs); constraints.disable_immobile_species(); return constraints; } } // namespace chloride } // namespace systems } // namespace reactmicp } // namespace specmicp diff --git a/src/specmicp/io/configuration.cpp b/src/specmicp/io/configuration.cpp index 2cba7c4..ca7222f 100644 --- a/src/specmicp/io/configuration.cpp +++ b/src/specmicp/io/configuration.cpp @@ -1,896 +1,898 @@ /* ============================================================================= Copyright (c) 2014-2017 F. Georget Princeton University Copyright (c) 2017-2018 F. Georget EPFL 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 "specmicp/adimensional/adimensional_system_solver_structs.hpp" #include "specmicp/adimensional/config_default_options_solver.h" #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 "specmicp/problem_solver/formulation.hpp" #include "specmicp/problem_solver/reactant_box.hpp" #include "specmicp/problem_solver/smart_solver.hpp" #include "specmicp_database/database.hpp" #include "specmicp_common/io/config_yaml_sections.h" #include namespace specmicp { namespace io { // Additional declarations // ======================= //! \brief Configure a fixed fugacity component //! //! \deprecated void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_activity, const RawDatabasePtr& raw_db ); //! \brief Configure a fixed fugacity component //! //! \deprecated void configure_specmicp_constraints_fixed_fugacity( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_fugacity, const RawDatabasePtr& raw_db ); //! \brief Configure a fixed molality constraint //! //! \deprecated void configure_specmicp_constraints_fixed_molality( AdimensionalSystemConstraints& constraints, const YAML::Node& conf_constraints_fixed_molality, const RawDatabasePtr& raw_db ); //! \brief Configure a fixed fugacity component void configure_specmicp_constraints_fixed_activity( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_activity ); //! \brief Configure a fixed fugacity component void configure_specmicp_constraints_fixed_fugacity( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_fugacity ); //! \brief Configure a fixed molality constraint void configure_specmicp_constraints_fixed_molality( AdimensionalSystemConstraints& constraints, const database::DataContainer * const raw_db, YAMLConfigHandle&& conf_constraints_fixed_molality ); // Implementation // ============== 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, SPC_CF_S_SPECMICP_A_RES_TOL, 0.0); conf.set_if_attribute_exists( solver.steptol, SPC_CF_S_SPECMICP_A_STEP_TOL, 0.0); conf.set_if_attribute_exists( solver.max_iter, SPC_CF_S_SPECMICP_A_MAX_ITER, 0); conf.set_if_attribute_exists( solver.maxstep, SPC_CF_S_SPECMICP_A_MAX_STEP_LENGTH, 0.0); conf.set_if_attribute_exists( solver.maxiter_maxstep, SPC_CF_S_SPECMICP_A_MAX_STEP_MAX_ITER, 0); conf.set_if_attribute_exists( solver.use_scaling, SPC_CF_S_SPECMICP_A_SCALING); conf.set_if_attribute_exists( solver.non_monotone_linesearch, SPC_CF_S_SPECMICP_A_NONMONOTONE); conf.set_if_attribute_exists( solver.factor_descent_condition, SPC_CF_S_SPECMICP_A_DESCENT_DIRECTION); conf.set_if_attribute_exists( solver.condition_limit, SPC_CF_S_SPECMICP_A_COND_CHECK); conf.set_if_attribute_exists( solver.threshold_cycling_linesearch, SPC_CF_S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH); AdimensionalSystemOptions& system = options.system_options; conf.set_if_attribute_exists( system.non_ideality_tolerance, SPC_CF_S_SPECMICP_A_NONIDEAL_TOL, 0.0); conf.set_if_attribute_exists( system.non_ideality_max_iter, SPC_CF_S_SPECMICP_A_NONIDEAL_MAX_ITER, 0); conf.set_if_attribute_exists( system.cutoff_total_concentration, SPC_CF_S_SPECMICP_A_CUTOFF_TOT_CONC, 0.0); conf.set_if_attribute_exists( system.restart_concentration, SPC_CF_S_SPECMICP_A_RESTART_CONCENTRATION); conf.set_if_attribute_exists( system.restart_water_volume_fraction, SPC_CF_S_SPECMICP_A_RESTART_WATER_VOL_FRAC, 0.0); conf.set_if_attribute_exists( system.under_relaxation_factor, SPC_CF_S_SPECMICP_A_UNDER_RELAXATION, 0.0, 1.0); + conf.set_if_attribute_exists( + system.solve_solid_solutions, SPC_CF_S_SPECMICP_A_SOLVE_SOLID_SOLUTIONS, false); options.units_set = the_units; } // ReactantBox // ============ namespace internal { // helper functions static void configure_formulation( ReactantBox& reactant_box, YAMLConfigHandle&& configuration ); static void configure_formulation_aqueous( ReactantBox& reactant_box, YAMLConfigHandle&& cf_aqueous ); static void configure_formulation_solid( ReactantBox& reactant_box, YAMLConfigHandle&& cf_aqueous ); static void configure_constraints( ReactantBox& reactant_box, YAMLConfigHandle&& configuration ); static void configure_constraints_fixed_activity( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_act ); static void configure_constraints_fixed_fugacity( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_act ); static void configure_constraints_fixed_molality( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_mol ); static void configure_constraints_fixed_SI( ReactantBox& reactant_box, YAMLConfigHandle&& conf_constraints_SI ); } // end namespace internal void SPECMICP_DLL_PUBLIC configure_specmicp_reactant_box( ReactantBox& reactant_box, YAMLConfigHandle&& configuration ) { internal::configure_formulation( reactant_box, configuration.get_section(SPC_CF_S_FORMULATION) ); internal::configure_constraints( reactant_box, configuration.get_section(SPC_CF_S_CONSTRAINTS) ); } ReactantBox SPECMICP_DLL_PUBLIC configure_specmicp_reactant_box( RawDatabasePtr raw_db, const units::UnitsSet& the_units, YAMLConfigHandle&& configuration ) { ReactantBox reactant_box(raw_db, the_units); configure_specmicp_reactant_box(reactant_box, std::move(configuration)); return reactant_box; } namespace internal { // helper functions static void configure_formulation( ReactantBox& reactant_box, YAMLConfigHandle&& cf_formulation) { // solution auto cf_solution = cf_formulation.get_section(SPC_CF_S_FORMULATION_A_SOLUTION); const auto amount = cf_solution.get_required_attribute(SPC_CF_S_FORMULATION_A_AMOUNT); const auto unit = cf_solution.get_required_attribute(SPC_CF_S_FORMULATION_A_UNIT); reactant_box.set_solution(amount, unit); if (cf_formulation.has_section(SPC_CF_S_FORMULATION_A_AQUEOUS)) { configure_formulation_aqueous( reactant_box, cf_formulation.get_section(SPC_CF_S_FORMULATION_A_AQUEOUS) ); } if (cf_formulation.has_section(SPC_CF_S_FORMULATION_A_MINERALS)) { configure_formulation_solid( reactant_box, cf_formulation.get_section(SPC_CF_S_FORMULATION_A_MINERALS) ); } } static void configure_formulation_aqueous( ReactantBox& reactant_box, YAMLConfigHandle&& cf_aqueous ) { if (not cf_aqueous.is_sequence()) { cf_aqueous.report_error( YAMLConfigError::ListExpected, "Aqueous species must be provide as a list of triplet" " {label amount, unit}" ); } for (auto ind: RangeIterator(cf_aqueous.size())) { auto cf = cf_aqueous.get_section(ind); const auto label = cf.get_required_attribute(SPC_CF_S_FORMULATION_A_LABEL); const auto value = cf.get_required_attribute(SPC_CF_S_FORMULATION_A_AMOUNT); const auto unit = cf.get_required_attribute(SPC_CF_S_FORMULATION_A_UNIT); reactant_box.add_aqueous_species(label, value, unit); } } static void configure_formulation_solid( ReactantBox& reactant_box, YAMLConfigHandle&& cf_solid ) { if (not cf_solid.is_sequence()) { cf_solid.report_error( YAMLConfigError::ListExpected, "Solid species must be provide as a list of triplet" " {label amount, unit}" ); } for (auto ind: RangeIterator(cf_solid.size())) { auto cf = cf_solid.get_section(ind); const auto label = cf.get_required_attribute< std::string>(SPC_CF_S_FORMULATION_A_LABEL); const auto value = cf.get_required_attribute< double>(SPC_CF_S_FORMULATION_A_AMOUNT); const auto unit = cf.get_required_attribute< std::string>(SPC_CF_S_FORMULATION_A_UNIT); reactant_box.add_solid_phase(label, value, unit); } } static void configure_constraints( ReactantBox& reactant_box, YAMLConfigHandle&& cf) { // find the constraint to apply bool has_saturated = false; bool has_fixed_saturation = false; bool has_water_conservation = false; bool does_conservation_water = true; scalar_t water_param = -1.0; if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM)) { has_saturated = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM); } if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_FIXEDSATURATION)) { water_param = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_FIXEDSATURATION, 0.0, 1.0); if (water_param == 1.0) { if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM)) { cf.report_error(YAMLConfigError::InvalidArgument, "SaturatedSystem and a Fixed saturation of 1.0 are both set." "Provide only one of the options"); } has_saturated = true; } else has_fixed_saturation = true; } if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_CONSERVATION_WATER)) { has_water_conservation = true; does_conservation_water = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_CONSERVATION_WATER); } else { has_water_conservation = false; does_conservation_water = true; } if ( (int) has_saturated + (int) has_fixed_saturation + (int) has_water_conservation > 1) { cf.report_error(YAMLConfigError::InvalidArgument, "Incompatible options, choose only one of : " "Saturated system, fixed saturation or conservation water"); } if (has_saturated) { reactant_box.set_saturated_system(); } else if (has_fixed_saturation) { reactant_box.set_fixed_saturation(water_param); } else if (not does_conservation_water) { reactant_box.disable_conservation_water(); } // Charge keeper // ------------- if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_CHARGEKEEPER)) { auto label = cf.get_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_CHARGEKEEPER); reactant_box.set_charge_keeper(label); } // Fixed activity // --------------- if (cf.has_section(SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY)) { configure_constraints_fixed_activity( reactant_box, cf.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY) ); } // Fixed fugacity // -------------- if (cf.has_section(SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY)) { configure_constraints_fixed_fugacity( reactant_box, cf.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY) ); } // Fixed molality // --------------- if (cf.has_section(SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY)) { configure_constraints_fixed_molality( reactant_box, cf.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY) ); } // Fixed saturation index // ---------------------- if (cf.has_section(SPC_CF_S_CONSTRAINTS_A_SATURATIONINDEX)) { configure_constraints_fixed_SI(reactant_box, cf.get_section(SPC_CF_S_CONSTRAINTS_A_SATURATIONINDEX) ); } if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_INERT_VOLUME_FRACTION)) { reactant_box.set_inert_volume_fraction( cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_INERT_VOLUME_FRACTION) ); } // Immobile species // ---------------- if (cf.get_optional_attribute(SPC_CF_S_CONSTRAINTS_A_DISABLEIMMOBILE, false)) { reactant_box.disable_immobile_species(); } } static void configure_constraints_fixed_activity( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_act ) { if (not cf_f_act.is_sequence()) { cf_f_act.report_error( YAMLConfigError::ListExpected, "Fixed activity component must be provide as a list of" " triplet {label amount, unit}" ); } for (auto ind: RangeIterator(cf_f_act.size())) { auto cf = cf_f_act.get_section(ind); const auto label = cf.get_required_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_LABEL); double value {0.0}; // value can be provided as value or log_value if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT)) { value = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT); } else if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG)) { const auto log_val = cf.get_attribute< double>(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG); value = std::pow(10.0, log_val); } else { cf.report_error( YAMLConfigError::MissingRequiredAttribute, "Either amount or log amount must be provided." ); } reactant_box.add_fixed_activity_component(label, value); } } static void configure_constraints_fixed_fugacity( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_fug ) { if (not cf_f_fug.is_sequence()) { cf_f_fug.report_error( YAMLConfigError::ListExpected, "Fixed fugacity gas must be provide as a list of" " triplet {label amount, unit}" ); } for (auto ind: RangeIterator(cf_f_fug.size())) { auto cf = cf_f_fug.get_section(ind); // both component and gas must be provided const auto label_c = cf.get_required_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_LABEL_COMPONENT); const auto label_g = cf.get_required_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_LABEL_GAS); double value {0.0}; // value can be provided as value or log_value if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT)) { value = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT); } else if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG)) { const auto log_val = cf.get_attribute< double>(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG); value = std::pow(10.0, log_val); } else { cf.report_error( YAMLConfigError::MissingRequiredAttribute, "Either amount or log amount must be provided." ); } reactant_box.add_fixed_fugacity_gas(label_g, label_c, value); } } static void configure_constraints_fixed_molality( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_mol ) { if (not cf_f_mol.is_sequence()) { cf_f_mol.report_error( YAMLConfigError::ListExpected, "Fixed molality component must be provide as a list of" " triplet {label amount, unit}" ); } for (auto ind: RangeIterator(cf_f_mol.size())) { auto cf = cf_f_mol.get_section(ind); const auto label = cf.get_required_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_LABEL); double value {0.0}; // value can be provided as value or log_value if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT)) { value = cf.get_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT); } else if (cf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG)) { const auto log_val = cf.get_attribute< double>(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG); value = std::pow(10.0, log_val); } else { cf.report_error( YAMLConfigError::MissingRequiredAttribute, "Either amount or log amount must be provided." ); } reactant_box.add_fixed_molality_component(label, value); } } static void configure_constraints_fixed_SI( ReactantBox& reactant_box, YAMLConfigHandle&& cf_f_SI ) { if (not cf_f_SI.is_sequence()) { cf_f_SI.report_error( YAMLConfigError::ListExpected, "Fixed saturation index constraint must be provide as a list of" " triplet {label_mineral, label, amount}" ); } for (auto ind: RangeIterator(cf_f_SI.size())) { auto cf = cf_f_SI.get_section(ind); const auto label = cf.get_required_attribute< std::string>(SPC_CF_S_CONSTRAINTS_A_LABEL); const auto label_m = cf.get_required_attribute( SPC_CF_S_CONSTRAINTS_A_LABEL_MINERAL); scalar_t value {0.0}; value = cf.get_optional_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT, 0.0); reactant_box.add_fixed_saturation_index(label_m, label, value); } } } // end namespace internal // 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(SPC_CF_S_CONSTRAINTS_A_CONSERVATION_WATER)) { auto tmp_water = conf_constraints.get_attribute( SPC_CF_S_CONSTRAINTS_A_CONSERVATION_WATER); if (tmp_water) { // only one constraints if (conf_constraints.get_optional_attribute(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM, false)) { conf_constraints.report_error(YAMLConfigError::InvalidArgument, "Attributes " SPC_CF_S_CONSTRAINTS_A_CONSERVATION_WATER "and " SPC_CF_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(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM)) { if (conf_constraints.get_attribute(SPC_CF_S_CONSTRAINTS_A_SATURATED_SYSTEM)) { constraints.set_saturated_system(); } } conf_constraints.set_if_attribute_exists(constraints.inert_volume_fraction, SPC_CF_S_CONSTRAINTS_A_INERT_VOLUME_FRACTION ); // Aqueous components // ================== // charge keeper // ------------- if (conf_constraints.has_attribute(SPC_CF_S_CONSTRAINTS_A_CHARGEKEEPER)) { const auto label = conf_constraints.get_attribute( SPC_CF_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(SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY)) { configure_specmicp_constraints_fixed_activity( constraints, raw_db, conf_constraints.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY) ); } // Fixed fugacity // -------------- if (conf_constraints.has_section(SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY)) { configure_specmicp_constraints_fixed_fugacity( constraints, raw_db, conf_constraints.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY) ); } // Fixed activity // --------------- if (conf_constraints.has_section(SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY)) { configure_specmicp_constraints_fixed_molality( constraints, raw_db, conf_constraints.get_section(SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY) ); } // Surface model // ============= if (conf_constraints.has_attribute(SPC_CF_S_CONSTRAINTS_A_SURFACE_MODEL)) { const bool is_enabled = conf_constraints.get_attribute( SPC_CF_S_CONSTRAINTS_A_SURFACE_MODEL ); if (is_enabled) { const auto value = conf_constraints.get_required_attribute( SPC_CF_S_CONSTRAINTS_A_SURFACE_CONCENTRATION); Vector x; x.resize(1); x(0) = value; constraints.set_equilibrium_surface_model(x); } else { constraints.disable_surface_model(); } } // Immobile concentration // ====================== if (conf_constraints.get_optional_attribute( SPC_CF_S_CONSTRAINTS_A_DISABLEIMMOBILE, false)) { constraints.disable_immobile_species(); } } // helpers functions //! \brief Return the id of a component index_t get_id_component( const database::DataContainer* const raw_db, YAMLConfigHandle& conf, const std::string& attr_key=SPC_CF_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; } //! \brief Return the id of a gas index_t get_id_gas( const database::DataContainer* const raw_db, YAMLConfigHandle& conf, const std::string& attr_key=SPC_CF_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; } //! \brief Return a value in log10 for fixed fugacity scalar_t get_value(YAMLConfigHandle& conf) { scalar_t value = 0; if (conf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT)) { value = std::log10(conf.get_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNT)); } else if (conf.has_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG)) { value = conf.get_attribute(SPC_CF_S_CONSTRAINTS_A_AMOUNTLOG); } else { conf.report_error( YAMLConfigError::InvalidArgument, "Expected one of '" SPC_CF_S_CONSTRAINTS_A_AMOUNT "' or '" SPC_CF_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(SPC_CF_S_INITIALIZATION_A_SOLUTION)); } if (conf_init.has_section(SPC_CF_S_INITIALIZATION_A_AQUEOUS)) { auto scf = conf_init.get_section(SPC_CF_S_INITIALIZATION_A_AQUEOUS); if (not scf.is_map()) { scf.report_error(YAMLConfigError::MapExpected, "A map {component, value} is expected."); } if (scf.has_attribute("all")) { solver.set_init_molality(scf.get_attribute("all")); } std::unordered_map tmap; tmap.reserve(scf.size()); for (auto it=scf.map_begin(); it!=scf.map_end(); ++it) { auto name = (*it).first; if (name != "all") { tmap.insert({name, scf.get_attribute(name)}); } } if (not tmap.empty()) { solver.set_init_molality(tmap); } } if (conf_init.has_section(SPC_CF_S_INITIALIZATION_A_MINERALS)) { auto scf = conf_init.get_section(SPC_CF_S_INITIALIZATION_A_MINERALS); if (not scf.is_map()) { scf.report_error(YAMLConfigError::MapExpected, "A map {component, value} is expected."); } std::unordered_map tmap; tmap.reserve(scf.size()); for (auto it=scf.map_begin(); it!=scf.map_end(); ++it) { auto name = (*it).first; tmap.insert({name, scf.get_attribute(name)}); } if (not tmap.empty()) { solver.set_init_volfrac_mineral(tmap); } } } } //end namespace io } //end namespace specmicp diff --git a/src/specmicp/io/print.cpp b/src/specmicp/io/print.cpp index b51cbfb..2ac5c68 100644 --- a/src/specmicp/io/print.cpp +++ b/src/specmicp/io/print.cpp @@ -1,156 +1,157 @@ /* ============================================================================= 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 "specmicp/adimensional/adimensional_system_structs.hpp" #include "specmicp/adimensional/adimensional_system_solver_structs.hpp" #include "specmicp_common/physics/io/units.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 << "Water equation : "; switch (constraints.water_equation) { case WaterEquationType::MassConservation: std::cout << "Unsaturated system, mass conservation \n"; break; case WaterEquationType::FixedSaturation: std::cout << "Unsaturated system, fixed saturation (" << constraints.water_parameter << ")\n"; break; case WaterEquationType::SaturatedSystem: std::cout << "saturated system\n"; break; case WaterEquationType::NoEquation: std::cout << "WARNING : no water equations\n"; } std::cout << "-------------" << std::endl; } //! \brief Output options void print_specmicp_options( std::ostream& out, const AdimensionalSystemSolverOptions& options ) { const micpsolver::MiCPSolverOptions& sopts = options.solver_options; out << std::boolalpha; out << " + MiCPSolver options " << "\n - residual tolerance " << sopts.fvectol << "\n - step tolerance " << sopts.steptol << "\n - max. # iterations " << sopts.max_iter << "\n - max. step length " << sopts.maxstep << "\n - # iters at max. step " << sopts.maxiter_maxstep << "\n - threshold stationary " << sopts.threshold_stationary_point << "\n <-> Advanced options" << "\n - threshold cycling " << sopts.threshold_cycling_linesearch << "\n - use crashing " << sopts.use_crashing << "\n - use scaling " << sopts.use_scaling << "\n - nonmonotone linesearch " << sopts.non_monotone_linesearch << "\n - max. factorization step " << sopts.max_factorization_step << "\n - power descent direction " << sopts.power_descent_condition << "\n - factor descent direction " << sopts.factor_descent_condition << "\n - gradient step factor " << sopts.factor_gradient_search_direction << std::endl; const AdimensionalSystemOptions& systs = options.system_options; out << " + System options" << "\n - non-ideality model " << systs.non_ideality << "\n - non-ideality max. iter " << systs.non_ideality_max_iter << "\n - non-ideality tolerance " << systs.non_ideality_tolerance << "\n - non-ideality start " << systs.start_non_ideality_computation << "\n - under-relaxation factor " << systs.under_relaxation_factor << "\n - restart log(concentration) " << systs.restart_concentration << "\n - new log(concentration) " << systs.new_component_concentration << "\n - cutoff total concentration " << systs.cutoff_total_concentration + << "\n - solve solid solution " << systs.solve_solid_solutions << std::endl; out << " + Units " << "\n - length " << to_string(options.units_set.length) << "\n - mass " << to_string(options.units_set.mass) << "\n - quantity " << to_string(options.units_set.quantity) << std::endl; out << " + Misc." << "\n - allow restart " << options.allow_restart << "\n - use pcfm " << options.use_pcfm << "\n - force_pcfm " << options.force_pcfm << std::endl; } } //end namespace io } //end namespace specmicp diff --git a/src/specmicp_common/io/config_yaml_sections.h b/src/specmicp_common/io/config_yaml_sections.h index 996bbc9..fe7bdb2 100644 --- a/src/specmicp_common/io/config_yaml_sections.h +++ b/src/specmicp_common/io/config_yaml_sections.h @@ -1,849 +1,859 @@ /* ============================================================================= Copyright (c) 2014-2017 F. Georget Princeton University Copyright (c) 2017-2018 F. Georget EPFL 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_UTILS_IO_CONFIGYAMLSECTIONS_H #define SPECMICP_UTILS_IO_CONFIGYAMLSECTIONS_H //! \file io/config_yaml_sections.h //! \brief Define sections in the YAML config files //! //! Name are built as follow //! PREFIX_[X_NAME]n //! //! where PREFIX is SPC_CF (specmicp conf) //! X is either A (for attribute), v (for value), S (for section) or SS (for subsection) //! and NAME is the name used in the conf file //! \def SPC_CF_NAME_PATH_ROOT //! \brief Name of the first level #define SPC_CF_NAME_PATH_ROOT "root" // common options // -------------- //! \def SPC_CF_A_MAX_ITER //! \brief Maximum iterations #define SPC_CF_A_MAX_ITER "maximum_iterations" //! \def SPC_CF_A_RES_TOL //! \brief The residuals tolerance //! //! Absolute in SpecMiCP //! Relative for DFPM #define SPC_CF_A_RES_TOL "residual_tolerance" //! \def SPC_CF_A_ABS_TOL //! \brief Absolute residuals tolerance #define SPC_CF_A_ABS_TOL "absolute_tolerance" //! \def SPC_CF_A_STEP_TOL //! \brief Absolute tolerance for the update #define SPC_CF_A_STEP_TOL "step_tolerance" //! \def SPC_CF_A_MAX_STEP_LENGTH //! \brief Maximum value for the update #define SPC_CF_A_MAX_STEP_LENGTH "maximum_step_length" //! \def SPC_CF_A_MAX_STEP_MAX_ITER //! \brief Maximum number at iterations when update value is maximum #define SPC_CF_A_MAX_STEP_MAX_ITER "maximum_step_maximum_iterations" //! \def SPC_CF_A_FILEPATH //! \brief Path to a file #define SPC_CF_A_FILEPATH "file" //! \def SPC_CF_A_COMPONENT //! \brief A component #define SPC_CF_A_COMPONENT "component" //! \def SPC_CF_A_NODES //! \brief one or several nodes //! //! example: "1,2,5-8" for nodes 1,2,5,6,7 and 8 #define SPC_CF_A_NODES "nodes" //! \def SPC_CF_A_TYPE //! \brief Used when a thing can have several values #define SPC_CF_A_TYPE "type" //! \def SPC_CF_V_DEFAULT //! \brief To indicate that a thing should be the default type #define SPC_CF_V_DEFAULT "default" //! \def SPC_CF_V_PLUGIN //! \brief To indicate that a thing should use a user provided plugin #define SPC_CF_V_PLUGIN "plugin" //! \def SPC_CF_A_PLUGIN_FILE //! \brief Attribute used to provide the path to a plugin file #define SPC_CF_A_PLUGIN_FILE "plugin_file" // filepath to a plugin //! \def SPC_CF_A_PLUGIN_NAME //! \brief Attribute used to provide the name of a plugin #define SPC_CF_A_PLUGIN_NAME "plugin_name" // name of the plugin to use // user variables // -------------- //! \def SPC_CF_S_USRVARS //! \brief Section containing user defined variables //! //! These variables are used to compute algebraic expressions at runtime #define SPC_CF_S_USRVARS "vars" //! \def SPC_CF_S_USRVARS_SS_INT //! \brief Subsection containing user defined integral variables //! //! Format is name: value //! //! Value can be an algebraic expression of previously defined integer variables #define SPC_CF_S_USRVARS_SS_INT "int" //! \def SPC_CF_S_USRVARS_SS_FLOAT //! \brief Subsection containing user defined floating-point variables //! //! Format is name: value //! //! Value can be an algebraic expression of previously defined variables //! (integer and floating points) #define SPC_CF_S_USRVARS_SS_FLOAT "float" // plugin manager // -------------- //! \def SPC_CF_S_PLUGINS //! \brief Section describing plugin options #define SPC_CF_S_PLUGINS "plugins" //! \def SPC_CF_S_PLUGINS_A_SEARCHDIRS //! \brief Directories where plugins can be found #define SPC_CF_S_PLUGINS_A_SEARCHDIRS "dirs" //! \def SPC_CF_S_PLUGINS_A_TOLOAD //! \brief Plugins to load #define SPC_CF_S_PLUGINS_A_TOLOAD "to_load" // Logs // ---- // type of output //! \def SPC_CF_S_LOGS_V_COUT //! \brief Logs should be written to standard output #define SPC_CF_S_LOGS_V_COUT "cout" //! \def SPC_CF_S_LOGS_V_CERR //! \brief Logs should be written to standard error output #define SPC_CF_S_LOGS_V_CERR "cerr" //! \def SPC_CF_S_LOGS_V_FILE //! \brief Logs should be written to a file #define SPC_CF_S_LOGS_V_FILE "file" // the different levels //! \def SPC_CF_S_LOGS_V_CRITICAL //! \brief Log message should be at least of level "critical" to be written #define SPC_CF_S_LOGS_V_CRITICAL "critical" //! \def SPC_CF_S_LOGS_V_ERROR //! \brief Log message should be at least of level "error" to be written #define SPC_CF_S_LOGS_V_ERROR "error" //! \def SPC_CF_S_LOGS_V_WARNING //! \brief Log message should be at least of level "warning" to be written #define SPC_CF_S_LOGS_V_WARNING "warning" //! \def SPC_CF_S_LOGS_V_INFO //! \brief Log message should be at least of level "info" to be written #define SPC_CF_S_LOGS_V_INFO "info" //! \def SPC_CF_S_LOGS_V_DEBUG //! \brief Log message should be at least of level "debug" to be written #define SPC_CF_S_LOGS_V_DEBUG "debug" // runtime logs //! \def SPC_CF_S_LOGS //! \brief Runtime logs #define SPC_CF_S_LOGS "logs" //! \def SPC_CF_S_LOGS_A_LEVEL //! \brief Output level for the runtime logs #define SPC_CF_S_LOGS_A_LEVEL "level" //! \def SPC_CF_S_LOGS_A_OUTPUT //! \brief The ouput of the runtime logs //! //! Values are given by SPC_CF_S_LOGS_V_X where x is //! either COUT, CERR, or FILE #define SPC_CF_S_LOGS_A_OUTPUT "output" //! \def SPC_CF_S_LOGS_A_FILEPATH //! \brief the path to a file where logs should be written //! //! Only needed if logs output is a file #define SPC_CF_S_LOGS_A_FILEPATH SPC_CF_A_FILEPATH // configuration logs //! \def SPC_CF_S_CONF_LOGS //! \brief Logs written when configuration is parsed #define SPC_CF_S_CONF_LOGS "configuration_logs" //! \def SPC_CF_S_CONF_LOGS_A_OUTPUT //! \brief The ouput of the configuration logs //! //! Values are given by SPC_CF_S_LOGS_V_X where x is //! either COUT, CERR, or FILE #define SPC_CF_S_CONF_LOGS_A_OUTPUT "output" //! \def SPC_CF_S_CONF_LOGS_A_FILE //! \brief the path to a file where logs configuration should be written //! //! Only needed if logs output is a file #define SPC_CF_S_CONF_LOGS_A_FILE SPC_CF_A_FILEPATH // units // ----- //! \def SPC_CF_S_UNITS //! \brief Configuration section for the units #define SPC_CF_S_UNITS "units" //! \def SPC_CF_S_UNITS_A_LENGTH //! \brief Configure the length unit #define SPC_CF_S_UNITS_A_LENGTH "length" //! \def SPC_CF_S_UNITS_A_MASS //! \brief Configure the mass unit #define SPC_CF_S_UNITS_A_MASS "mass" //! \def SPC_CF_S_UNITS_A_QUANTITY //! \brief Configure the amount of substance unit #define SPC_CF_S_UNITS_A_QUANTITY "quantity" // mesh // ---- //! \def SPC_CF_S_MESH //! \brief Section to configure the mesh #define SPC_CF_S_MESH "mesh" //! \def SPC_CF_S_MESH_A_TYPE //! \brief Type of the mesh #define SPC_CF_S_MESH_A_TYPE "type" //! \def SPC_CF_S_MESH_SS_UNIFMESH //! \brief Subsection to configure a uniform mesh #define SPC_CF_S_MESH_SS_UNIFMESH "uniform_mesh" //! \def SPC_CF_S_MESH_SS_UNIFMESH_A_DX //! \brief The length of an element #define SPC_CF_S_MESH_SS_UNIFMESH_A_DX "dx" //! \def SPC_CF_S_MESH_SS_UNIFMESH_A_NBNODES //! \brief The number of nodes #define SPC_CF_S_MESH_SS_UNIFMESH_A_NBNODES "nb_nodes" //! \def SPC_CF_S_MESH_SS_UNIFMESH_A_SECTION //! \brief Cross section of the sample #define SPC_CF_S_MESH_SS_UNIFMESH_A_SECTION "section" //! \def SPC_CF_S_MESH_SS_RAMPMESH //! \brief Subsection to configure a ramp mesh //! //! Ramp mesh start by an increasing element size and finish with a plateau //! at a max element size #define SPC_CF_S_MESH_SS_RAMPMESH "ramp_mesh" //! \def SPC_CF_S_MESH_SS_RAMPMESH_A_MIN_DX //! \brief The minimum size of an element #define SPC_CF_S_MESH_SS_RAMPMESH_A_MIN_DX "min_dx" //! \def SPC_CF_S_MESH_SS_RAMPMESH_A_MAX_DX //! \brief The maximum size of an element #define SPC_CF_S_MESH_SS_RAMPMESH_A_MAX_DX "max_dx" //! \def SPC_CF_S_MESH_SS_RAMPMESH_A_RAMP_LENGTH //! \brief Total length of the ramp #define SPC_CF_S_MESH_SS_RAMPMESH_A_RAMP_LENGTH "length_ramp" //! \def SPC_CF_S_MESH_SS_RAMPMESH_A_PLATEAU_LENGTH //! \brief Total lenght of the plateau #define SPC_CF_S_MESH_SS_RAMPMESH_A_PLATEAU_LENGTH "length_plateau" //! \def SPC_CF_S_MESH_SS_RAMPMESH_A_SECTION //! \brief Cross section of the sample #define SPC_CF_S_MESH_SS_RAMPMESH_A_SECTION "section" //! \def SPC_CF_S_MESH_SS_UNIFAXISYMESH //! \brief An axysimetric mesh, uniform (in radius, no volume) //! //! Either the number of nodes or the step can be provided. #define SPC_CF_S_MESH_SS_UNIFAXISYMESH "uniform_axisymmetric" //! \def SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_RADIUS //! \brief Total radius of the sample #define SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_RADIUS "radius" //! \def SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_NBNODES //! \brief Number of nodes #define SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_NBNODES "nb_nodes" //! \def SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_HEIGHT //! \brief Height of the sample #define SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_HEIGHT "height" //! \def SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_DX //! \brief Lenght between two nodes #define SPC_CF_S_MESH_SS_UNIFAXISYMESH_A_DX "dx" //! \def SPC_CF_S_MESH_SS_CUSTNODEMESH //! \brief Subsection to configure a custom node mesh #define SPC_CF_S_MESH_SS_CUSTNODEMESH "custom_nodes" //! \def SPC_CF_S_MESH_SS_CUSTNODEMESH_A_COORDS //! \brief Cross section of the sample #define SPC_CF_S_MESH_SS_CUSTNODEMESH_A_COORDS "coordinates" //! \def SPC_CF_S_MESH_SS_CUSTNODEMESH_A_SECTION //! \brief Cross section of the sample #define SPC_CF_S_MESH_SS_CUSTNODEMESH_A_SECTION "section" // database // -------- //! \def SPC_CF_S_DATABASE //! \brief Section to configure the database #define SPC_CF_S_DATABASE "database" //! \def SPC_CF_S_DATABASE_A_PATH //! \brief Path to teh database file //! //! Can be name of a file if search directories has been correctly set. //! In particular, the database should search in directories provided by //! the environment variable SPECMICP_DATABASE_PATH #define SPC_CF_S_DATABASE_A_PATH "path" //! \def SPC_CF_S_DATABASE_A_CHECKCOMPO //! \brief If true, database will run a consistency check //! (formula, electroneutrality...) //! //! It is overwritten by a flag in database file if provided. #define SPC_CF_S_DATABASE_A_CHECKCOMPO "check_compo" //! \def SPC_CF_S_DATABASE_A_SWAP //! \brief Map containing species to be swapped #define SPC_CF_S_DATABASE_A_SWAP "swap_components" //! \def SPC_CF_S_DATABASE_A_SWAP_K_IN //! \brief Aqueous species to add as a component #define SPC_CF_S_DATABASE_A_SWAP_K_IN "in" //! \def SPC_CF_S_DATABASE_A_SWAP_K_OUT //! \brief Current compoment to remove from basis #define SPC_CF_S_DATABASE_A_SWAP_K_OUT "out" //! \def SPC_CF_S_DATABASE_A_REMOVE_GAS -//! \brief A list of gas to remove from the database +//! \brief If true remove gas from the database #define SPC_CF_S_DATABASE_A_REMOVE_GAS "remove_gas" //! \def SPC_CF_S_DATABASE_A_REMOVE_SOLIDS -//! \brief A list of solids to remove from the database +//! \brief If true remove solids from the database #define SPC_CF_S_DATABASE_A_REMOVE_SOLIDS "remove_solids" //! \def SPC_CF_S_DATABASE_A_REMOVE_SORBED -//! \brief A list of sorbed species to remove from the database +//! \brief If true remove sorbed species from the database #define SPC_CF_S_DATABASE_A_REMOVE_SSITES "remove_ssites" //! \def SPC_CF_S_DATABASE_A_REMOVE_SORBED -//! \brief A list of sorbed species to remove from the database +//! \brief If true remove surface sites from the database #define SPC_CF_S_DATABASE_A_REMOVE_SORBED "remove_sorbed" +//! \def SPC_CF_S_DATABASE_A_REMOVE_SOLID_SOLUTIONS +//! \brief A list of solid solutions to remove from the databse +#define SPC_CF_S_DATABASE_A_REMOVE_SOLID_SOLUTIONS "remove_solid_solutions" //! \def SPC_CF_S_DATABASE_A_EXTRA_GAS //! \brief a YAML-format string to add gas to the database #define SPC_CF_S_DATABASE_A_EXTRA_GAS "extra_gas" //! \def SPC_CF_S_DATABASE_A_EXTRA_SOLIDS //! \brief a YAML-format string to add solid phases to the database #define SPC_CF_S_DATABASE_A_EXTRA_SOLIDS "extra_solids" //! \def SPC_CF_S_DATABASE_A_EXTRA_SSITES //! \brief a YAML-format string to add surface sites to the database #define SPC_CF_S_DATABASE_A_EXTRA_SSITES "extra_ssites" //! \def SPC_CF_S_DATABASE_A_EXTRA_SORBED //! \brief a YAML-format string to add sorbed species to the database #define SPC_CF_S_DATABASE_A_EXTRA_SORBED "extra_sorbed" - +//! \def SPC_CF_S_DATABASE_A_EXTRA_SOLID_SOLUTIONS +//! \brief a YAML-format string to add solid solutions to the database +#define SPC_CF_S_DATABASE_A_EXTRA_SOLID_SOLUTIONS "extra_solid_solutions" //! \def SPC_CF_S_DATABASE_A_REMOVE_HALF_CELLS //! \brief If true remove all half cells reaction from the database //! //! half-cells reactions are reaction involving the electron component #define SPC_CF_S_DATABASE_A_REMOVE_HALF_CELLS "remove_half_cells" //! \def SPC_CF_S_DATABASE_A_LIST_SOLIDS_TOKEEP //! \brief a list of solid phases to keep in the database //! //! All solid phases not listed will be removed from the database #define SPC_CF_S_DATABASE_A_LIST_SOLIDS_TOKEEP "list_solids_to_keep" // SpecMiCP options // ---------------- //! \def SPC_CF_S_SPECMICP //! \brief Section to configure specmicp solver options //! //! May have a different name if several option sets are needed (ex. ReactMiCP), //! but attributes are the same. #define SPC_CF_S_SPECMICP "specmicp_options" //! \def SPC_CF_S_SPECMICP_A_MAX_ITER //! \brief The maximum number of iterations allowed #define SPC_CF_S_SPECMICP_A_MAX_ITER SPC_CF_A_MAX_ITER //! \def SPC_CF_S_SPECMICP_A_RES_TOL //! \brief The absolute residual tolerance #define SPC_CF_S_SPECMICP_A_RES_TOL SPC_CF_A_RES_TOL //! \def SPC_CF_S_SPECMICP_A_STEP_TOL //! \brief The update tolerance #define SPC_CF_S_SPECMICP_A_STEP_TOL SPC_CF_A_STEP_TOL //! \def SPC_CF_S_SPECMICP_A_MAX_STEP_LENGTH //! \brief The maximum update allowed //! //! This option is used to avoid divergence #define SPC_CF_S_SPECMICP_A_MAX_STEP_LENGTH SPC_CF_A_MAX_STEP_LENGTH //! \def SPC_CF_S_SPECMICP_A_MAX_STEP_MAX_ITER //! \brief The maximum number of iterations at maximum update allowed //! //! This option is used to detect tolerances #define SPC_CF_S_SPECMICP_A_MAX_STEP_MAX_ITER SPC_CF_A_MAX_STEP_MAX_ITER //! \def SPC_CF_S_SPECMICP_A_SCALING //! \brief If true solver automatically scale the linear problem #define SPC_CF_S_SPECMICP_A_SCALING "enable_scaling" //! \def SPC_CF_S_SPECMICP_A_NONMONOTONE //! \brief If true, the solver uses a nonmonotone linesearch algorithm #define SPC_CF_S_SPECMICP_A_NONMONOTONE "enable_nonmonotone_linesearch" //! \def SPC_CF_S_SPECMICP_A_DESCENT_DIRECTION //! \brief Multiplication applied to the descent direction when used as update //! //! Must be positive. #define SPC_CF_S_SPECMICP_A_DESCENT_DIRECTION "factor_descent_direction" //! \def SPC_CF_S_SPECMICP_A_COND_CHECK //! \brief Upper threshold for the condition number of the matrix //! //! if =-1, the check is not run #define SPC_CF_S_SPECMICP_A_COND_CHECK "threshold_condition_check" //! \def SPC_CF_S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH //! \brief Threshold used to check if linesearch is stuck in a loop #define SPC_CF_S_SPECMICP_A_TRSHOLD_CYCLING_LSEARCH "threshold_cycling_linesearch" //! \def SPC_CF_S_SPECMICP_A_NONIDEAL_TOL //! \brief Relative tolerance for the non-ideality model solution #define SPC_CF_S_SPECMICP_A_NONIDEAL_TOL "non_ideality_tolerance" //! \def SPC_CF_S_SPECMICP_A_NONIDEAL_MAX_ITER //! \brief The maximum number of iteration to solve the non-ideality model #define SPC_CF_S_SPECMICP_A_NONIDEAL_MAX_ITER "non_ideality_" SPC_CF_A_MAX_ITER //! \def SPC_CF_S_SPECMICP_A_CUTOFF_TOT_CONC //! \brief Cutoff to include an equation into the system #define SPC_CF_S_SPECMICP_A_CUTOFF_TOT_CONC "cutoff_total_concentration" //! \def SPC_CF_S_SPECMICP_A_RESTART_CONCENTRATION //! \brief If the solver fails, the component concentrations will be reset to this value //! //! Value must be the log10 of the concentration (e.g. -3 instead of 10^-3) #define SPC_CF_S_SPECMICP_A_RESTART_CONCENTRATION "restart_concentration" //! \def SPC_CF_S_SPECMICP_A_RESTART_WATER_VOL_FRAC //! \brief If the solver fails, the volume fraction of water will be reset to this value #define SPC_CF_S_SPECMICP_A_RESTART_WATER_VOL_FRAC "restart_water_volume_fraction" //! \def SPC_CF_S_SPECMICP_A_UNDER_RELAXATION //! \brief Under relaxation factor for water volume fraction update #define SPC_CF_S_SPECMICP_A_UNDER_RELAXATION "under_relaxation" + +//! \def SPC_CF_S_SPECMICP_A_SOLVE_SOLID_SOLUTIONS +//! \brief If true, solve solid solutions +#define SPC_CF_S_SPECMICP_A_SOLVE_SOLID_SOLUTIONS "enable_solid_solutions" + // SpecMiCP formulation // -------------------- //! \def SPC_CF_S_FORMULATION //! \brief Create a new chemical system #define SPC_CF_S_FORMULATION "formulation" //! \def SPC_CF_S_FORMULATION_A_SOLUTION //! \brief Configure the water in the system //! //! A map expecting keys amount and unit #define SPC_CF_S_FORMULATION_A_SOLUTION "solution" //! \def SPC_CF_S_FORMULATION_A_AQUEOUS //! \brief Configure the aqueous solution in the system //! //! A list of maps expecting keys amount, unit and label #define SPC_CF_S_FORMULATION_A_AQUEOUS "aqueous" //! \def SPC_CF_S_FORMULATION_A_MINERALS //! \brief Configure the solid phases in the system //! //! A list of maps expecting keys amount, unit and label #define SPC_CF_S_FORMULATION_A_MINERALS "solid_phases" //! \def SPC_CF_S_FORMULATION_A_AMOUNT //! \brief Provide the amount of a species to be added //! //! Meaning depends on the unit #define SPC_CF_S_FORMULATION_A_AMOUNT "amount" //! \def SPC_CF_S_FORMULATION_A_UNIT //! \brief Provide the unit //! //! Available units depend on the type of the species being added #define SPC_CF_S_FORMULATION_A_UNIT "unit" //! \def SPC_CF_S_FORMULATION_A_LABEL //! \brief Specify the species to be added //! //! A list of maps expecting keys amount, unit and label #define SPC_CF_S_FORMULATION_A_LABEL "label" //! \def SPC_CF_S_INITIALIZATION //! \brief Initialization of AdimSmartSolver #define SPC_CF_S_INITIALIZATION "initialization" //! \def SPC_CF_S_INITIALIZATION_A_SOLUTION //! \brief The initial volume fraction #define SPC_CF_S_INITIALIZATION_A_SOLUTION "solution" //! \def SPC_CF_S_INITIALIZATION_A_MINERALS //! \brief Initialization of solid phases volume fractions //! //! A map {solid_phase, volume_fraction} is expected #define SPC_CF_S_INITIALIZATION_A_MINERALS "solid_phases" //! \def SPC_CF_S_INITIALIZATION_A_AQUEOUS //! \brief Initialization of aqueous species //! //! A map {aq_species, volume_fraction} is expected. //! If aq_species is "all", values are applied to all components. #define SPC_CF_S_INITIALIZATION_A_AQUEOUS "aqueous" // SpecMiCP constraints // -------------------- //! \def SPC_CF_S_CONSTRAINTS //! \brief Contraintes for the SpecMiCP solver #define SPC_CF_S_CONSTRAINTS "constraints" //! \def SPC_CF_S_CONSTRAINTS_A_CHARGEKEEPER //! \brief A component that will be adjusted to keep the electroneutrality #define SPC_CF_S_CONSTRAINTS_A_CHARGEKEEPER "charge_keeper" //! \def SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY //! \brief List of components with fixed activity //! //! List of maps {label, amount} or {label, amount_log} #define SPC_CF_S_CONSTRAINTS_A_FIXEDACTIVITY "fixed_activity" //! \def SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY //! \brief List of components with fixed molality //! //! List of maps {label, amount} or {label, amount_log} #define SPC_CF_S_CONSTRAINTS_A_FIXEDMOLALITY "fixed_molality" //! \def SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY //! \brief List of gas/components with fixed fugacity //! //! List of maps {label_gas, label_component, amount} or //! {label_gas, label_component, amount_log} #define SPC_CF_S_CONSTRAINTS_A_FIXEDFUGACITY "fixed_fugacity" //! \def SPC_CF_S_CONSTRAINTS_A_FIXEDSATURATIONINDEX //! \brief List of minerals/components with fixed saturation index //! //! List of maps {label_mineral, label_component, SI} or //! {label_mineral, label_component, SI} #define SPC_CF_S_CONSTRAINTS_A_SATURATIONINDEX "fixed_saturation_index" //! \def SPC_CF_S_CONSTRAINTS_A_FIXEDSATURATION //! \brief Set the system to have a fix saturation //! //! Expect the saturation (0