Page MenuHomec4science

adimensional_system_solver.cpp
No OneTemporary

File Metadata

Created
Mon, May 13, 17:08

adimensional_system_solver.cpp

/*-------------------------------------------------------
- Module : specmicp
- File : reduced_system_solver
- Author : Fabien Georget
Copyright (c) 2014, Fabien Georget <fabieng@princeton.edu>, 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:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the Princeton University 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 OWNER 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 "specmicp/adimensional/adimensional_system_solution.hpp"
#include "adimensional_system_pcfm.hpp"
#include "utils/log.hpp"
namespace specmicp {
// constructor
// -----------
AdimensionalSystemSolver::AdimensionalSystemSolver(RawDatabasePtr data,
const AdimensionalSystemConstraints& constraints,
AdimensionalSystemSolverOptions options
):
OptionsHandler<AdimensionalSystemSolverOptions>(options),
m_data(data),
m_system(std::make_shared<AdimensionalSystem>(
data,
constraints,
options.system_options
)),
m_var(Vector::Zero(data->nb_component+data->nb_mineral))
{}
AdimensionalSystemSolver::AdimensionalSystemSolver(RawDatabasePtr data,
const AdimensionalSystemConstraints& constraints,
const AdimensionalSystemSolution& previous_solution,
AdimensionalSystemSolverOptions options
):
OptionsHandler<AdimensionalSystemSolverOptions>(options),
m_data(data),
m_system(std::make_shared<AdimensionalSystem>(
data,
constraints,
previous_solution,
options.system_options
)),
m_var(Vector::Zero(data->nb_component+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 scalar_t save_factor_descent = get_options().solver_options.factor_descent_condition;
const bool save_scaling = get_options().solver_options.use_scaling;
get_options().solver_options.factor_descent_condition *= 1e-2;
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.factor_descent_condition = save_factor_descent;
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<AdimensionalSystem> 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<index_t>& non_active_component = m_system->get_non_active_component();
if ((non_active_component.size() == 0)
and
(m_system->ideq_surf() != no_equation))
{
// we still copy the data, if we failed to solve the problem, we can restart
if (m_system->is_active_component(0))
m_var = x; // direct copy
else
m_var = x.block(1, 0, x.rows()-1, 1);
for (int i=0; i<m_var.rows(); ++i)
{
if (m_var(i) == -HUGE_VAL) // check for previously undefined value
{
m_var(i) = m_system->get_options().new_component_concentration;
}
}
}
else // remove the dof that are not part of the problem
{
uindex_t new_i = 0;
if (m_system->is_active_component(0))
{
m_var(0) = x(m_system->dof_water());
++new_i;
}
for (index_t i: m_data->range_aqueous_component())
{
auto it = std::find(non_active_component.begin(), non_active_component.end(),i);
if (it != non_active_component.end()) 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;
}
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<index_t>& non_active_component = m_system->get_non_active_component();
if (non_active_component.size() == 0 and m_system->ideq_surf() != no_equation) // shortcut
{
if (m_system->is_active_component(0))
x = m_var; //direct copy
else
x.block(1, 0, x.rows()-1, 1) = m_var;
// at that point we should have the correct solution
}
else
{
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->saturation_water(x);
}
for (index_t i: m_data->range_aqueous_component())
{
auto it = std::find(non_active_component.begin(), non_active_component.end(),i);
if (it != non_active_component.end())
{
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;
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 << " <? " << res_0;
if (std::isfinite(final_residual) and final_residual < res_0)
retcode = PCFMReturnCode::Success;
}
// if we did a good job, use the solution as initial state
if (retcode == PCFMReturnCode::Success)
{
DEBUG << "Successful PCFM iterations.";
set_return_vector(x);
}
else // we reset
{
DEBUG << "Unsuccessful PCFM iterations. The problem is reset to the initial state.";
set_true_variable_vector(x);
m_system->set_secondary_variables(m_var);
}
}
// Initialisation of variables
// ---------------------------
void AdimensionalSystemSolver::initialise_variables(
Vector& x,
scalar_t volume_fraction_water,
std::map<std::string, scalar_t> log_molalities,
std::map<std::string, scalar_t> volume_fraction_minerals,
scalar_t log_free_sorption_site_concentration
)
{
m_system->reasonable_starting_guess(x);
database::Database database(m_data);
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 = database.component_label_to_id(pair.first);
if (idc == no_species or idc == 0)
{
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 = database.mineral_label_to_id(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

Event Timeline