384 using IndexType = viennahrle::Index<D>;
386 SmartPointer<Domain<T, D>> siInterface =
nullptr;
387 SmartPointer<Domain<T, D>> ambientInterface =
nullptr;
388 SmartPointer<Domain<T, D>> maskInterface =
nullptr;
397 static constexpr int maskInteriorSign = -1;
403 bool maskBarrierOnly_ =
false;
405 IndexType diffusionMinIndex{};
406 IndexType diffusionMaxIndex{};
407 bool diffusionBoundsSet =
false;
410 SmartPointer<OxidationDiffusion<T, D>> diffusionField;
411 SmartPointer<OxidationDeformation<T, D>> deformationField;
412 T lastMaxVelocity_ =
T(0);
415 std::unordered_map<std::size_t, T> concentrationCache_;
420 std::optional<OxidationMaskTracker<T, D>> maskTracker_;
424 return concentrationCache_;
427 concentrationCache_ = std::move(cache);
434 SmartPointer<
Domain<T, D>> passedMaskInterface =
nullptr)
435 : siInterface(passedSiInterface),
436 ambientInterface(passedAmbientInterface),
437 maskInterface(passedMaskInterface) {}
439 template <
class... Args>
static auto New(Args &&...args) {
440 return SmartPointer<Oxidation>::New(std::forward<Args>(args)...);
447 ambientInterface = ambient;
450 maskInterface = mask;
454 oxidationParams = params;
457 deformationParams = params;
460 couplingParams = params;
478 diffusionMinIndex = minIndex;
479 diffusionMaxIndex = maxIndex;
480 diffusionBoundsSet =
true;
485 return diffusionField;
490 return deformationField;
497 void apply(
T advectionTime) { applyImpl(advectionTime, std::nullopt); }
501 return applyImpl(requestedTime, std::clamp(cflFactor,
T(1e-3),
T(0.499)));
505 T applyImpl(
T requestedTime, std::optional<T> cflFactor) {
506 if (siInterface ==
nullptr || ambientInterface ==
nullptr) {
507 Logger::getInstance()
508 .addError(
"Oxidation: Si or ambient interface is null.")
513 if (requestedTime <=
T(0))
519 const bool hasMask = (maskInterface !=
nullptr);
523 const bool monolithicMask = hasMask && !maskBarrierOnly_;
524 const std::string prefix = hasMask ?
"LOCOS" :
"Oxidation";
526 VIENNACORE_LOG_INFO(prefix +
": starting time step, requested_dt=" +
527 std::to_string(requestedTime) +
" hr");
529 const auto baseConcentrationCache = concentrationCache_;
530 const auto basePressureCache = pressureCache_;
531 const auto baseStressCache = stressCache_;
532 std::string lastFieldFailureReason;
534 auto solveFields = [&](
T stressTimeStep) ->
bool {
535 auto rejectSolve = [&](
const std::string &reason) {
536 lastFieldFailureReason = reason;
540 auto validateCoupledModel = [&](
const SmartPointer<OxidationModel<T, D>>
542 if (!model->hasConverged()) {
543 const auto reason = model->getFailureReason();
546 ?
"pressure-concentration coupling failed (residual=" +
547 std::to_string(model->getResidual()) +
", tolerance=" +
548 std::to_string(couplingParams.tolerance) +
")"
551 if (!diffusionField->lastSolveConverged() ||
552 !diffusionField->hasFiniteConcentrationField()) {
554 "diffusion solve failed (residual=" +
555 std::to_string(diffusionField->getNormalizedResidual()) +
556 ", tolerance=" + std::to_string(oxidationParams.tolerance) +
")");
558 if (!deformationField->lastSolveConverged() ||
559 !deformationField->hasFiniteSolution()) {
561 "deformation solve failed (mechanics=" +
562 std::to_string(deformationField->getResidual()) +
564 std::to_string(deformationField->getLastContinuityResidual()) +
566 std::to_string(deformationField->getLastStokesResidual()) +
")");
571 lastFieldFailureReason.clear();
572 auto stepDeformationParams = deformationParams;
573 stepDeformationParams.stressTimeStep = stressTimeStep;
574 if (monolithicMask) {
575 stepDeformationParams.nitrideYoungModulus = maskParams.youngModulus;
576 stepDeformationParams.nitridePoissonRatio = maskParams.poissonRatio;
577 stepDeformationParams.nitrideViscosity = maskParams.viscosity;
578 stepDeformationParams.nitrideShearActivationVolume =
579 maskParams.shearActivationVolume;
585 siInterface, ambientInterface, oxidationParams);
586 diffusionField->setConcentrationCache(baseConcentrationCache);
587 diffusionField->setFeedbackCache(basePressureCache, baseStressCache);
588 diffusionField->setGpuMode(gpuMode_);
590 diffusionField->setMaskInterface(maskInterface, maskInteriorSign);
593 siInterface, ambientInterface, diffusionField, oxidationParams,
594 stepDeformationParams);
595 deformationField->setGpuMode(gpuMode_);
598 if (hasMask && !maskBarrierOnly_)
599 deformationField->setMaskInterface(maskInterface, maskInteriorSign);
602 diffusionField, deformationField, couplingParams);
603 if (diffusionBoundsSet)
604 coupledModel->setSolveBounds(diffusionMinIndex, diffusionMaxIndex);
605 VIENNACORE_LOG_DEBUG(
606 prefix +
": solving coupled diffusion/deformation field for dt=" +
607 std::to_string(stressTimeStep) +
" hr");
610 coupledModel->apply();
612 if (Logger::hasTiming())
613 Logger::getInstance().addTiming(
" coupled(iter=1)", tCoupled).print();
614 VIENNACORE_LOG_DEBUG(prefix +
615 ": coupled diffusion/deformation solve complete");
616 return validateCoupledModel(coupledModel);
619 auto makeAmbientVelocity = [&]() -> SmartPointer<VelocityField<T>> {
626 return deformationField;
629 T advectionTime = requestedTime;
630 SmartPointer<VelocityField<T>> ambientVelocity;
632 auto computeMaxVelocity =
633 [&](
const SmartPointer<VelocityField<T>> &passedAmbientVelocity) {
634 T maxVelocity = diffusionField->getDissipationAlpha(0, -1, {});
637 for (
unsigned d = 0; d <
D; ++d)
639 std::max(maxVelocity,
640 passedAmbientVelocity->getDissipationAlpha(d, -1, {}));
644 auto cflLimitedTime = [&](
T trialTime,
T maxVelocity) {
645 if (maxVelocity <= std::numeric_limits<T>::epsilon())
647 const T gridDelta = siInterface->getGrid().getGridDelta();
648 return std::min(trialTime, (*cflFactor) *
gridDelta / maxVelocity);
652 T trialTime = requestedTime;
653 const T minTrialTime = std::max(
654 requestedTime *
T(1e-10), std::numeric_limits<T>::epsilon() *
T(100));
655 bool accepted =
false;
656 for (
unsigned attempt = 0; attempt < 16; ++attempt) {
657 const bool predictorConverged = solveFields(trialTime);
658 if (!predictorConverged) {
659 const T nextTrial = trialTime *
T(0.5);
661 prefix +
": rejecting non-converged coupled predictor (" +
662 lastFieldFailureReason +
"), retrying with requested_dt=" +
663 std::to_string(nextTrial) +
" hr");
664 trialTime = nextTrial;
665 if (trialTime < minTrialTime)
670 ambientVelocity = makeAmbientVelocity();
671 T maxVelocity = computeMaxVelocity(ambientVelocity);
672 if (!std::isfinite(maxVelocity))
673 VIENNACORE_LOG_ERROR(prefix +
": non-finite CFL velocity estimate.");
675 advectionTime = cflLimitedTime(trialTime, maxVelocity);
678 ": CFL decision requested_dt=" + std::to_string(trialTime) +
679 " hr, actual_dt=" + std::to_string(advectionTime) +
680 " hr, max_velocity=" + std::to_string(maxVelocity) +
" um/hr");
682 if (advectionTime < trialTime * (
T(1) -
T(1e-8))) {
683 const bool finalConverged = solveFields(advectionTime);
684 if (!finalConverged) {
685 const T nextTrial = advectionTime *
T(0.5);
687 prefix +
": rejecting non-converged CFL re-solve (" +
688 lastFieldFailureReason +
"), retrying with requested_dt=" +
689 std::to_string(nextTrial) +
" hr");
690 trialTime = nextTrial;
691 if (trialTime < minTrialTime)
695 ambientVelocity = makeAmbientVelocity();
696 maxVelocity = computeMaxVelocity(ambientVelocity);
697 if (!std::isfinite(maxVelocity))
698 VIENNACORE_LOG_ERROR(prefix +
699 ": non-finite accepted CFL velocity.");
701 const T verifiedTime = cflLimitedTime(advectionTime, maxVelocity);
702 if (verifiedTime < advectionTime * (
T(1) -
T(1e-8))) {
703 VIENNACORE_LOG_INFO(prefix +
704 ": rejecting CFL re-solve because accepted "
705 "velocity requires requested_dt=" +
706 std::to_string(verifiedTime) +
" hr");
707 trialTime = verifiedTime;
708 if (trialTime < minTrialTime)
714 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
719 VIENNACORE_LOG_ERROR(prefix +
720 ": unable to find a converged CFL-limited step" +
721 (lastFieldFailureReason.empty()
723 : std::string(
" (last failure: ") +
724 lastFieldFailureReason +
")."));
726 const bool fieldsConverged = solveFields(requestedTime);
727 if (!fieldsConverged)
728 VIENNACORE_LOG_ERROR(
729 prefix +
": coupled solve failed" +
730 (lastFieldFailureReason.empty()
732 : std::string(
" (") + lastFieldFailureReason +
")."));
733 ambientVelocity = makeAmbientVelocity();
737 pressureCache_ = diffusionField->getPressureCache();
738 stressCache_ = diffusionField->getStressCache();
740 diffusionField->markSolved();
746 auto ambientVolume = [&]() ->
T {
747 const T delta = ambientInterface->getGrid().getGridDelta();
749 for (
unsigned d = 0; d <
D; ++d)
753 it(ambientInterface->getDomain());
754 !it.isFinished(); it.next()) {
756 for (
unsigned d = 0; d <
D; ++d)
757 cells *=
T(it.getEndIndices(
static_cast<int>(d)) -
758 it.getStartIndices(
static_cast<int>(d)) + 1);
760 vol += std::min(std::max(
T(0.5) - it.getValue(),
T(0)),
T(1)) *
762 else if (it.getValue() <
T(0))
763 vol += cellVol * cells;
767 const T volBefore = ambientVolume();
774 const T volAfterPreclip = ambientVolume();
776 diffusionField->writePersistentFields();
777 deformationField->writeFieldsToLevelSet();
779 auto advect = [&](SmartPointer<Domain<T, D>> levelSet,
780 SmartPointer<VelocityField<T>> velocityField) {
782 adv.insertNextLevelSet(levelSet);
783 adv.setVelocityField(velocityField);
784 adv.setSpatialScheme(spatialScheme);
785 adv.setTemporalScheme(temporalScheme);
786 adv.setAdvectionTime(advectionTime);
792 advect(ambientInterface, ambientVelocity);
793 advect(siInterface, diffusionField);
794 if (hasMask && maskBarrierOnly_)
798 advect(maskInterface,
800 else if (monolithicMask) {
801 if (!maskTracker_ || !maskTracker_->tracks(maskInterface))
802 maskTracker_.emplace(maskInterface);
803 advect(maskInterface,
805 maskTracker_->correct(
807 [&](
const Vec3D<T> &x) {
808 return deformationField->getMaterialVelocity(x,
true);
813 VIENNACORE_LOG_TIMING(std::string(
" advection(") + (hasMask ?
"3" :
"2") +
816 const T volAfterAdvect = ambientVolume();
827 const T volAfterPostclip = ambientVolume();
829 Interior<T, D> fill(ambientInterface);
830 fill.setGuide(siInterface);
834 const T volAfterFill = ambientVolume();
835 const T toNm = std::pow(
T(1000),
static_cast<int>(
D));
839 prefix +
": VOLTRACE dV_preclip=" +
840 std::to_string((volAfterPreclip - volBefore) * toNm) +
" dV_advect=" +
841 std::to_string((volAfterAdvect - volAfterPreclip) * toNm) +
843 std::to_string((volAfterPostclip - volAfterAdvect) * toNm) +
845 std::to_string((volAfterFill - volAfterPostclip) * toNm) +
846 " vol_start=" + std::to_string(volBefore * toNm) +
847 " vol_end=" + std::to_string(volAfterFill * toNm) +
" nm^D");
856 diffusionField->writePersistentFields();
857 deformationField->writeFieldsToLevelSet();
859 VIENNACORE_LOG_INFO(prefix +
": time step complete, actual_dt=" +
860 std::to_string(advectionTime) +
" hr");
863 VIENNACORE_LOG_TIMING(
"── step total", tStep);
865 return advectionTime;