152 using IndexType = viennahrle::Index<D>;
154 SmartPointer<Domain<T, D>> siInterface =
nullptr;
155 SmartPointer<Domain<T, D>> ambientInterface =
nullptr;
156 SmartPointer<Domain<T, D>> maskInterface =
nullptr;
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();
171 IndexType diffusionMinIndex{};
172 IndexType diffusionMaxIndex{};
173 bool diffusionBoundsSet =
false;
175 IndexType maskBendingMinIndex{};
176 IndexType maskBendingMaxIndex{};
177 bool maskBendingBoundsSet =
false;
180 SmartPointer<OxidationDiffusion<T, D>> diffusionField;
181 SmartPointer<OxidationDeformation<T, D>> deformationField;
182 SmartPointer<OxidationMaskBending<T, D>> maskBendingField;
183 T lastMaxVelocity_ =
T(0);
187 std::unordered_map<std::size_t, T> concentrationCache_;
191 return concentrationCache_;
194 concentrationCache_ = std::move(cache);
204 if (deformationField)
205 deformationField->clearMaskVelocityField();
210 SmartPointer<
Domain<T, D>> passedMaskInterface =
nullptr)
211 : siInterface(passedSiInterface),
212 ambientInterface(passedAmbientInterface),
213 maskInterface(passedMaskInterface) {}
215 template <
class... Args>
static auto New(Args &&...args) {
216 return SmartPointer<Oxidation>::New(std::forward<Args>(args)...);
221 gpuPreconditioner_ = preconditioner;
226 ambientInterface = ambient;
229 maskInterface = mask;
233 oxidationParams = params;
236 deformationParams = params;
239 couplingParams = params;
252 maskCouplingIterations = std::max(1u, iterations);
256 maskCouplingTolerance = std::max(tolerance,
T(0));
263 diffusionMinIndex = minIndex;
264 diffusionMaxIndex = maxIndex;
265 diffusionBoundsSet =
true;
270 const IndexType &maxIndex) {
271 maskBendingMinIndex = minIndex;
272 maskBendingMaxIndex = maxIndex;
273 maskBendingBoundsSet =
true;
278 return diffusionField;
283 return deformationField;
288 return maskBendingField;
292 return lastMaskCouplingIterations;
301 void apply(
T advectionTime) { applyImpl(advectionTime, std::nullopt); }
305 return applyImpl(requestedTime, std::clamp(cflFactor,
T(1e-3),
T(0.499)));
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.")
317 if (requestedTime <=
T(0))
323 const bool hasMask = (maskInterface !=
nullptr);
324 const std::string prefix = hasMask ?
"LOCOS" :
"Oxidation";
326 VIENNACORE_LOG_INFO(prefix +
": starting time step, requested_dt=" +
327 std::to_string(requestedTime) +
" hr");
329 const auto baseConcentrationCache = concentrationCache_;
330 std::string lastFieldFailureReason;
331 bool lastFailureWasMaskFixedPoint =
false;
332 T adaptiveMaskRelaxationScale =
T(1);
334 auto solveFields = [&](
T stressTimeStep,
bool logCouplingResult) ->
bool {
335 auto rejectSolve = [&](
const std::string &reason) {
336 lastFieldFailureReason = reason;
340 auto validateCoupledModel = [&](
const SmartPointer<OxidationModel<T, D>>
342 if (!model->hasConverged()) {
343 const auto reason = model->getFailureReason();
346 ?
"pressure-concentration coupling failed (residual=" +
347 std::to_string(model->getResidual()) +
", tolerance=" +
348 std::to_string(couplingParams.tolerance) +
")"
351 if (!diffusionField->lastSolveConverged() ||
352 !diffusionField->hasFiniteConcentrationField()) {
354 "diffusion solve failed (residual=" +
355 std::to_string(diffusionField->getNormalizedResidual()) +
356 ", tolerance=" + std::to_string(oxidationParams.tolerance) +
")");
358 if (!deformationField->lastSolveConverged() ||
359 !deformationField->hasFiniteSolution()) {
361 "deformation solve failed (mechanics=" +
362 std::to_string(deformationField->getResidual()) +
", pressure=" +
363 std::to_string(deformationField->getLastPressureResidual()) +
365 std::to_string(deformationField->getLastStokesResidual()) +
")");
370 auto validateMaskSolve = [&]() {
371 if (maskBendingField ==
nullptr)
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) +
")");
380 const T couplingResidual =
381 maskBendingField->getLastApplyVelocityChange();
382 if (!std::isfinite(couplingResidual))
384 "mask velocity coupling produced non-finite values");
388 lastFieldFailureReason.clear();
389 lastFailureWasMaskFixedPoint =
false;
390 auto stepDeformationParams = deformationParams;
391 stepDeformationParams.stressTimeStep = stressTimeStep;
397 if (deformationField)
398 deformationField->clearMaskVelocityField();
403 siInterface, ambientInterface, oxidationParams);
404 diffusionField->setConcentrationCache(baseConcentrationCache);
405 diffusionField->setGpuMode(gpuMode_);
406 diffusionField->setGpuPreconditioner(gpuPreconditioner_);
408 diffusionField->setMaskInterface(maskInterface, maskInteriorSign);
411 siInterface, ambientInterface, diffusionField, oxidationParams,
412 stepDeformationParams);
413 deformationField->setGpuMode(gpuMode_);
414 deformationField->setGpuPreconditioner(gpuPreconditioner_);
416 deformationField->setMaskInterface(maskInterface, maskInteriorSign);
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");
427 coupledModel->apply();
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))
438 auto stepMaskParams = maskParams;
439 stepMaskParams.stressTimeStep = stressTimeStep;
440 stepMaskParams.relaxation = std::clamp(
441 maskParams.relaxation * adaptiveMaskRelaxationScale,
T(0.01),
T(1));
443 deformationField, maskInterface, stepMaskParams, maskInteriorSign);
444 maskBendingField->setAmbientInterface(ambientInterface,
446 if (maskBendingBoundsSet)
447 maskBendingField->setSolveBounds(maskBendingMinIndex,
448 maskBendingMaxIndex);
449 VIENNACORE_LOG_DEBUG(prefix +
": solving mask bending field");
453 maskBendingField->apply();
454 }
catch (
const std::exception &e) {
456 return rejectSolve(
"mask bending solve error: " +
457 std::string(e.what()));
460 if (Logger::hasTiming())
461 Logger::getInstance()
462 .addTiming(
" maskBending(iter=1)", tMask)
464 if (!validateMaskSolve())
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)));
473 lastMaskCouplingIterations = 1;
474 lastMaskCouplingResidual =
475 maskBendingField->getLastApplyVelocityChange();
476 deformationField->setMaskVelocityField(maskBendingField);
477 for (
unsigned iteration = 1; iteration < maskCouplingIterations;
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))
489 VIENNACORE_LOG_DEBUG(prefix +
": coupling iteration " +
490 std::to_string(iteration + 1) +
491 " solving mask field");
494 maskBendingField->apply();
495 }
catch (
const std::exception &e) {
497 return rejectSolve(
"mask bending solve error at iteration " +
498 std::to_string(iteration + 1) +
": " + e.what());
501 if (!validateMaskSolve())
503 if (Logger::hasTiming())
504 Logger::getInstance()
505 .addTiming(
" coupled(iter=" + std::to_string(iteration + 1) +
509 " maskBending(iter=" + std::to_string(iteration + 1) +
")",
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)
521 const T maskAbsoluteDisplacement =
522 maskBendingField->getLastApplyAbsoluteVelocityChange() *
530 T maxMaskVelocity =
T(0);
531 for (
unsigned d = 0; d <
D; ++d)
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) {
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) +
553 }
else if (logCouplingResult) {
554 VIENNACORE_LOG_WARNING(
556 ": mask/oxide coupling did not converge "
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.");
565 if (!maskCouplingConverged) {
566 lastFailureWasMaskFixedPoint =
true;
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) +
578 maskBendingField =
nullptr;
584 auto makeAmbientVelocity = [&]() -> SmartPointer<VelocityField<T>> {
587 deformationField, maskBendingField, maskInterface, ambientInterface,
590 return deformationField;
593 T advectionTime = requestedTime;
594 SmartPointer<VelocityField<T>> ambientVelocity;
596 auto computeMaxVelocity =
597 [&](
const SmartPointer<VelocityField<T>> &passedAmbientVelocity) {
598 T maxVelocity = diffusionField->getDissipationAlpha(0, -1, {});
599 for (
unsigned d = 0; d <
D; ++d) {
601 std::max(maxVelocity,
602 passedAmbientVelocity->getDissipationAlpha(d, -1, {}));
605 std::max(maxVelocity,
606 maskBendingField->getDissipationAlpha(d, -1, {}));
611 auto cflLimitedTime = [&](
T trialTime,
T maxVelocity) {
612 if (maxVelocity <= std::numeric_limits<T>::epsilon())
614 const T gridDelta = siInterface->getGrid().getGridDelta();
615 return std::min(trialTime, (*cflFactor) * gridDelta / maxVelocity);
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);
629 adaptiveMaskRelaxationScale =
630 std::max(
T(0.05), adaptiveMaskRelaxationScale *
T(0.5));
631 const T nextTrial = dampMask ? trialTime : trialTime *
T(0.5);
634 ": rejecting non-converged coupled predictor "
636 (lastFieldFailureReason.empty()
637 ?
"mask residual=" + std::to_string(lastMaskCouplingResidual)
638 : lastFieldFailureReason) +
639 (dampMask ?
", retrying with mask relaxation scale=" +
640 std::to_string(adaptiveMaskRelaxationScale)
642 "), retrying with requested_dt=" + std::to_string(nextTrial) +
644 trialTime = nextTrial;
645 if (trialTime < minTrialTime)
650 ambientVelocity = makeAmbientVelocity();
651 T maxVelocity = computeMaxVelocity(ambientVelocity);
652 if (!std::isfinite(maxVelocity))
653 VIENNACORE_LOG_ERROR(prefix +
": non-finite CFL velocity estimate.");
655 advectionTime = cflLimitedTime(trialTime, maxVelocity);
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");
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);
668 adaptiveMaskRelaxationScale =
669 std::max(
T(0.05), adaptiveMaskRelaxationScale *
T(0.5));
671 dampMask ? advectionTime : advectionTime *
T(0.5);
674 ": rejecting non-converged CFL re-solve "
676 (lastFieldFailureReason.empty()
678 std::to_string(lastMaskCouplingResidual)
679 : lastFieldFailureReason) +
680 (dampMask ?
", retrying with mask relaxation scale=" +
681 std::to_string(adaptiveMaskRelaxationScale)
683 "), retrying with requested_dt=" + std::to_string(nextTrial) +
685 trialTime = nextTrial;
686 if (trialTime < minTrialTime)
690 ambientVelocity = makeAmbientVelocity();
691 maxVelocity = computeMaxVelocity(ambientVelocity);
692 if (!std::isfinite(maxVelocity))
693 VIENNACORE_LOG_ERROR(prefix +
694 ": non-finite accepted CFL velocity.");
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)
709 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
719 const bool oxideFinite =
720 diffusionField && deformationField &&
721 diffusionField->hasFiniteConcentrationField() &&
722 deformationField->hasFiniteSolution();
723 if (hasMask && oxideFinite) {
724 VIENNACORE_LOG_WARNING(
726 ": all CFL attempts exhausted; freezing mask for "
728 std::to_string(minTrialTime) +
729 " hr (last failure: " + lastFieldFailureReason +
730 "). Consider increasing maskReferenceViscosity or "
731 "maskCouplingIterations.");
732 maskBendingField =
nullptr;
733 ambientVelocity = makeAmbientVelocity();
734 advectionTime = minTrialTime;
735 lastMaxVelocity_ = computeMaxVelocity(ambientVelocity);
737 VIENNACORE_LOG_ERROR(prefix +
738 ": unable to find a converged CFL-limited step" +
739 (lastFieldFailureReason.empty()
741 : std::string(
" (last failure: ") +
742 lastFieldFailureReason +
")."));
746 const bool fieldsConverged = solveFields(requestedTime,
true);
747 if (!fieldsConverged)
748 VIENNACORE_LOG_ERROR(
749 prefix +
": coupled solve failed" +
750 (lastFieldFailureReason.empty()
752 : std::string(
" (") + lastFieldFailureReason +
")."));
753 ambientVelocity = makeAmbientVelocity();
756 concentrationCache_ = diffusionField->getConcentrationCache();
758 diffusionField->markSolved();
762 BooleanOperation<T, D>(ambientInterface, maskInterface,
766 diffusionField->writePersistentFields();
767 deformationField->writeFieldsToLevelSet();
768 if (hasMask && maskBendingField)
769 maskBendingField->writeFieldsToLevelSet();
771 if (hasMask && maskBendingField)
772 maskBendingField->finalizeElasticAdvectionVelocity();
774 auto advect = [&](SmartPointer<Domain<T, D>> levelSet,
775 SmartPointer<VelocityField<T>> velocityField) {
777 adv.insertNextLevelSet(levelSet);
778 adv.setVelocityField(velocityField);
779 adv.setSpatialScheme(spatialScheme);
780 adv.setTemporalScheme(temporalScheme);
781 adv.setAdvectionTime(advectionTime);
787 advect(ambientInterface, ambientVelocity);
788 advect(siInterface, diffusionField);
789 if (hasMask && maskBendingField)
790 advect(maskInterface, maskBendingField);
792 VIENNACORE_LOG_TIMING(std::string(
" advection(") + (hasMask ?
"3" :
"2") +
800 Interior<T, D>(maskInterface).apply();
801 BooleanOperation<T, D>(ambientInterface, maskInterface,
807 if (maskBendingField)
808 maskBendingField->writeFieldsToLevelSet();
811 Interior<T, D> fill(ambientInterface);
812 fill.setGuide(siInterface);
822 diffusionField->writePersistentFields();
823 deformationField->writeFieldsToLevelSet();
825 VIENNACORE_LOG_INFO(prefix +
": time step complete, actual_dt=" +
826 std::to_string(advectionTime) +
" hr");
829 VIENNACORE_LOG_TIMING(
"── step total", tStep);
831 return advectionTime;