152 using IndexType = viennahrle::Index<D>;
153 using IndexCacheMap =
154 std::unordered_map<IndexType, T, typename IndexType::hash>;
156 SmartPointer<Domain<T, D>> siInterface =
nullptr;
157 SmartPointer<Domain<T, D>> ambientInterface =
nullptr;
158 SmartPointer<Domain<T, D>> maskInterface =
nullptr;
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();
173 IndexType diffusionMinIndex{};
174 IndexType diffusionMaxIndex{};
175 bool diffusionBoundsSet =
false;
177 IndexType maskBendingMinIndex{};
178 IndexType maskBendingMaxIndex{};
179 bool maskBendingBoundsSet =
false;
182 SmartPointer<OxidationDiffusion<T, D>> diffusionField;
183 SmartPointer<OxidationDeformation<T, D>> deformationField;
184 SmartPointer<OxidationMaskBending<T, D>> maskBendingField;
185 T lastMaxVelocity_ =
T(0);
189 IndexCacheMap concentrationCache_;
193 return concentrationCache_;
196 concentrationCache_ = std::move(cache);
206 if (deformationField)
207 deformationField->clearMaskVelocityField();
212 SmartPointer<
Domain<T, D>> passedMaskInterface =
nullptr)
213 : siInterface(passedSiInterface),
214 ambientInterface(passedAmbientInterface),
215 maskInterface(passedMaskInterface) {}
217 template <
class... Args>
static auto New(Args &&...args) {
218 return SmartPointer<Oxidation>::New(std::forward<Args>(args)...);
223 gpuPreconditioner_ = preconditioner;
228 ambientInterface = ambient;
231 maskInterface = mask;
235 oxidationParams = params;
238 deformationParams = params;
241 couplingParams = params;
254 maskCouplingIterations = std::max(1u, iterations);
258 maskCouplingTolerance = std::max(tolerance,
T(0));
265 diffusionMinIndex = minIndex;
266 diffusionMaxIndex = maxIndex;
267 diffusionBoundsSet =
true;
272 const IndexType &maxIndex) {
273 maskBendingMinIndex = minIndex;
274 maskBendingMaxIndex = maxIndex;
275 maskBendingBoundsSet =
true;
280 return diffusionField;
285 return deformationField;
290 return maskBendingField;
294 return lastMaskCouplingIterations;
303 void apply(
T advectionTime) { applyImpl(advectionTime, std::nullopt); }
307 return applyImpl(requestedTime, std::clamp(cflFactor,
T(1e-3),
T(0.499)));
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.")
319 if (requestedTime <=
T(0))
325 const bool hasMask = (maskInterface !=
nullptr);
326 const std::string prefix = hasMask ?
"LOCOS" :
"Oxidation";
328 VIENNACORE_LOG_INFO(prefix +
": starting time step, requested_dt=" +
329 std::to_string(requestedTime) +
" hr");
331 const auto baseConcentrationCache = concentrationCache_;
332 std::string lastFieldFailureReason;
333 bool lastFailureWasMaskFixedPoint =
false;
334 T adaptiveMaskRelaxationScale =
T(1);
336 auto solveFields = [&](
T stressTimeStep,
bool logCouplingResult) ->
bool {
337 auto rejectSolve = [&](
const std::string &reason) {
338 lastFieldFailureReason = reason;
342 auto validateCoupledModel = [&](
const SmartPointer<OxidationModel<T, D>>
344 if (!model->hasConverged()) {
345 const auto reason = model->getFailureReason();
348 ?
"pressure-concentration coupling failed (residual=" +
349 std::to_string(model->getResidual()) +
", tolerance=" +
350 std::to_string(couplingParams.tolerance) +
")"
353 if (!diffusionField->lastSolveConverged() ||
354 !diffusionField->hasFiniteConcentrationField()) {
356 "diffusion solve failed (residual=" +
357 std::to_string(diffusionField->getNormalizedResidual()) +
358 ", tolerance=" + std::to_string(oxidationParams.tolerance) +
")");
360 if (!deformationField->lastSolveConverged() ||
361 !deformationField->hasFiniteSolution()) {
363 "deformation solve failed (mechanics=" +
364 std::to_string(deformationField->getResidual()) +
", pressure=" +
365 std::to_string(deformationField->getLastPressureResidual()) +
367 std::to_string(deformationField->getLastStokesResidual()) +
")");
372 auto validateMaskSolve = [&]() {
373 if (maskBendingField ==
nullptr)
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) +
")");
382 const T couplingResidual =
383 maskBendingField->getLastApplyVelocityChange();
384 if (!std::isfinite(couplingResidual))
386 "mask velocity coupling produced non-finite values");
390 lastFieldFailureReason.clear();
391 lastFailureWasMaskFixedPoint =
false;
392 auto stepDeformationParams = deformationParams;
393 stepDeformationParams.stressTimeStep = stressTimeStep;
399 if (deformationField)
400 deformationField->clearMaskVelocityField();
405 siInterface, ambientInterface, oxidationParams);
406 diffusionField->setConcentrationCache(baseConcentrationCache);
407 diffusionField->setGpuMode(gpuMode_);
408 diffusionField->setGpuPreconditioner(gpuPreconditioner_);
410 diffusionField->setMaskInterface(maskInterface, maskInteriorSign);
413 siInterface, ambientInterface, diffusionField, oxidationParams,
414 stepDeformationParams);
415 deformationField->setGpuMode(gpuMode_);
416 deformationField->setGpuPreconditioner(gpuPreconditioner_);
418 deformationField->setMaskInterface(maskInterface, maskInteriorSign);
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");
429 coupledModel->apply();
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))
440 auto stepMaskParams = maskParams;
441 stepMaskParams.stressTimeStep = stressTimeStep;
442 stepMaskParams.relaxation = std::clamp(
443 maskParams.relaxation * adaptiveMaskRelaxationScale,
T(0.01),
T(1));
445 deformationField, maskInterface, stepMaskParams, maskInteriorSign);
446 maskBendingField->setAmbientInterface(ambientInterface,
448 if (maskBendingBoundsSet)
449 maskBendingField->setSolveBounds(maskBendingMinIndex,
450 maskBendingMaxIndex);
451 VIENNACORE_LOG_DEBUG(prefix +
": solving mask bending field");
455 maskBendingField->apply();
456 }
catch (
const std::exception &e) {
458 return rejectSolve(
"mask bending solve error: " +
459 std::string(e.what()));
462 if (Logger::hasTiming())
463 Logger::getInstance()
464 .addTiming(
" maskBending(iter=1)", tMask)
466 if (!validateMaskSolve())
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)));
475 lastMaskCouplingIterations = 1;
476 lastMaskCouplingResidual =
477 maskBendingField->getLastApplyVelocityChange();
478 deformationField->setMaskVelocityField(maskBendingField);
479 for (
unsigned iteration = 1; iteration < maskCouplingIterations;
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))
491 VIENNACORE_LOG_DEBUG(prefix +
": coupling iteration " +
492 std::to_string(iteration + 1) +
493 " solving mask field");
496 maskBendingField->apply();
497 }
catch (
const std::exception &e) {
499 return rejectSolve(
"mask bending solve error at iteration " +
500 std::to_string(iteration + 1) +
": " + e.what());
503 if (!validateMaskSolve())
505 if (Logger::hasTiming())
506 Logger::getInstance()
507 .addTiming(
" coupled(iter=" + std::to_string(iteration + 1) +
511 " maskBending(iter=" + std::to_string(iteration + 1) +
")",
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)
523 const T maskAbsoluteDisplacement =
524 maskBendingField->getLastApplyAbsoluteVelocityChange() *
532 T maxMaskVelocity =
T(0);
533 for (
unsigned d = 0; d <
D; ++d)
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) {
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) +
555 }
else if (logCouplingResult) {
556 VIENNACORE_LOG_WARNING(
558 ": mask/oxide coupling did not converge "
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.");
567 if (!maskCouplingConverged) {
568 lastFailureWasMaskFixedPoint =
true;
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) +
580 maskBendingField =
nullptr;
586 auto makeAmbientVelocity = [&]() -> SmartPointer<VelocityField<T>> {
589 deformationField, maskBendingField, maskInterface, ambientInterface,
592 return deformationField;
595 T advectionTime = requestedTime;
596 SmartPointer<VelocityField<T>> ambientVelocity;
598 auto computeMaxVelocity =
599 [&](
const SmartPointer<VelocityField<T>> &passedAmbientVelocity) {
600 T maxVelocity = diffusionField->getDissipationAlpha(0, -1, {});
601 for (
unsigned d = 0; d <
D; ++d) {
603 std::max(maxVelocity,
604 passedAmbientVelocity->getDissipationAlpha(d, -1, {}));
607 std::max(maxVelocity,
608 maskBendingField->getDissipationAlpha(d, -1, {}));
613 auto cflLimitedTime = [&](
T trialTime,
T maxVelocity) {
614 if (maxVelocity <= std::numeric_limits<T>::epsilon())
616 const T gridDelta = siInterface->getGrid().getGridDelta();
617 return std::min(trialTime, (*cflFactor) * gridDelta / maxVelocity);
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);
631 adaptiveMaskRelaxationScale =
632 std::max(
T(0.05), adaptiveMaskRelaxationScale *
T(0.5));
633 const T nextTrial = dampMask ? trialTime : trialTime *
T(0.5);
636 ": rejecting non-converged coupled predictor "
638 (lastFieldFailureReason.empty()
639 ?
"mask residual=" + std::to_string(lastMaskCouplingResidual)
640 : lastFieldFailureReason) +
641 (dampMask ?
", retrying with mask relaxation scale=" +
642 std::to_string(adaptiveMaskRelaxationScale)
644 "), retrying with requested_dt=" + std::to_string(nextTrial) +
646 trialTime = nextTrial;
647 if (trialTime < minTrialTime)
652 ambientVelocity = makeAmbientVelocity();
653 T maxVelocity = computeMaxVelocity(ambientVelocity);
654 if (!std::isfinite(maxVelocity))
655 VIENNACORE_LOG_ERROR(prefix +
": non-finite CFL velocity estimate.");
657 advectionTime = cflLimitedTime(trialTime, maxVelocity);
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");
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);
670 adaptiveMaskRelaxationScale =
671 std::max(
T(0.05), adaptiveMaskRelaxationScale *
T(0.5));
673 dampMask ? advectionTime : advectionTime *
T(0.5);
676 ": rejecting non-converged CFL re-solve "
678 (lastFieldFailureReason.empty()
680 std::to_string(lastMaskCouplingResidual)
681 : lastFieldFailureReason) +
682 (dampMask ?
", retrying with mask relaxation scale=" +
683 std::to_string(adaptiveMaskRelaxationScale)
685 "), retrying with requested_dt=" + std::to_string(nextTrial) +
687 trialTime = nextTrial;
688 if (trialTime < minTrialTime)
692 ambientVelocity = makeAmbientVelocity();
693 maxVelocity = computeMaxVelocity(ambientVelocity);
694 if (!std::isfinite(maxVelocity))
695 VIENNACORE_LOG_ERROR(prefix +
696 ": non-finite accepted CFL velocity.");
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)
711 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
721 const bool oxideFinite =
722 diffusionField && deformationField &&
723 diffusionField->hasFiniteConcentrationField() &&
724 deformationField->hasFiniteSolution();
725 if (hasMask && oxideFinite) {
726 VIENNACORE_LOG_WARNING(
728 ": all CFL attempts exhausted; freezing mask for "
730 std::to_string(minTrialTime) +
731 " hr (last failure: " + lastFieldFailureReason +
732 "). Consider increasing maskReferenceViscosity or "
733 "maskCouplingIterations.");
734 maskBendingField =
nullptr;
735 ambientVelocity = makeAmbientVelocity();
736 advectionTime = minTrialTime;
737 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
739 VIENNACORE_LOG_ERROR(prefix +
740 ": unable to find a converged CFL-limited step" +
741 (lastFieldFailureReason.empty()
743 : std::string(
" (last failure: ") +
744 lastFieldFailureReason +
")."));
748 const bool fieldsConverged = solveFields(requestedTime,
true);
749 if (!fieldsConverged)
750 VIENNACORE_LOG_ERROR(
751 prefix +
": coupled solve failed" +
752 (lastFieldFailureReason.empty()
754 : std::string(
" (") + lastFieldFailureReason +
")."));
755 ambientVelocity = makeAmbientVelocity();
758 concentrationCache_ = diffusionField->getConcentrationCache();
760 diffusionField->markSolved();
768 diffusionField->writePersistentFields();
769 deformationField->writeFieldsToLevelSet();
770 if (hasMask && maskBendingField)
771 maskBendingField->writeFieldsToLevelSet();
773 if (hasMask && maskBendingField)
774 maskBendingField->finalizeElasticAdvectionVelocity();
776 auto advect = [&](SmartPointer<Domain<T, D>> levelSet,
777 SmartPointer<VelocityField<T>> velocityField) {
779 adv.insertNextLevelSet(levelSet);
780 adv.setVelocityField(velocityField);
781 adv.setSpatialScheme(spatialScheme);
782 adv.setTemporalScheme(temporalScheme);
783 adv.setAdvectionTime(advectionTime);
789 advect(ambientInterface, ambientVelocity);
790 advect(siInterface, diffusionField);
791 if (hasMask && maskBendingField)
792 advect(maskInterface, maskBendingField);
794 VIENNACORE_LOG_TIMING(std::string(
" advection(") + (hasMask ?
"3" :
"2") +
809 if (maskBendingField)
810 maskBendingField->writeFieldsToLevelSet();
813 Interior<T, D> fill(ambientInterface);
814 fill.setGuide(siInterface);
824 diffusionField->writePersistentFields();
825 deformationField->writeFieldsToLevelSet();
827 VIENNACORE_LOG_INFO(prefix +
": time step complete, actual_dt=" +
828 std::to_string(advectionTime) +
" hr");
831 VIENNACORE_LOG_TIMING(
"── step total", tStep);
833 return advectionTime;