ViennaLS
Loading...
Searching...
No Matches
lsOxidation.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <lsAdvect.hpp>
6#include <lsInterior.hpp>
8
9#include <algorithm>
10#include <cmath>
11#include <exception>
12#include <iostream>
13
14#include <limits>
15#include <optional>
16#include <stdexcept>
17#include <string>
18#include <unordered_map>
19#include <vcTimer.hpp>
20
21namespace viennals {
22
23using namespace viennacore;
24
33
34template <class T>
35std::optional<T> findLOCOSInterfaceY(SmartPointer<Domain<T, 2>> levelSet,
36 viennahrle::IndexType i,
37 viennahrle::IndexType jMin,
38 viennahrle::IndexType jMax) {
39 using ConstIterator =
40 viennahrle::ConstSparseIterator<typename Domain<T, 2>::DomainType>;
41 ConstIterator it(levelSet->getDomain());
42
43 const T gridDelta = levelSet->getGrid().getGridDelta();
44 viennahrle::Index<2> previousIndex{i, jMin};
45 it.goToIndices(previousIndex);
46 T previous = it.getValue();
47
48 for (auto j = jMin + 1; j <= jMax; ++j) {
49 viennahrle::Index<2> currentIndex{i, j};
50 it.goToIndices(currentIndex);
51 const T current = it.getValue();
52 if ((previous <= 0. && current >= 0.) ||
53 (previous >= 0. && current <= 0.)) {
54 const T denominator = std::abs(previous) + std::abs(current);
55 const T fraction = denominator > std::numeric_limits<T>::epsilon()
56 ? std::abs(previous) / denominator
57 : 0.;
58 return (static_cast<T>(j - 1) + fraction) * gridDelta;
59 }
60 previous = current;
61 }
62
63 return std::nullopt;
64}
65
72template <class T>
74 SmartPointer<Domain<T, 2>> siInitial, SmartPointer<Domain<T, 2>> siAfter,
75 SmartPointer<Domain<T, 2>> ambientInitial,
76 SmartPointer<Domain<T, 2>> ambientAfter, T xMin, T xMax,
77 viennahrle::IndexType jMin, viennahrle::IndexType jMax,
78 T expansionCoefficient) {
80 const T gridDelta = siInitial->getGrid().getGridDelta();
81 const auto iMin =
82 static_cast<viennahrle::IndexType>(std::ceil(xMin / gridDelta));
83 const auto iMax =
84 static_cast<viennahrle::IndexType>(std::floor(xMax / gridDelta));
85
86 for (auto i = iMin; i <= iMax; ++i) {
87 const auto si0 = findLOCOSInterfaceY<T>(siInitial, i, jMin, jMax);
88 const auto si1 = findLOCOSInterfaceY<T>(siAfter, i, jMin, jMax);
89 const auto amb0 = findLOCOSInterfaceY<T>(ambientInitial, i, jMin, jMax);
90 const auto amb1 = findLOCOSInterfaceY<T>(ambientAfter, i, jMin, jMax);
91 if (!si0 || !si1 || !amb0 || !amb1)
92 continue;
93
94 result.siliconRecession += std::max(*si0 - *si1, T(0.)) * gridDelta;
95 result.ambientLift += std::max(*amb1 - *amb0, T(0.)) * gridDelta;
96 ++result.samples;
97 }
98
99 result.expectedAmbientLift =
100 result.siliconRecession * (expansionCoefficient - T(1.));
101 if (result.siliconRecession > 0.)
102 result.ambientLiftRatio = result.ambientLift / result.siliconRecession;
103 if (result.expectedAmbientLift > 0.)
104 result.relativeError =
105 std::abs(result.ambientLift - result.expectedAmbientLift) /
106 result.expectedAmbientLift;
107 return result;
108}
109
151template <class T, int D> class Oxidation {
152 using IndexType = viennahrle::Index<D>;
153
154 SmartPointer<Domain<T, D>> siInterface = nullptr;
155 SmartPointer<Domain<T, D>> ambientInterface = nullptr;
156 SmartPointer<Domain<T, D>> maskInterface = nullptr;
157
158 OxidationParameters oxidationParams;
159 OxidationDeformationParameters deformationParams;
160 OxidationCouplingParameters couplingParams;
161 OxidationMaskParameters maskParams;
162
165 static constexpr int maskInteriorSign = -1;
166 unsigned maskCouplingIterations = 8;
167 T maskCouplingTolerance = 2.e-2;
168 unsigned lastMaskCouplingIterations = 0;
169 T lastMaskCouplingResidual = std::numeric_limits<T>::max();
170
171 IndexType diffusionMinIndex{};
172 IndexType diffusionMaxIndex{};
173 bool diffusionBoundsSet = false;
174
175 IndexType maskBendingMinIndex{};
176 IndexType maskBendingMaxIndex{};
177 bool maskBendingBoundsSet = false;
178
179 // Populated by apply(); available for diagnostics afterwards.
180 SmartPointer<OxidationDiffusion<T, D>> diffusionField;
181 SmartPointer<OxidationDeformation<T, D>> deformationField;
182 SmartPointer<OxidationMaskBending<T, D>> maskBendingField;
183 T lastMaxVelocity_ = T(0);
184
185 GpuMode gpuMode_ = GpuMode::Cpu;
186 GpuPreconditioner gpuPreconditioner_ = GpuPreconditioner::Jacobi;
187 std::unordered_map<std::size_t, T> concentrationCache_;
188
189public:
190 const std::unordered_map<std::size_t, T> &getConcentrationCache() const {
191 return concentrationCache_;
192 }
193 void setConcentrationCache(std::unordered_map<std::size_t, T> cache) {
194 concentrationCache_ = std::move(cache);
195 }
196
197 Oxidation() = default;
198
200 // Break the shared_ptr cycle between
201 // OxidationDeformation::maskVelocityField and
202 // OxidationMaskBending::deformationField so both reach ref-count 0 when the
203 // SmartPointer members are destroyed in reverse declaration order.
204 if (deformationField)
205 deformationField->clearMaskVelocityField();
206 }
207
208 Oxidation(SmartPointer<Domain<T, D>> passedSiInterface,
209 SmartPointer<Domain<T, D>> passedAmbientInterface,
210 SmartPointer<Domain<T, D>> passedMaskInterface = nullptr)
211 : siInterface(passedSiInterface),
212 ambientInterface(passedAmbientInterface),
213 maskInterface(passedMaskInterface) {}
214
215 template <class... Args> static auto New(Args &&...args) {
216 return SmartPointer<Oxidation>::New(std::forward<Args>(args)...);
217 }
218
219 void setGpuMode(GpuMode mode) { gpuMode_ = mode; }
221 gpuPreconditioner_ = preconditioner;
222 }
223
224 void setSiInterface(SmartPointer<Domain<T, D>> si) { siInterface = si; }
225 void setAmbientInterface(SmartPointer<Domain<T, D>> ambient) {
226 ambientInterface = ambient;
227 }
228 void setMaskInterface(SmartPointer<Domain<T, D>> mask) {
229 maskInterface = mask;
230 }
231
233 oxidationParams = params;
234 }
236 deformationParams = params;
237 }
239 couplingParams = params;
240 }
242 maskParams = params;
243 }
244
246 void setSpatialScheme(SpatialSchemeEnum scheme) { spatialScheme = scheme; }
247
249 void setTemporalScheme(TemporalSchemeEnum scheme) { temporalScheme = scheme; }
250
251 void setMaskCouplingIterations(unsigned iterations) {
252 maskCouplingIterations = std::max(1u, iterations);
253 }
254
255 void setMaskCouplingTolerance(T tolerance) {
256 maskCouplingTolerance = std::max(tolerance, T(0));
257 }
258
262 void setSolveBounds(const IndexType &minIndex, const IndexType &maxIndex) {
263 diffusionMinIndex = minIndex;
264 diffusionMaxIndex = maxIndex;
265 diffusionBoundsSet = true;
266 }
267
269 void setMaskBendingBounds(const IndexType &minIndex,
270 const IndexType &maxIndex) {
271 maskBendingMinIndex = minIndex;
272 maskBendingMaxIndex = maxIndex;
273 maskBendingBoundsSet = true;
274 }
275
277 SmartPointer<OxidationDiffusion<T, D>> getDiffusionField() const {
278 return diffusionField;
279 }
280
282 SmartPointer<OxidationDeformation<T, D>> getDeformationField() const {
283 return deformationField;
284 }
285
287 SmartPointer<OxidationMaskBending<T, D>> getMaskBendingField() const {
288 return maskBendingField;
289 }
290
291 unsigned getMaskCouplingIterations() const {
292 return lastMaskCouplingIterations;
293 }
294
295 T getMaskCouplingResidual() const { return lastMaskCouplingResidual; }
296
298 T getLastMaxVelocity() const { return lastMaxVelocity_; }
299
301 void apply(T advectionTime) { applyImpl(advectionTime, std::nullopt); }
302
304 T applyCFLLimited(T requestedTime, T cflFactor) {
305 return applyImpl(requestedTime, std::clamp(cflFactor, T(1e-3), T(0.499)));
306 }
307
308private:
309 T applyImpl(T requestedTime, std::optional<T> cflFactor) {
310 if (siInterface == nullptr || ambientInterface == nullptr) {
311 Logger::getInstance()
312 .addError("Oxidation: Si or ambient interface is null.")
313 .print();
314 return T(0);
315 }
316
317 if (requestedTime <= T(0))
318 return T(0);
319
320 Timer<> tStep;
321 tStep.start();
322
323 const bool hasMask = (maskInterface != nullptr);
324 const std::string prefix = hasMask ? "LOCOS" : "Oxidation";
325
326 VIENNACORE_LOG_INFO(prefix + ": starting time step, requested_dt=" +
327 std::to_string(requestedTime) + " hr");
328
329 const auto baseConcentrationCache = concentrationCache_;
330 std::string lastFieldFailureReason;
331 bool lastFailureWasMaskFixedPoint = false;
332 T adaptiveMaskRelaxationScale = T(1);
333
334 auto solveFields = [&](T stressTimeStep, bool logCouplingResult) -> bool {
335 auto rejectSolve = [&](const std::string &reason) {
336 lastFieldFailureReason = reason;
337 return false;
338 };
339
340 auto validateCoupledModel = [&](const SmartPointer<OxidationModel<T, D>>
341 &model) {
342 if (!model->hasConverged()) {
343 const auto reason = model->getFailureReason();
344 return rejectSolve(
345 reason.empty()
346 ? "pressure-concentration coupling failed (residual=" +
347 std::to_string(model->getResidual()) + ", tolerance=" +
348 std::to_string(couplingParams.tolerance) + ")"
349 : reason);
350 }
351 if (!diffusionField->lastSolveConverged() ||
352 !diffusionField->hasFiniteConcentrationField()) {
353 return rejectSolve(
354 "diffusion solve failed (residual=" +
355 std::to_string(diffusionField->getNormalizedResidual()) +
356 ", tolerance=" + std::to_string(oxidationParams.tolerance) + ")");
357 }
358 if (!deformationField->lastSolveConverged() ||
359 !deformationField->hasFiniteSolution()) {
360 return rejectSolve(
361 "deformation solve failed (mechanics=" +
362 std::to_string(deformationField->getResidual()) + ", pressure=" +
363 std::to_string(deformationField->getLastPressureResidual()) +
364 ", stokes=" +
365 std::to_string(deformationField->getLastStokesResidual()) + ")");
366 }
367 return true;
368 };
369
370 auto validateMaskSolve = [&]() {
371 if (maskBendingField == nullptr)
372 return true;
373 const T maskResidual = maskBendingField->getResidual();
374 if (!std::isfinite(maskResidual) ||
375 maskResidual > maskParams.tolerance) {
376 return rejectSolve("mask traction solve failed (residual=" +
377 std::to_string(maskResidual) + ", tolerance=" +
378 std::to_string(maskParams.tolerance) + ")");
379 }
380 const T couplingResidual =
381 maskBendingField->getLastApplyVelocityChange();
382 if (!std::isfinite(couplingResidual))
383 return rejectSolve(
384 "mask velocity coupling produced non-finite values");
385 return true;
386 };
387
388 lastFieldFailureReason.clear();
389 lastFailureWasMaskFixedPoint = false;
390 auto stepDeformationParams = deformationParams;
391 stepDeformationParams.stressTimeStep = stressTimeStep;
392
393 // Break the shared_ptr cycle (OxidationDeformation ↔
394 // OxidationMaskBending) left by the previous solveFields call so the old
395 // objects are freed when deformationField and maskBendingField are
396 // replaced below.
397 if (deformationField)
398 deformationField->clearMaskVelocityField();
399
400 // --- Coupled diffusion + deformation solve ---
401
402 diffusionField = OxidationDiffusion<T, D>::New(
403 siInterface, ambientInterface, oxidationParams);
404 diffusionField->setConcentrationCache(baseConcentrationCache);
405 diffusionField->setGpuMode(gpuMode_);
406 diffusionField->setGpuPreconditioner(gpuPreconditioner_);
407 if (hasMask)
408 diffusionField->setMaskInterface(maskInterface, maskInteriorSign);
409
410 deformationField = OxidationDeformation<T, D>::New(
411 siInterface, ambientInterface, diffusionField, oxidationParams,
412 stepDeformationParams);
413 deformationField->setGpuMode(gpuMode_);
414 deformationField->setGpuPreconditioner(gpuPreconditioner_);
415 if (hasMask)
416 deformationField->setMaskInterface(maskInterface, maskInteriorSign);
417
418 auto coupledModel = OxidationModel<T, D>::New(
419 diffusionField, deformationField, couplingParams);
420 if (diffusionBoundsSet)
421 coupledModel->setSolveBounds(diffusionMinIndex, diffusionMaxIndex);
422 VIENNACORE_LOG_DEBUG(
423 prefix + ": solving coupled diffusion/deformation field for dt=" +
424 std::to_string(stressTimeStep) + " hr");
425 Timer<> tCoupled;
426 tCoupled.start();
427 coupledModel->apply();
428 tCoupled.finish();
429 if (Logger::hasTiming())
430 Logger::getInstance().addTiming(" coupled(iter=1)", tCoupled).print();
431 VIENNACORE_LOG_DEBUG(prefix +
432 ": coupled diffusion/deformation solve complete");
433 if (!validateCoupledModel(coupledModel))
434 return false;
435
436 if (hasMask) {
437 // --- Mask bending solve ---
438 auto stepMaskParams = maskParams;
439 stepMaskParams.stressTimeStep = stressTimeStep;
440 stepMaskParams.relaxation = std::clamp(
441 maskParams.relaxation * adaptiveMaskRelaxationScale, T(0.01), T(1));
442 maskBendingField = OxidationMaskBending<T, D>::New(
443 deformationField, maskInterface, stepMaskParams, maskInteriorSign);
444 maskBendingField->setAmbientInterface(ambientInterface,
445 maskInteriorSign);
446 if (maskBendingBoundsSet)
447 maskBendingField->setSolveBounds(maskBendingMinIndex,
448 maskBendingMaxIndex);
449 VIENNACORE_LOG_DEBUG(prefix + ": solving mask bending field");
450 Timer<> tMask;
451 tMask.start();
452 try {
453 maskBendingField->apply();
454 } catch (const std::exception &e) {
455 tMask.finish();
456 return rejectSolve("mask bending solve error: " +
457 std::string(e.what()));
458 }
459 tMask.finish();
460 if (Logger::hasTiming())
461 Logger::getInstance()
462 .addTiming(" maskBending(iter=1)", tMask)
463 .print();
464 if (!validateMaskSolve())
465 return false;
466 T initialRes = maskBendingField->getLastApplyVelocityChange();
467 VIENNACORE_LOG_DEBUG(
468 prefix + ": mask bending solve complete, residual=" +
469 (initialRes >= std::numeric_limits<T>::max() * T(0.99)
470 ? std::string("initial")
471 : std::to_string(initialRes)));
472
473 lastMaskCouplingIterations = 1;
474 lastMaskCouplingResidual =
475 maskBendingField->getLastApplyVelocityChange();
476 deformationField->setMaskVelocityField(maskBendingField);
477 for (unsigned iteration = 1; iteration < maskCouplingIterations;
478 ++iteration) {
479 deformationField->setMaskVelocityField(maskBendingField);
480 VIENNACORE_LOG_DEBUG(prefix + ": coupling iteration " +
481 std::to_string(iteration + 1) +
482 " solving coupled field");
483 Timer<> tIterCoupled, tIterMask;
484 tIterCoupled.start();
485 coupledModel->apply();
486 tIterCoupled.finish();
487 if (!validateCoupledModel(coupledModel))
488 return false;
489 VIENNACORE_LOG_DEBUG(prefix + ": coupling iteration " +
490 std::to_string(iteration + 1) +
491 " solving mask field");
492 tIterMask.start();
493 try {
494 maskBendingField->apply();
495 } catch (const std::exception &e) {
496 tIterMask.finish();
497 return rejectSolve("mask bending solve error at iteration " +
498 std::to_string(iteration + 1) + ": " + e.what());
499 }
500 tIterMask.finish();
501 if (!validateMaskSolve())
502 return false;
503 if (Logger::hasTiming())
504 Logger::getInstance()
505 .addTiming(" coupled(iter=" + std::to_string(iteration + 1) +
506 ")",
507 tIterCoupled)
508 .addTiming(
509 " maskBending(iter=" + std::to_string(iteration + 1) + ")",
510 tIterMask)
511 .print();
512 lastMaskCouplingIterations = iteration + 1;
513 lastMaskCouplingResidual =
514 maskBendingField->getLastApplyVelocityChange();
515 VIENNACORE_LOG_DEBUG(
516 prefix + ": coupling iteration " + std::to_string(iteration + 1) +
517 " residual=" + std::to_string(lastMaskCouplingResidual));
518 if (lastMaskCouplingResidual <= maskCouplingTolerance)
519 break;
520 }
521 const T maskAbsoluteDisplacement =
522 maskBendingField->getLastApplyAbsoluteVelocityChange() *
523 stressTimeStep;
524 // Max physical displacement per step from the mask velocity field.
525 // Independent of coupling oscillation amplitude — the oscillation check
526 // (maskAbsoluteDisplacement) measures how much the velocity CHANGES
527 // between iterations; this measures how far the surface actually moves.
528 // When the active-set oscillates at a fixed amplitude, reducing dt
529 // does not reduce maskAbsoluteDisplacement, but it does reduce this.
530 T maxMaskVelocity = T(0);
531 for (unsigned d = 0; d < D; ++d)
532 maxMaskVelocity =
533 std::max(maxMaskVelocity, maskBendingField->getDissipationAlpha(
534 static_cast<int>(d), -1, {}));
535 const T maskMaxDisplacement = maxMaskVelocity * stressTimeStep;
536 const T maskDisplacementTolerance =
537 maskCouplingTolerance * siInterface->getGrid().getGridDelta();
538 const bool maskCouplingConverged =
539 lastMaskCouplingResidual <= maskCouplingTolerance ||
540 (std::isfinite(maskAbsoluteDisplacement) &&
541 maskAbsoluteDisplacement <= maskDisplacementTolerance) ||
542 (std::isfinite(maskMaxDisplacement) &&
543 maskMaxDisplacement <= maskDisplacementTolerance);
544 if (maskCouplingConverged) {
545 VIENNACORE_LOG_INFO(
546 prefix + ": mask/oxide coupling converged in " +
547 std::to_string(lastMaskCouplingIterations) +
548 " iterations (residual=" +
549 std::to_string(lastMaskCouplingResidual) +
550 ", displacement=" + std::to_string(maskAbsoluteDisplacement) +
551 " um, maxDisplacement=" + std::to_string(maskMaxDisplacement) +
552 " um)");
553 } else if (logCouplingResult) {
554 VIENNACORE_LOG_WARNING(
555 prefix +
556 ": mask/oxide coupling did not converge "
557 "after " +
558 std::to_string(lastMaskCouplingIterations) +
559 " iterations (residual=" +
560 std::to_string(lastMaskCouplingResidual) +
561 ", displacement=" + std::to_string(maskAbsoluteDisplacement) +
562 " um" + ", tolerance=" + std::to_string(maskCouplingTolerance) +
563 "). Consider increasing maskCouplingIterations.");
564 }
565 if (!maskCouplingConverged) {
566 lastFailureWasMaskFixedPoint = true;
567 return rejectSolve(
568 "mask/oxide coupling failed (residual=" +
569 std::to_string(lastMaskCouplingResidual) +
570 ", displacement=" + std::to_string(maskAbsoluteDisplacement) +
571 " um, maxDisplacement=" + std::to_string(maskMaxDisplacement) +
572 " um" + ", tolerance=" + std::to_string(maskCouplingTolerance) +
573 ", relaxation=" + std::to_string(stepMaskParams.relaxation) +
574 ")");
575 }
576 return true;
577 } else {
578 maskBendingField = nullptr;
579 }
580
581 return true;
582 };
583
584 auto makeAmbientVelocity = [&]() -> SmartPointer<VelocityField<T>> {
585 if (hasMask) {
587 deformationField, maskBendingField, maskInterface, ambientInterface,
588 maskInteriorSign);
589 }
590 return deformationField;
591 };
592
593 T advectionTime = requestedTime;
594 SmartPointer<VelocityField<T>> ambientVelocity;
595
596 auto computeMaxVelocity =
597 [&](const SmartPointer<VelocityField<T>> &passedAmbientVelocity) {
598 T maxVelocity = diffusionField->getDissipationAlpha(0, -1, {});
599 for (unsigned d = 0; d < D; ++d) {
600 maxVelocity =
601 std::max(maxVelocity,
602 passedAmbientVelocity->getDissipationAlpha(d, -1, {}));
603 if (hasMask)
604 maxVelocity =
605 std::max(maxVelocity,
606 maskBendingField->getDissipationAlpha(d, -1, {}));
607 }
608 return maxVelocity;
609 };
610
611 auto cflLimitedTime = [&](T trialTime, T maxVelocity) {
612 if (maxVelocity <= std::numeric_limits<T>::epsilon())
613 return trialTime;
614 const T gridDelta = siInterface->getGrid().getGridDelta();
615 return std::min(trialTime, (*cflFactor) * gridDelta / maxVelocity);
616 };
617
618 if (cflFactor) {
619 T trialTime = requestedTime;
620 const T minTrialTime = std::max(
621 requestedTime * T(1e-10), std::numeric_limits<T>::epsilon() * T(100));
622 bool accepted = false;
623 for (unsigned attempt = 0; attempt < 16; ++attempt) {
624 const bool predictorConverged = solveFields(trialTime, false);
625 if (!predictorConverged) {
626 const bool dampMask = lastFailureWasMaskFixedPoint &&
627 adaptiveMaskRelaxationScale > T(0.051);
628 if (dampMask)
629 adaptiveMaskRelaxationScale =
630 std::max(T(0.05), adaptiveMaskRelaxationScale * T(0.5));
631 const T nextTrial = dampMask ? trialTime : trialTime * T(0.5);
632 VIENNACORE_LOG_INFO(
633 prefix +
634 ": rejecting non-converged coupled predictor "
635 "(" +
636 (lastFieldFailureReason.empty()
637 ? "mask residual=" + std::to_string(lastMaskCouplingResidual)
638 : lastFieldFailureReason) +
639 (dampMask ? ", retrying with mask relaxation scale=" +
640 std::to_string(adaptiveMaskRelaxationScale)
641 : std::string()) +
642 "), retrying with requested_dt=" + std::to_string(nextTrial) +
643 " hr");
644 trialTime = nextTrial;
645 if (trialTime < minTrialTime)
646 break;
647 continue;
648 }
649
650 ambientVelocity = makeAmbientVelocity();
651 T maxVelocity = computeMaxVelocity(ambientVelocity);
652 if (!std::isfinite(maxVelocity))
653 VIENNACORE_LOG_ERROR(prefix + ": non-finite CFL velocity estimate.");
654
655 advectionTime = cflLimitedTime(trialTime, maxVelocity);
656 VIENNACORE_LOG_INFO(
657 prefix +
658 ": CFL decision requested_dt=" + std::to_string(trialTime) +
659 " hr, actual_dt=" + std::to_string(advectionTime) +
660 " hr, max_velocity=" + std::to_string(maxVelocity) + " um/hr");
661
662 if (advectionTime < trialTime * (T(1) - T(1e-8))) {
663 const bool finalConverged = solveFields(advectionTime, false);
664 if (!finalConverged) {
665 const bool dampMask = lastFailureWasMaskFixedPoint &&
666 adaptiveMaskRelaxationScale > T(0.051);
667 if (dampMask)
668 adaptiveMaskRelaxationScale =
669 std::max(T(0.05), adaptiveMaskRelaxationScale * T(0.5));
670 const T nextTrial =
671 dampMask ? advectionTime : advectionTime * T(0.5);
672 VIENNACORE_LOG_INFO(
673 prefix +
674 ": rejecting non-converged CFL re-solve "
675 "(" +
676 (lastFieldFailureReason.empty()
677 ? "mask residual=" +
678 std::to_string(lastMaskCouplingResidual)
679 : lastFieldFailureReason) +
680 (dampMask ? ", retrying with mask relaxation scale=" +
681 std::to_string(adaptiveMaskRelaxationScale)
682 : std::string()) +
683 "), retrying with requested_dt=" + std::to_string(nextTrial) +
684 " hr");
685 trialTime = nextTrial;
686 if (trialTime < minTrialTime)
687 break;
688 continue;
689 }
690 ambientVelocity = makeAmbientVelocity();
691 maxVelocity = computeMaxVelocity(ambientVelocity);
692 if (!std::isfinite(maxVelocity))
693 VIENNACORE_LOG_ERROR(prefix +
694 ": non-finite accepted CFL velocity.");
695
696 const T verifiedTime = cflLimitedTime(advectionTime, maxVelocity);
697 if (verifiedTime < advectionTime * (T(1) - T(1e-8))) {
698 VIENNACORE_LOG_INFO(prefix +
699 ": rejecting CFL re-solve because accepted "
700 "velocity requires requested_dt=" +
701 std::to_string(verifiedTime) + " hr");
702 trialTime = verifiedTime;
703 if (trialTime < minTrialTime)
704 break;
705 continue;
706 }
707 }
708
709 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
710 accepted = true;
711 break;
712 }
713 if (!accepted) {
714 // Last resort: if the oxide solve (diffusion + deformation) is still
715 // finite — only the mask coupling diverged — freeze the mask for one
716 // minimal step so the geometry can evolve past the singular contact
717 // configuration. The mask doesn't move; the oxide advances at
718 // minTrialTime with no mask feedback this step.
719 const bool oxideFinite =
720 diffusionField && deformationField &&
721 diffusionField->hasFiniteConcentrationField() &&
722 deformationField->hasFiniteSolution();
723 if (hasMask && oxideFinite) {
724 VIENNACORE_LOG_WARNING(
725 prefix +
726 ": all CFL attempts exhausted; freezing mask for "
727 "one step at dt=" +
728 std::to_string(minTrialTime) +
729 " hr (last failure: " + lastFieldFailureReason +
730 "). Consider increasing maskReferenceViscosity or "
731 "maskCouplingIterations.");
732 maskBendingField = nullptr; // skip mask advection this step
733 ambientVelocity = makeAmbientVelocity();
734 advectionTime = minTrialTime;
735 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
736 } else {
737 VIENNACORE_LOG_ERROR(prefix +
738 ": unable to find a converged CFL-limited step" +
739 (lastFieldFailureReason.empty()
740 ? std::string(".")
741 : std::string(" (last failure: ") +
742 lastFieldFailureReason + ")."));
743 }
744 }
745 } else {
746 const bool fieldsConverged = solveFields(requestedTime, true);
747 if (!fieldsConverged)
748 VIENNACORE_LOG_ERROR(
749 prefix + ": coupled solve failed" +
750 (lastFieldFailureReason.empty()
751 ? std::string(".")
752 : std::string(" (") + lastFieldFailureReason + ")."));
753 ambientVelocity = makeAmbientVelocity();
754 }
755
756 concentrationCache_ = diffusionField->getConcentrationCache();
757
758 diffusionField->markSolved();
759
760 // Pre-advection clip: keep oxide outside the mask body (LOCOS only).
761 if (hasMask)
762 BooleanOperation<T, D>(ambientInterface, maskInterface,
764 .apply();
765
766 diffusionField->writePersistentFields();
767 deformationField->writeFieldsToLevelSet();
768 if (hasMask && maskBendingField)
769 maskBendingField->writeFieldsToLevelSet();
770
771 if (hasMask && maskBendingField)
772 maskBendingField->finalizeElasticAdvectionVelocity();
773
774 auto advect = [&](SmartPointer<Domain<T, D>> levelSet,
775 SmartPointer<VelocityField<T>> velocityField) {
776 Advect<T, D> adv;
777 adv.insertNextLevelSet(levelSet);
778 adv.setVelocityField(velocityField);
779 adv.setSpatialScheme(spatialScheme);
780 adv.setTemporalScheme(temporalScheme);
781 adv.setAdvectionTime(advectionTime);
782 adv.apply();
783 };
784
785 Timer<> tAdvect;
786 tAdvect.start();
787 advect(ambientInterface, ambientVelocity);
788 advect(siInterface, diffusionField);
789 if (hasMask && maskBendingField)
790 advect(maskInterface, maskBendingField);
791 tAdvect.finish();
792 VIENNACORE_LOG_TIMING(std::string(" advection(") + (hasMask ? "3" : "2") +
793 " surfaces)",
794 tAdvect);
795
796 // Post-advection clip: remove oxide that grew into the mask (LOCOS only).
797 // Mask gets Interior fill first so the BooleanOp has accurate φ_mask values
798 // at points inside the mask region.
799 if (hasMask) {
800 Interior<T, D>(maskInterface).apply();
801 BooleanOperation<T, D>(ambientInterface, maskInterface,
803 .apply();
804 // Re-write mask velocity with the Interior-filled HRLE for the same
805 // reason as the oxide re-write below — lsAdvect left only the narrow
806 // band, so pointData is smaller than the post-Interior HRLE.
807 if (maskBendingField)
808 maskBendingField->writeFieldsToLevelSet();
809 }
810 {
811 Interior<T, D> fill(ambientInterface);
812 fill.setGuide(siInterface); // stop fill at Si surface
813 fill.apply();
814 }
815
816 // Re-write persistent fields (concentration, pressure) now that the HRLE
817 // has interior points. The first writePersistentFields() above only
818 // covered the narrow band; after lsAdvect the new HRLE is again a narrow
819 // band stripped of interior data. By re-writing here we ensure that the
820 // next outer step's buildNodes() can warm-start ALL oxide nodes — not just
821 // the surface band — when it reads back from pointData.
822 diffusionField->writePersistentFields();
823 deformationField->writeFieldsToLevelSet();
824
825 VIENNACORE_LOG_INFO(prefix + ": time step complete, actual_dt=" +
826 std::to_string(advectionTime) + " hr");
827
828 tStep.finish();
829 VIENNACORE_LOG_TIMING("── step total", tStep);
830
831 return advectionTime;
832 }
833};
834
835} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
static auto New(Args &&...args)
Definition lsOxidationMask.hpp:2266
static auto New(Args &&...args)
Definition lsOxidationDeformation.hpp:211
static auto New(Args &&...args)
Definition lsOxidationDiffusion.hpp:208
static SmartPointer< OxidationMaskBending > New(SmartPointer< OxidationDeformation< T, D > > passedDeformation, OxidationMaskParameters passedParameters={})
Definition lsOxidationMask.hpp:206
static auto New(Args &&...args)
Definition lsOxidationModel.hpp:46
T getMaskCouplingResidual() const
Definition lsOxidation.hpp:295
void setMaskCouplingTolerance(T tolerance)
Definition lsOxidation.hpp:255
void setCouplingParameters(OxidationCouplingParameters params)
Definition lsOxidation.hpp:238
void setGpuPreconditioner(GpuPreconditioner preconditioner)
Definition lsOxidation.hpp:220
static auto New(Args &&...args)
Definition lsOxidation.hpp:215
SmartPointer< OxidationDeformation< T, D > > getDeformationField() const
Return the deformation field populated by the most recent apply() call.
Definition lsOxidation.hpp:282
void setDeformationParameters(OxidationDeformationParameters params)
Definition lsOxidation.hpp:235
T applyCFLLimited(T requestedTime, T cflFactor)
Execute one CFL-limited oxidation step; returns the actual time advanced.
Definition lsOxidation.hpp:304
const std::unordered_map< std::size_t, T > & getConcentrationCache() const
Definition lsOxidation.hpp:190
void setSolveBounds(const IndexType &minIndex, const IndexType &maxIndex)
Set the Cartesian index bounding box for the diffusion and deformation solves. If not set,...
Definition lsOxidation.hpp:262
void setSpatialScheme(SpatialSchemeEnum scheme)
Set the spatial integration scheme for all advections.
Definition lsOxidation.hpp:246
void setMaskInterface(SmartPointer< Domain< T, D > > mask)
Definition lsOxidation.hpp:228
void setMaskParameters(OxidationMaskParameters params)
Definition lsOxidation.hpp:241
unsigned getMaskCouplingIterations() const
Definition lsOxidation.hpp:291
void setMaskBendingBounds(const IndexType &minIndex, const IndexType &maxIndex)
Set the Cartesian index bounding box for the mask bending solve (LOCOS).
Definition lsOxidation.hpp:269
void setGpuMode(GpuMode mode)
Definition lsOxidation.hpp:219
~Oxidation()
Definition lsOxidation.hpp:199
void setSiInterface(SmartPointer< Domain< T, D > > si)
Definition lsOxidation.hpp:224
void setMaskCouplingIterations(unsigned iterations)
Definition lsOxidation.hpp:251
SmartPointer< OxidationDiffusion< T, D > > getDiffusionField() const
Return the diffusion field populated by the most recent apply() call.
Definition lsOxidation.hpp:277
SmartPointer< OxidationMaskBending< T, D > > getMaskBendingField() const
Return the mask bending field (null when no mask is set).
Definition lsOxidation.hpp:287
void setAmbientInterface(SmartPointer< Domain< T, D > > ambient)
Definition lsOxidation.hpp:225
Oxidation(SmartPointer< Domain< T, D > > passedSiInterface, SmartPointer< Domain< T, D > > passedAmbientInterface, SmartPointer< Domain< T, D > > passedMaskInterface=nullptr)
Definition lsOxidation.hpp:208
void setTemporalScheme(TemporalSchemeEnum scheme)
Set the temporal integration scheme for all advections.
Definition lsOxidation.hpp:249
void setOxidationParameters(OxidationParameters params)
Definition lsOxidation.hpp:232
void apply(T advectionTime)
Execute one oxidation time step of duration advectionTime.
Definition lsOxidation.hpp:301
T getLastMaxVelocity() const
Maximum interface velocity (µm/hr) from the most recent CFL-limited step.
Definition lsOxidation.hpp:298
void setConcentrationCache(std::unordered_map< std::size_t, T > cache)
Definition lsOxidation.hpp:193
float gridDelta
Definition AirGapDeposition.py:61
Definition lsAdvect.hpp:41
SpatialSchemeEnum
Enumeration for the different spatial discretization schemes used by the advection kernel.
Definition lsAdvectIntegrationSchemes.hpp:10
@ ENGQUIST_OSHER_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:11
GpuMode
Selects the BiCGSTAB back-end for the diffusion solve. GPU failures are reported and not silently fal...
Definition lsOxidationDiffusion.hpp:26
@ Cpu
Always use CPU (default).
Definition lsOxidationDiffusion.hpp:27
std::optional< T > findLOCOSInterfaceY(SmartPointer< Domain< T, 2 > > levelSet, viennahrle::IndexType i, viennahrle::IndexType jMin, viennahrle::IndexType jMax)
Definition lsOxidation.hpp:35
TemporalSchemeEnum
Enumeration for the different time integration schemes used to select the advection kernel.
Definition lsAdvectIntegrationSchemes.hpp:31
@ FORWARD_EULER
Definition lsAdvectIntegrationSchemes.hpp:32
@ RELATIVE_COMPLEMENT
Definition lsBooleanOperation.hpp:30
GpuPreconditioner
Selects the preconditioner used by the GPU BiCGSTAB solver. Jacobi matches the CPU solver's precondit...
Definition lsOxidationDiffusion.hpp:35
@ Jacobi
Definition lsOxidationDiffusion.hpp:35
LOCOSConservationDiagnostics< T > computeLOCOSOpenWindowConservation(SmartPointer< Domain< T, 2 > > siInitial, SmartPointer< Domain< T, 2 > > siAfter, SmartPointer< Domain< T, 2 > > ambientInitial, SmartPointer< Domain< T, 2 > > ambientAfter, T xMin, T xMax, viennahrle::IndexType jMin, viennahrle::IndexType jMax, T expansionCoefficient)
Measure the open-window volume balance after one 2D LOCOS step.
Definition lsOxidation.hpp:73
Definition lsOxidation.hpp:25
T ambientLiftRatio
Definition lsOxidation.hpp:29
T siliconRecession
Definition lsOxidation.hpp:26
T relativeError
Definition lsOxidation.hpp:30
unsigned samples
Definition lsOxidation.hpp:31
T ambientLift
Definition lsOxidation.hpp:27
T expectedAmbientLift
Definition lsOxidation.hpp:28
Definition lsOxidationModel.hpp:13
Parameters for the Cartesian-grid oxide deformation model.
Definition lsOxidationDeformation.hpp:18
Definition lsOxidationMask.hpp:17
Parameters for the steady oxidant diffusion model used by OxidationDiffusion.
Definition lsOxidationDiffusion.hpp:39