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