Page MenuHomec4science

kato_saturated.cpp
No OneTemporary

File Metadata

Created
Wed, May 1, 02:46

kato_saturated.cpp

/*
* SPDX-License-Indentifier: AGPL-3.0-or-later
*
* Copyright (©) 2016-2024 EPFL (École Polytechnique Fédérale de Lausanne),
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
* Copyright (©) 2020-2024 Lucas Frérot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "kato_saturated.hh"
#include "logger.hh"
#include <iomanip>
#include <limits>
/* -------------------------------------------------------------------------- */
namespace tamaas {
/* -------------------------------------------------------------------------- */
KatoSaturated::KatoSaturated(Model& model, const GridBase<Real>& surface,
Real tolerance, Real pmax)
: PolonskyKeerRey(model, surface, tolerance, PolonskyKeerRey::pressure,
PolonskyKeerRey::pressure),
pmax(pmax) {
model.request<true, Real>("KatoSaturated::residual_displacement",
model.getType(), model.getBoundaryDiscretization(),
1);
}
/* -------------------------------------------------------------------------- */
Real KatoSaturated::solve(std::vector<Real> load) {
GridBase<Real> initial_surface = surface;
GridBase<Real>& residual_disp =
model.field<Real>("KatoSaturated::residual_displacement");
residual_disp = 0;
const auto norm = surface.var();
auto negative = [this, norm](const GridBase<Real>& f) {
const auto neg_norm = Loop::reduce<operation::plus>(
[](const Real& f) { return (f < 0) * f * f; }, f);
return neg_norm / (norm * f.getGlobalNbPoints()) > this->tolerance;
};
UInt n = 0;
do {
surface = initial_surface;
surface -= residual_disp;
PolonskyKeerRey::solve(load);
// Update the rough surface
Loop::loop([] CUDA_LAMBDA(Real & h_pl, Real g) { h_pl -= g * (g < 0); },
residual_disp, *this->dual);
} while (negative(*this->dual) and n++ < this->max_iterations);
surface = initial_surface;
*this->displacement_view += residual_disp;
return Real(n >= this->max_iterations);
}
/* -------------------------------------------------------------------------- */
Real KatoSaturated::meanOnUnsaturated(const GridBase<Real>& /*field*/) const {
return 0;
}
Real KatoSaturated::computeSquaredNorm(const GridBase<Real>& /*field*/) const {
return 1.;
}
void KatoSaturated::updateSearchDirection(Real /*factor*/) {
*this->search_direction = *this->dual;
}
Real KatoSaturated::computeCriticalStep(Real /*target*/) {
// integral_op->apply(*search_direction, *projected_search_direction);
// Real num = search_direction->dot(*search_direction);
// Real denum = projected_search_direction->dot(*search_direction);
// return 0.1 * num / denum;
return 1;
}
bool KatoSaturated::updatePrimal(Real step) {
UInt na_num = Loop::reduce<operation::plus>(
[step] CUDA_LAMBDA(Real & p, const Real& /*q*/, const Real& t) -> UInt {
p -= step * t; // Updating primal
return 0;
// if (p < 0)
// p = 0; // Truncating neg values
// if (p == 0 && q < 0) { // If non-admissible state
// p -= step * q;
// return 1;
// } else
// return 0;
},
*primal, *dual, *search_direction);
return na_num == 0;
}
/* -------------------------------------------------------------------------- */
Real KatoSaturated::computeError() const {
// We shift the gap by the minimum on unsaturated area
const auto pmax = this->pmax;
const Real shift = Loop::reduce<operation::min>(
[pmax] CUDA_LAMBDA(Real p, Real d) {
return (p < pmax) ? d : std::numeric_limits<Real>::max();
},
*primal, *dual);
// Ignore points that are saturated
const Real error = Loop::reduce<operation::plus>(
[pmax, shift] CUDA_LAMBDA(Real p, Real d) {
return (p < pmax) * p * (d - shift);
},
*primal, *dual);
if (std::isnan(error))
throw nan_error{
TAMAAS_MSG("Encountered NaN in complementarity error: this may be ",
"caused by a contact area of a single node.")};
Real norm = 1;
if (variable_type == pressure)
norm = std::abs(primal->sum() * this->surface_stddev);
else
norm = std::abs(dual->sum() * this->surface_stddev);
return std::abs(error) / norm;
}
/* -------------------------------------------------------------------------- */
void KatoSaturated::enforceMeanValue(Real mean) {
// We want to cancel the difference between saturated alpha + t and the
// applied pressure
const auto pmax = this->pmax;
*primal -= primal->mean();
auto f = [&](Real scale) {
Real sum = Loop::reduce<operation::plus>(
[pmax, scale] CUDA_LAMBDA(Real t) -> Real {
t += scale;
if (t > pmax)
return pmax;
if (t < 0)
return 0;
return t;
},
*this->primal);
sum /= this->primal->getGlobalNbPoints();
return sum - mean;
};
if (pmax < mean)
throw std::runtime_error{TAMAAS_MSG("cannot find equilibrium")};
// Dichotomy + Secant Newton on f
// Initial points
Real x_n_2 = -primal->max(), x_n_1 = -primal->min(), x_n = 0.;
Real f_n_2 = 0., f_n_1 = 0., f_n = 0.;
// Find a not-too-large search interval
Logger().get(LogLevel::debug) << TAMAAS_MSG("Searching equilibrium interval");
while (std::signbit(f(x_n_1)) == std::signbit(f(x_n_2))) {
x_n_1 *= 10;
x_n_2 *= 10;
}
Logger().get(LogLevel::debug) << TAMAAS_MSG(
"Reducing interval [abs(x1/x2) = ", std::abs(x_n_1 / x_n_2), ']');
UInt n_dic = 10;
f_n_1 = f(x_n_1);
for (UInt i = 0; i < n_dic; ++i) {
x_n = 0.5 * (x_n_1 + x_n_2);
f_n = f(x_n);
if (std::signbit(f_n) == std::signbit(f_n_1)) {
x_n_1 = x_n;
f_n_1 = f_n;
} else
x_n_2 = x_n;
}
Logger().get(LogLevel::debug) << TAMAAS_MSG(
"Starting Newton secant [abs(x1/x2) = ", std::abs(x_n_1 / x_n_2), ']');
// Secant loop
do {
f_n_2 = f(x_n_2);
f_n_1 = f(x_n_1);
if (f_n_1 == f_n_2)
break; // Avoid nans
x_n = x_n_1 - f_n_1 * (x_n_1 - x_n_2) / (f_n_1 - f_n_2);
f_n = f(x_n);
x_n_2 = x_n_1;
x_n_1 = x_n;
} while (std::abs(f_n / mean) > 1e-14);
// Pressure update
Loop::loop(
[pmax, x_n] CUDA_LAMBDA(Real & t) {
t += x_n;
if (t > pmax)
t = pmax;
else if (t < 0)
t = 0.;
},
*this->primal);
}
/* -------------------------------------------------------------------------- */
void KatoSaturated::enforceAdmissibleState() {
/// Make dual admissible
const auto pmax = this->pmax;
Real shift = Loop::reduce<operation::min>(
[pmax] CUDA_LAMBDA(Real p, Real d) {
return (p < pmax) ? d : std::numeric_limits<Real>::max();
},
*primal, *dual);
*dual -= shift;
*displacement_view = *dual;
*displacement_view += this->surface;
}
} // namespace tamaas

Event Timeline