79 failureReason_.clear();
80 if (diffusionField ==
nullptr || deformationField ==
nullptr) {
82 .addError(
"OxidationModel: Missing diffusion or deformation "
88 if (useRequestedBounds) {
89 diffusionField->setSolveBounds(minIndex, maxIndex);
90 deformationField->setSolveBounds(minIndex, maxIndex);
92 diffusionField->clearSolveBounds();
93 deformationField->clearSolveBounds();
101 const auto oxParams = diffusionField->getParameters();
102 const bool stressFeedback = oxParams.reactionActivationVolume != 0.;
103 const bool pressureFeedback = oxParams.diffusionActivationVolume != 0.;
104 const T thermal =
T(1.380649e-23) * std::max(
T(oxParams.temperature),
T(1));
105 const std::size_t stride = stressFeedback ? 10 : 1;
106 std::vector<IndexType> nodeIndices;
107 std::vector<T> p_raw;
108 std::vector<T> p_km1, p_k;
111 struct CouplingIterateReset {
112 SmartPointer<OxidationDeformation<T, D>> field;
113 ~CouplingIterateReset() { field->setCouplingIterate(
false); }
114 } couplingIterateReset{deformationField};
116 for (; iterations < parameters.maxIterations; ++iterations) {
117 VIENNACORE_LOG_DEBUG(
"OxidationModel: coupling iteration " +
118 std::to_string(iterations + 1) +
"/" +
119 std::to_string(parameters.maxIterations) +
120 " starting diffusion solve");
121 diffusionField->apply();
122 VIENNACORE_LOG_DEBUG(
123 "OxidationModel: diffusion solve complete, nodes=" +
124 std::to_string(diffusionField->getNumberOfSolutionNodes()) +
125 ", iterations=" + std::to_string(diffusionField->getIterations()) +
126 ", residual=" + std::to_string(diffusionField->getResidual()));
127 if (!diffusionField->lastSolveConverged() ||
128 !diffusionField->hasFiniteConcentrationField()) {
129 residual = std::numeric_limits<T>::infinity();
131 "diffusion solve failed (residual=" +
132 std::to_string(diffusionField->getNormalizedResidual()) +
134 std::to_string(diffusionField->getParameters().tolerance) +
")";
135 Logger::getInstance()
136 .addWarning(
"OxidationModel: " + failureReason_ +
".")
141 VIENNACORE_LOG_DEBUG(
"OxidationModel: coupling iteration " +
142 std::to_string(iterations + 1) +
"/" +
143 std::to_string(parameters.maxIterations) +
144 " starting deformation solve");
145 if (stressFeedback || pressureFeedback)
146 deformationField->setCouplingIterate(
true);
149 deformationField->apply();
151 VIENNACORE_LOG_DEBUG(
152 "OxidationModel: deformation solve complete, nodes=" +
153 std::to_string(deformationField->getNumberOfSolutionNodes()) +
154 ", iterations=" + std::to_string(deformationField->getIterations()) +
155 ", residual=" + std::to_string(deformationField->getResidual()));
156 if (!deformationField->lastSolveConverged() ||
157 !deformationField->hasFiniteSolution()) {
158 residual = std::numeric_limits<T>::infinity();
160 "deformation solve failed (mechanics=" +
161 std::to_string(deformationField->getResidual()) +
", continuity=" +
162 std::to_string(deformationField->getLastContinuityResidual()) +
164 std::to_string(deformationField->getLastStokesResidual()) +
")";
165 VIENNACORE_LOG_WARNING(
"OxidationModel: " + failureReason_ +
".");
168 if (Logger::hasTiming())
169 Logger::getInstance()
170 .addTiming(
" deformation(couplingIter=" +
171 std::to_string(iterations + 1) +
")",
176 if (iterations == 0) {
179 deformationField->forEachSolutionNode(
180 [&](
const IndexType &idx,
T p,
const std::array<T, 9> &stress) {
181 nodeIndices.push_back(idx);
184 p_raw.insert(p_raw.end(), stress.begin(), stress.end());
188 deformationField->forEachSolutionNode(
189 [&](
const IndexType &,
T p,
const std::array<T, 9> &stress) {
192 for (
const T component : stress)
193 p_raw[ii++] = component;
196 const std::size_t n = p_raw.size();
202 if (iterations == 0 && p_k.empty()) {
204 for (
const auto &idx : nodeIndices) {
206 std::array<T, 9> stress;
207 diffusionField->getFeedback(idx, pressure, stress);
208 p_k.push_back(pressure);
210 p_k.insert(p_k.end(), stress.begin(), stress.end());
213 if (!std::all_of(p_raw.begin(), p_raw.end(),
214 [](
T value) { return std::isfinite(value); })) {
215 residual = std::numeric_limits<T>::infinity();
217 "deformation pressure feedback produced non-finite values";
218 VIENNACORE_LOG_WARNING(
"OxidationModel: " + failureReason_ +
".");
226 T aitkenTheta =
T(1);
227 if (p_k.size() == n && p_km1.size() == n) {
228 T dot_Fkm1_dF =
T(0), dot_dF_dF =
T(0);
229 for (std::size_t i = 0; i < n; ++i) {
230 const T Fkm1 = p_k[i] - p_km1[i];
231 const T Fk = p_raw[i] - p_k[i];
232 const T dF = Fk - Fkm1;
233 dot_Fkm1_dF += Fkm1 * dF;
234 dot_dF_dF += dF * dF;
236 if (dot_dF_dF >
T(1e-100))
238 std::max(
T(0.1), std::min(
T(1.0), -dot_Fkm1_dF / dot_dF_dF));
249 std::vector<T> p_blended(n);
250 T maxChange =
T(0), maxPressure =
T(0);
251 for (std::size_t i = 0; i < n; ++i) {
252 const T oldP = p_k.empty() ?
T(0) : p_k[i];
253 p_blended[i] = (
T(1) - aitkenTheta) * oldP + aitkenTheta * p_raw[i];
254 if (i % stride == 0) {
255 maxChange = std::max(maxChange, std::abs(p_blended[i] - oldP));
256 maxPressure = std::max(maxPressure, std::abs(p_blended[i]));
260 if (pressureFeedback)
263 std::max(maxPressure,
264 thermal / std::abs(
T(oxParams.diffusionActivationVolume)));
265 if (stressFeedback) {
267 candidate.reserve(nodeIndices.size());
268 for (std::size_t i = 0; i < nodeIndices.size(); ++i) {
269 std::array<T, 9> stress;
270 std::copy_n(p_blended.begin() + i * stride + 1, 9, stress.begin());
274 const auto &used = diffusionField->getInterfaceNormalStress();
275 const T unstressed = -
T(oxParams.referencePressure);
276 T maxStressChange =
T(0), maxStress =
T(0);
277 for (std::size_t i = 0; i < next.size() && i < used.size(); ++i) {
278 if (!std::isfinite(next[i]) && !std::isfinite(used[i]))
280 const T a = std::isfinite(next[i]) ? next[i] : unstressed;
281 const T b = std::isfinite(used[i]) ? used[i] : unstressed;
282 maxStressChange = std::max(maxStressChange, std::abs(a - b));
283 maxStress = std::max(maxStress, std::abs(a));
290 std::abs(
T(oxParams.reactionActivationVolume))));
298 if (residual < parameters.tolerance) {
301 deformationField->setCouplingIterate(
false);
302 if (stressFeedback || pressureFeedback)
303 deformationField->apply();
308 for (std::size_t i = 0; i < nodeIndices.size(); ++i) {
309 diffusionField->setPressure(nodeIndices[i], p_blended[i * stride]);
310 if (stressFeedback) {
311 std::array<T, 9> stress;
312 std::copy_n(p_blended.begin() + i * stride + 1, 9, stress.begin());
313 diffusionField->setStress(nodeIndices[i], stress);
318 p_km1 = p_k.empty() ? std::vector<T>(n,
T(0)) : std::move(p_k);
321 VIENNACORE_LOG_DEBUG(
322 "OxidationModel: coupling iteration " +
323 std::to_string(iterations + 1) +
324 " pressure-feedback residual=" + std::to_string(residual));
325 if (residual < parameters.tolerance)
328 const unsigned completedIterations =
329 std::min(iterations + 1, parameters.maxIterations);
330 VIENNACORE_LOG_DEBUG(
"OxidationModel: coupled solve complete, iterations=" +
331 std::to_string(completedIterations) +
332 ", residual=" + std::to_string(residual));
333 converged_ = std::isfinite(residual) && residual <= parameters.tolerance;
335 VIENNACORE_LOG_WARNING(
336 "OxidationModel: pressure-concentration coupling did not "
338 std::to_string(completedIterations) +
339 " iterations (residual=" + std::to_string(residual) +
340 ", tolerance=" + std::to_string(parameters.tolerance) +
341 "). Consider increasing maxIterations or relaxation.");