LCOV - code coverage report
Current view: top level - src/tensor_computes - FFTMechanics.C (source / functions) Hit Total Coverage
Test: idaholab/swift: #92 (25e020) with base b3cd84 Lines: 0 84 0.0 %
Date: 2025-09-10 17:10:32 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /**********************************************************************/
       2             : /*                    DO NOT MODIFY THIS HEADER                       */
       3             : /*             Swift, a Fourier spectral solver for MOOSE             */
       4             : /*                                                                    */
       5             : /*            Copyright 2024 Battelle Energy Alliance, LLC            */
       6             : /*                        ALL RIGHTS RESERVED                         */
       7             : /**********************************************************************/
       8             : 
       9             : #include "FFTMechanics.h"
      10             : #include "DomainAction.h"
      11             : #include "MooseError.h"
      12             : #include "SwiftUtils.h"
      13             : #include <ATen/TensorIndexing.h>
      14             : #include <ATen/core/TensorBody.h>
      15             : #include <ATen/ops/unsqueeze_ops.h>
      16             : #include <util/Optional.h>
      17             : 
      18             : registerMooseObject("SwiftApp", FFTMechanics);
      19             : 
      20             : InputParameters
      21           0 : FFTMechanics::validParams()
      22             : {
      23           0 :   InputParameters params = TensorOperator<>::validParams();
      24           0 :   params.addClassDescription("deGeus variational mechanics solve. Updates the coupled buffer "
      25             :                              "holding the deformation gradient tensor.");
      26             :   // params.addParam<TensorInputBufferName>("C0", "Stiffness tensor estimate");
      27             :   // params.addParam<TensorInputBufferName>("C", "Stiffness tensor");
      28           0 :   params.addRequiredParam<TensorInputBufferName>("K", "Bulk modulus");
      29           0 :   params.addParam<TensorInputBufferName>("mu", "Shear modulus");
      30           0 :   params.addParam<Real>("l_tol", 1e-2, "Linear congugate gradient solve tolerance");
      31           0 :   params.addParam<unsigned int>("l_max_its",
      32             :                                 "Maximum number of congugate gradient solve iterations");
      33           0 :   params.addParam<Real>("nl_rel_tol", 1e-5, "Nonlinear solve absolute tolerance");
      34           0 :   params.addParam<Real>("nl_abs_tol", 1e-8, "Nonlinear solve relative tolerance");
      35           0 :   params.addParam<unsigned int>("nl_max_its", 100, "Maximum number of nonlinear solve iterations");
      36           0 :   params.addParam<TensorInputBufferName>("stress", "stress", "Computed stress");
      37           0 :   params.addParam<TensorInputBufferName>("tangent_operator", "dstressdstrain", "Tangent operator");
      38           0 :   params.addRequiredParam<TensorComputeName>("constitutive_model",
      39             :                                              "Tensor compute for the constitutive model (computes "
      40             :                                              "stress from displacement gradeint tensor)");
      41           0 :   params.addParam<TensorInputBufferName>("applied_macroscopic_strain",
      42             :                                          "Applied macroscopic strain");
      43           0 :   params.addParam<TensorInputBufferName>("F", "F", "Deformation gradient tensor.");
      44           0 :   params.addParam<bool>("verbose", false, "Print non-linear residuals.");
      45           0 :   return params;
      46           0 : }
      47             : 
      48           0 : FFTMechanics::FFTMechanics(const InputParameters & parameters)
      49             :   : TensorOperator<>(parameters),
      50           0 :     _ti(torch::eye(_dim, MooseTensor::floatTensorOptions())),
      51           0 :     _tI(MooseTensor::unsqueeze0(_ti, _dim)),
      52           0 :     _tI4(MooseTensor::unsqueeze0(torch::einsum("il,jk", {_ti, _ti}), _dim)),
      53           0 :     _tI4rt(MooseTensor::unsqueeze0(torch::einsum("ik,jl", {_ti, _ti}), _dim)),
      54           0 :     _tI4s((_tI4 + _tI4rt) / 2.0),
      55           0 :     _tII(MooseTensor::dyad22(_tI, _tI)),
      56           0 :     _tK(getInputBuffer("K")),
      57           0 :     _tmu(getInputBuffer("mu")),
      58           0 :     _r2_shape(_domain.getValueShape({_dim, _dim})),
      59           0 :     _tF(getInputBuffer<>("F")),
      60           0 :     _tP(getInputBuffer("stress")),
      61           0 :     _tK4(getInputBuffer("tangent_operator")),
      62           0 :     _l_tol(getParam<Real>("l_tol")),
      63           0 :     _l_max_its(isParamValid("l_max_its") ? getParam<unsigned int>("l_max_its")
      64           0 :                                          : _domain.getNumberOfCells()),
      65           0 :     _nl_rel_tol(getParam<Real>("nl_rel_tol")),
      66           0 :     _nl_abs_tol(getParam<Real>("nl_abs_tol")),
      67           0 :     _nl_max_its(getParam<unsigned int>("nl_max_its")),
      68           0 :     _constitutive_model(getCompute("constitutive_model")),
      69           0 :     _applied_macroscopic_strain(isParamValid("applied_macroscopic_strain")
      70           0 :                                     ? &getInputBuffer("applied_macroscopic_strain")
      71             :                                     : nullptr),
      72           0 :     _verbose(getParam<bool>("verbose"))
      73             : {
      74             :   // Build projection tensor once
      75           0 :   const auto & q = _domain.getKGrid();
      76           0 :   const auto Q = _domain.getKSquare().unsqueeze(-1).unsqueeze(-1);
      77             : 
      78           0 :   auto M = torch::where(Q == 0, 0.0, q.unsqueeze(-2) * q.unsqueeze(-1) / Q);
      79             : 
      80           0 :   M = M.unsqueeze(-3).unsqueeze(-1);
      81             : 
      82           0 :   const auto delta_im = _ti.unsqueeze(1).unsqueeze(1).expand({_dim, _dim, _dim, _dim});
      83             : 
      84           0 :   _Ghat4 = (M * delta_im).to(MooseTensor::complexFloatTensorOptions());
      85           0 : }
      86             : 
      87             : void
      88           0 : FFTMechanics::check()
      89             : {
      90           0 :   const auto & stress_name = getParam<TensorOutputBufferName>("stress");
      91           0 :   if (!_constitutive_model.getSuppliedItems().count(stress_name))
      92           0 :     paramError("constitutive_model", "does not provide stress tensor '", stress_name, "'.");
      93           0 : }
      94             : 
      95             : void
      96           0 : FFTMechanics::computeBuffer()
      97             : {
      98             :   using namespace MooseTensor;
      99             : 
     100           0 :   const auto shape = _domain.getShape();
     101           0 :   std::vector<int64_t> _r2shape(shape.begin(), shape.end());
     102           0 :   _r2shape.push_back(_dim);
     103           0 :   _r2shape.push_back(_dim);
     104             : 
     105           0 :   const auto G = [&](const torch::Tensor & A2)
     106           0 :   { return _domain.ifft(ddot42(_Ghat4, _domain.fft(A2))).reshape(-1); };
     107           0 :   const auto K_dF = [&](const torch::Tensor & dFm)
     108           0 :   { return trans2(ddot42(_tK4, trans2(dFm.reshape(_r2shape)))); };
     109           0 :   const auto G_K_dF = [&](const torch::Tensor & dFm) { return G(K_dF(dFm)); };
     110             : 
     111             :   // initialize deformation gradient, and stress/stiffness       [grid of tensors]
     112           0 :   _u = _tF;
     113           0 :   _constitutive_model.computeBuffer();
     114             : 
     115             :   // initial residual: distribute "barF" over grid using "K4"
     116           0 :   auto b = _applied_macroscopic_strain ? -G_K_dF(_applied_macroscopic_strain->expand(_r2_shape))
     117           0 :                                        : -G_K_dF(torch::zeros_like(_tF));
     118             : 
     119             :   // _u += DbarF;
     120           0 :   if (_applied_macroscopic_strain)
     121           0 :     _u = _u + _applied_macroscopic_strain->expand(_r2_shape);
     122             : 
     123             :   const auto Fn =
     124           0 :       at::linalg_norm(_u, c10::nullopt, c10::nullopt, false, c10::nullopt).cpu().item<double>();
     125             : 
     126             :   unsigned int iiter = 0;
     127           0 :   auto dFm = torch::zeros_like(b);
     128             : 
     129             :   // iterate as long as the iterative update does not vanish
     130             :   while (true)
     131             :   {
     132             :     const auto [dFm_new, iterations, lnorm] =
     133           0 :         conjugateGradientSolve(G_K_dF, b, dFm, _l_tol, _l_max_its);
     134             :     dFm = dFm_new;
     135             : 
     136             :     // update DOFs (array -> tens.grid)
     137           0 :     _u = _u + dFm.reshape(_r2_shape);
     138             : 
     139             :     // new residual stress and tangent
     140           0 :     _constitutive_model.computeBuffer();
     141             : 
     142             :     // convert res.stress to residual
     143           0 :     b = -G(_tP);
     144             : 
     145             :     const auto anorm =
     146           0 :         at::linalg_norm(dFm, c10::nullopt, c10::nullopt, false, c10::nullopt).cpu().item<double>();
     147           0 :     const auto rnorm = anorm / Fn;
     148             : 
     149             :     // print nonlinear residual to the screen
     150           0 :     if (_verbose)
     151           0 :       _console << "|R|=" << anorm << "\t|R/R0|=" << rnorm << '\n';
     152             : 
     153             :     // check convergence
     154           0 :     if ((rnorm < _nl_rel_tol || anorm < _nl_abs_tol) && iiter > 0)
     155             :       break;
     156             : 
     157           0 :     iiter++;
     158             : 
     159           0 :     if (iiter > _nl_max_its)
     160           0 :       paramError("nl_max_its",
     161             :                  "Exceeded the maximum number of nonlinear iterations without converging.");
     162             :   }
     163           0 : }

Generated by: LCOV version 1.14