LCOV - code coverage report
Current view: top level - src/Integrator - Dendrite.H (source / functions) Hit Total Coverage
Test: coverage_merged.info Lines: 70 70 100.0 %
Date: 2024-11-18 05:28:54 Functions: 8 8 100.0 %

          Line data    Source code
       1             : //
       2             : // This is a BASIC phase field method for dendritic growth as described in
       3             : // "A Numerical Approach to Three-Dimensional Dendritic Solidification" by Ryo Kobayashi (1994)
       4             : // Reference: https://doi.org/10.1080/10586458.1994.10504577
       5             : //
       6             : 
       7             : #ifndef INTEGRATOR_DENDRITE_H // Include guards
       8             : #define INTEGRATOR_DENDRITE_H // 
       9             : 
      10             : #include <iostream>
      11             : #include <fstream>
      12             : #include <iomanip>
      13             : 
      14             : #include "AMReX.H"
      15             : #include "AMReX_ParallelDescriptor.H"
      16             : #include "AMReX_ParmParse.H"
      17             : 
      18             : #include "IO/ParmParse.H"
      19             : #include "Integrator/Integrator.H"
      20             : #include "BC/Constant.H"
      21             : #include "IC/IC.H"
      22             : #include "IC/Sphere.H"
      23             : #include "IC/Constant.H"
      24             : #include "IC/Expression.H"
      25             : #include "Numeric/Stencil.H"
      26             : 
      27             : namespace Integrator
      28             : {
      29             : class Dendrite : virtual public Integrator
      30             : {
      31             : public:
      32             :     // Empty constructor
      33           1 :     Dendrite() : Integrator()
      34           1 :     {}
      35             : 
      36             :     // Constructor that triggers parse
      37           1 :     Dendrite(IO::ParmParse& pp) : Dendrite()
      38             :     {
      39           1 :         Parse(*this, pp);
      40           1 :     }
      41             : 
      42           1 :     static void Parse(Dendrite& value, IO::ParmParse& pp)
      43             :     {
      44           1 :         pp_query_required("alpha", value.alpha); // Pre-multiplier of "m" barrier height
      45           1 :         pp_query_required("delta", value.delta); // Anisotropy factor
      46           1 :         pp_query_required("gamma", value.gamma); // Anisotropic temperature coupling factor
      47             : 
      48           1 :         pp_query_required("eps", value.eps); // Diffuse boundary width
      49           1 :         pp_query_required("tau", value.tau); // Diffusive timescale
      50             : 
      51             :         // Refinement criteria for temperature
      52           1 :         pp_query_default("heat.refinement_threshold", value.refinement_threshold_temp, 0.01);
      53             :         // Refinement criteria for phi
      54           1 :         pp_query_default("phi.refinement_threshold", value.refinement_threshold_phi, 0.01);
      55             : 
      56             :         // Expressions for ICs
      57           1 :         value.ic_temp = new IC::Expression(value.geom, pp, "ic.temp");
      58           1 :         value.ic_phi = new IC::Expression(value.geom, pp, "ic.phi");
      59             : 
      60             :         // Use a constant BC object for temperature
      61           1 :         value.bc_temp = new BC::Constant(1);
      62           1 :         value.bc_phi = new BC::Constant(1);
      63           1 :         pp_queryclass("bc.temp", *static_cast<BC::Constant*>(value.bc_temp));
      64           1 :         pp_queryclass("bc.phi", *static_cast<BC::Constant*>(value.bc_phi));
      65             : 
      66           1 :         value.RegisterNewFab(value.temp_mf, value.bc_temp, value.number_of_components, value.number_of_ghost_cells, "Temp", true);
      67           1 :         value.RegisterNewFab(value.temp_old_mf, value.bc_temp, value.number_of_components, value.number_of_ghost_cells, "Temp_old", false);
      68           1 :         value.RegisterNewFab(value.phi_mf, value.bc_phi, value.number_of_components, value.number_of_ghost_cells, "phi", true);
      69           1 :         value.RegisterNewFab(value.phi_old_mf, value.bc_phi, value.number_of_components, value.number_of_ghost_cells, "phi_old", false);
      70           1 :     }
      71             : protected:
      72             : 
      73             :     // Use the ic object to initialize the temperature field
      74           4 :     void Initialize(int lev)
      75             :     {
      76           4 :         ic_temp->Initialize(lev, temp_old_mf);
      77           4 :         ic_phi->Initialize(lev, phi_old_mf);
      78           4 :         ic_temp->Initialize(lev, temp_mf);
      79           4 :         ic_phi->Initialize(lev, phi_mf);
      80           4 :     }
      81             : 
      82             : 
      83             :     // Integrate the heat equation
      84          14 :     void Advance(int lev, Set::Scalar /*time*/, Set::Scalar dt)
      85             :     {
      86             :         // Swap the old temp fab and the new temp fab so we use
      87             :         // the new one.
      88          14 :         std::swap(*temp_mf[lev], *temp_old_mf[lev]);
      89          14 :         std::swap(*phi_mf[lev], *phi_old_mf[lev]);
      90             : 
      91             :         // Get the cell size corresponding to this level
      92          14 :         const Set::Scalar* DX = geom[lev].CellSize();
      93             : 
      94             :         // Iterate over all of the patches on this level
      95          88 :         for (amrex::MFIter mfi(*temp_mf[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
      96             :         {
      97             :             // Get the box (index dimensions) for this patch
      98          74 :             const amrex::Box& bx = mfi.tilebox();
      99             : 
     100             :             // Get an array-accessible handle to the data on this patch.
     101          74 :             amrex::Array4<const Set::Scalar> const& temp = (*temp_old_mf[lev]).array(mfi);
     102          74 :             amrex::Array4<Set::Scalar>       const& temp_new = (*temp_mf[lev]).array(mfi);
     103          74 :             amrex::Array4<const Set::Scalar> const& phi = (*phi_old_mf[lev]).array(mfi);
     104          74 :             amrex::Array4<Set::Scalar>       const& phi_new = (*phi_mf[lev]).array(mfi);
     105             : 
     106             :             // Iterate over the grid on this patch
     107          74 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     108             :             {
     109             : 
     110       10752 :                 Set::Vector gradphi = Numeric::Gradient(phi, i, j, k, 0, DX);
     111             : 
     112             :                 // 
     113             :                 // Calculate anisotropy function
     114             :                 // [ equation 2.12 in reference]
     115             :                 //
     116       10752 :                 Set::Scalar v44 = AMREX_D_TERM(
     117             :                     gradphi(0) * gradphi(0) * gradphi(0) * gradphi(0),
     118             :                     +gradphi(1) * gradphi(1) * gradphi(1) * gradphi(1),
     119             :                     +gradphi(2) * gradphi(2) * gradphi(2) * gradphi(2));
     120       10752 :                 Set::Scalar v22 = gradphi.squaredNorm(); v22 *= v22;
     121       32256 :                 Set::Scalar sigma = 1.0 - delta * (1.0 - v44 / (v22 + 1E-12));
     122             : 
     123             :                 // 
     124             :                 // Calculate anisotropic barrier height
     125             :                 // [ unnumbered equation on page 67 in the reference ]
     126             :                 //
     127       10752 :                 Set::Scalar m = -(alpha / Set::Constant::Pi) * std::atan(gamma * sigma * temp(i, j, k));
     128             : 
     129             : 
     130             :                 //
     131             :                 // Evolve order paramter
     132             :                 // [ equation 2.10 in the reference ]
     133             :                 //                
     134       10752 :                 Set::Scalar lapphi = Numeric::Laplacian(phi, i, j, k, 0, DX);
     135       64512 :                 Set::Scalar Dphi = (eps * eps * lapphi + phi(i, j, k) * (1.0 - phi(i, j, k)) * (phi(i, j, k) - 0.5 + m)) / tau;
     136       21504 :                 phi_new(i, j, k) = phi(i, j, k) + dt * Dphi;
     137             : 
     138             :                 //
     139             :                 // Evolve temperature
     140             :                 // [ equation 2.11 in the reference ]
     141             :                 //
     142       10752 :                 Set::Scalar laptemp = Numeric::Laplacian(temp, i, j, k, 0, DX);
     143       21504 :                 temp_new(i, j, k) = temp(i, j, k) + dt * (laptemp + Dphi);
     144       10752 :             });
     145             :         }
     146          14 :     }
     147             : 
     148             :     // Tag cells for mesh refinement based on temperature gradient
     149           9 :     void TagCellsForRefinement(int lev, amrex::TagBoxArray& a_tags, Set::Scalar /*time*/, int /*ngrow*/)
     150             :     {
     151             :         // Get cell dimensions as done above.
     152           9 :         const Set::Scalar* DX = geom[lev].CellSize();
     153             :         // Calculate the diagonal.
     154           9 :         Set::Scalar dr = sqrt(AMREX_D_TERM(DX[0] * DX[0], +DX[1] * DX[1], +DX[2] * DX[2]));
     155             : 
     156             :         // Iterate over the patches on this level
     157          33 :         for (amrex::MFIter mfi(*temp_mf[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
     158             :         {
     159             :             // Get the box and handles as done above.
     160          24 :             const amrex::Box& bx = mfi.tilebox();
     161          24 :             amrex::Array4<char>         const& tags = a_tags.array(mfi);
     162          24 :             amrex::Array4<Set::Scalar>  const& temp = (*temp_mf[lev]).array(mfi);
     163          24 :             amrex::Array4<Set::Scalar>  const& phi = (*phi_mf[lev]).array(mfi);
     164             : 
     165             :             // Iterate over the grid as done above.
     166          24 :             amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
     167             :             {
     168             :                 // Calculate the temperature gradient.
     169       17024 :                 Set::Vector grad_temp = Numeric::Gradient(temp, i, j, k, 0, DX);
     170       17024 :                 Set::Vector grad_phi = Numeric::Gradient(phi, i, j, k, 0, DX);
     171             : 
     172             :                 // Is the gradient * cell_size too big? If so, then
     173             :                 // mark this cell as needing refinement.
     174       17024 :                 if (grad_temp.lpNorm<2>() * dr > refinement_threshold_temp)
     175         278 :                     tags(i, j, k) = amrex::TagBox::SET;
     176       17024 :                 if (grad_phi.lpNorm<2>() * dr > refinement_threshold_phi)
     177         278 :                     tags(i, j, k) = amrex::TagBox::SET;
     178       17024 :             });
     179             :         }
     180           9 :     }
     181             : 
     182             : protected:
     183             :     Set::Field<Set::Scalar> temp_mf;
     184             :     Set::Field<Set::Scalar> temp_old_mf;
     185             :     Set::Field<Set::Scalar> phi_mf;
     186             :     Set::Field<Set::Scalar> phi_old_mf;
     187             : 
     188             : private:
     189             :     int number_of_components = 1;            // Number of components
     190             :     int number_of_ghost_cells = 1;           // Number of ghost cells
     191             : 
     192             : 
     193             :     // calculation of m
     194             :     Set::Scalar alpha, delta, gamma;
     195             :     // evolution of p
     196             :     Set::Scalar eps, tau;
     197             : 
     198             :     Set::Scalar refinement_threshold_temp = NAN; // Criterion for cell refinement
     199             :     Set::Scalar refinement_threshold_phi = NAN; // Criterion for cell refinement
     200             : 
     201             :     IC::IC* ic_temp, * ic_phi;
     202             :     BC::BC<Set::Scalar>* bc_temp, * bc_phi;
     203             : };
     204             : } // namespace Integrator
     205             : #endif

Generated by: LCOV version 1.14