Alamo
PhaseFieldMicrostructure.cpp
Go to the documentation of this file.
1
2#include <eigen3/Eigen/Eigenvalues>
3
4#include <cmath>
5
6#include <AMReX_SPACE.H>
7
10#include "BC/Constant.H"
11#include "Set/Set.H"
12#include "Util/Util.H"
13#include "IC/Random.H"
14#include "IC/Trig.H"
15#include "IC/Sphere.H"
16#include "IC/Expression.H"
18#include "Numeric/Stencil.H"
21#include "IC/Trig.H"
22
27
28#include "Util/MPI.H"
29
30namespace Integrator
31{
32template<class model_type>
34{
35 BL_PROFILE("PhaseFieldMicrostructure::Advance");
37 const Set::Scalar* DX = this->geom[lev].CellSize();
38
39 std::swap(eta_old_mf[lev], eta_mf[lev]);
40
41
42 Set::Scalar df_max = std::numeric_limits<Set::Scalar>::min();
43
44 Model::Interface::GB::SH gbmodel(0.0, 0.0, anisotropy.sigma0, anisotropy.sigma1);
45
46 for (amrex::MFIter mfi(*eta_mf[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
47 {
48 amrex::Box bx = mfi.tilebox();
49 Set::Patch<Set::Scalar> etanew = eta_mf.Patch(lev,mfi);
50 Set::Patch<const Set::Scalar> eta = eta_old_mf.Patch(lev,mfi);
51
52 Set::Scalar *df_max_handle = &df_max;
53
54 Set::Patch<const Set::Matrix> sigma = stress_mf.Patch(lev,mfi);
55 Set::Patch<const Set::Vector> disp = this->disp_mf.Patch(lev,mfi);
56
57 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
58 {
59 Set::Matrix sig = Set::Matrix::Zero();
60 if (pf.elastic_df) sig = Numeric::Interpolate::NodeToCellAverage(sigma, i, j, k, 0);
61
62 for (int m = 0; m < number_of_grains; m++)
63 {
64 //if (shearcouple.on) etaold(i,j,k,m) = eta(i,j,k,m);
65
66 Set::Scalar driving_force = 0.0;
67 Set::Scalar driving_force_threshold = NAN;
68 if (pf.threshold.on) driving_force_threshold = 0.0;
69 Set::Scalar kappa = NAN, mu = NAN;
70
71 //
72 // BOUNDARY TERM and SECOND ORDER REGULARIZATION
73 //
74
75 Set::Vector Deta = Numeric::Gradient(eta, i, j, k, m, DX);
76 Set::Scalar normgrad = Deta.lpNorm<2>();
77 if (normgrad < 1E-4)
78 continue; // This ought to speed things up.
79
80 Set::Matrix DDeta = Numeric::Hessian(eta, i, j, k, m, DX);
81 Set::Scalar laplacian = DDeta.trace();
82
83 if (!anisotropy.on || time < anisotropy.tstart)
84 {
85 kappa = pf.l_gb * 0.75 * pf.sigma0;
86 mu = 0.75 * (1.0 / 0.23) * pf.sigma0 / pf.l_gb;
87 if (pf.threshold.boundary) driving_force_threshold += -kappa * laplacian;
88 else driving_force += -kappa * laplacian;
89 }
90 else
91 {
92 Set::Matrix4<AMREX_SPACEDIM, Set::Sym::Full> DDDDEta = Numeric::DoubleHessian<AMREX_SPACEDIM>(eta, i, j, k, m, DX);
93 auto anisotropic_df = boundary->DrivingForce(Deta, DDeta, DDDDEta);
94 if (pf.threshold.boundary) driving_force_threshold += pf.l_gb * 0.75 * std::get<0>(anisotropic_df);
95 else driving_force += pf.l_gb * 0.75 * std::get<0>(anisotropic_df);
96 if (std::isnan(std::get<0>(anisotropic_df))) Util::Abort(INFO);
97 if (pf.threshold.boundary) driving_force_threshold += anisotropy.beta * std::get<1>(anisotropic_df);
98 else driving_force += anisotropy.beta * std::get<1>(anisotropic_df);
99 if (std::isnan(std::get<1>(anisotropic_df))) Util::Abort(INFO);
100 mu = 0.75 * (1.0 / 0.23) * boundary->W(Deta) / pf.l_gb;
101 }
102
103 //
104 // CHEMICAL POTENTIAL
105 //
106
107 Set::Scalar sum_of_squares = 0.;
108 for (int n = 0; n < number_of_grains; n++)
109 {
110 if (m == n)
111 continue;
112 sum_of_squares += eta(i, j, k, n) * eta(i, j, k, n);
113 }
114 if (pf.threshold.chempot)
115 driving_force_threshold += mu * (eta(i, j, k, m) * eta(i, j, k, m) - 1.0 + 2.0 * pf.gamma * sum_of_squares) * eta(i, j, k, m);
116 else
117 driving_force += mu * (eta(i, j, k, m) * eta(i, j, k, m) - 1.0 + 2.0 * pf.gamma * sum_of_squares) * eta(i, j, k, m);
118
119
120 //
121 // LAGRANGE MULTIPLIER
122 //
123 if (lagrange.on && m == 0 && time > lagrange.tstart)
124 {
125 if (pf.threshold.lagrange)
126 driving_force_threshold += lagrange.lambda * (volume - lagrange.vol0);
127 else
128 driving_force += lagrange.lambda * (volume - lagrange.vol0);
129 }
130
131 //
132 // SYNTHETIC DRIVING FORCE
133 //
134 if (sdf.on && time > sdf.tstart)
135 {
136 if (pf.threshold.sdf)
137 driving_force_threshold += sdf.val[m](time);
138 else
139 driving_force += sdf.val[m](time);
140 }
141
142
143 //
144 // ELASTIC DRIVING FORCE
145 //
146 if (pf.elastic_df)
147 {
148 Set::Scalar elastic_df_m = 0.0;
149
150 if (shearcouple.on)
151 {
152 for (int n = 0; n < number_of_grains; n++)
153 {
154 if (n==m) continue;
155 Set::Scalar etam = eta(i,j,k,m);
156 Set::Scalar etan = eta(i,j,k,n);
157
158 Set::Scalar sumsq = (etam*etam + etan*etan);
159 sumsq = sumsq * sumsq;
160
161 //Set::Scalar dgm = 2.0*etan*etan*etam / sumsq;
162 Set::Scalar dgn = 2.0*etan*etam*etam / sumsq;
163
164
165 Set::Matrix Fgbn = shearcouple.Fgb[n*number_of_grains + m];
166
167 if (model_type::kinvar == Model::Solid::KinematicVariable::F)
168 {
169 Set::Matrix F =
170 Set::Matrix::Identity() +
171 Numeric::NodeGradientOnCell(disp,i,j,k,DX);
172 Fgbn = F*Fgbn;
173 }
174
175 elastic_df_m += (sig.transpose() * Fgbn).trace() * dgn;
176 }
177 }
178 else
179 {
180 Set::Matrix F0avg = Set::Matrix::Zero();
181
182 for (int n = 0; n < number_of_grains; n++)
183 F0avg += eta(i, j, k, n) * mechanics.model[n].F0;
184
185 Set::Matrix dF0deta = Set::Matrix::Zero();
186
187 for (int n = 0; n < number_of_grains; n++)
188 {
189 if (n == m) continue;
190 Set::Scalar normsq = eta(i, j, k, m) * eta(i, j, k, m) + eta(i, j, k, n) * eta(i, j, k, n);
191 dF0deta += (2.0 * eta(i, j, k, m) * eta(i, j, k, n) * eta(i, j, k, n) *
192 (mechanics.model[m].F0 - mechanics.model[n].F0))
193 / normsq / normsq;
194 }
195
196 elastic_df_m += (dF0deta.transpose() * sig).trace();
197 }
198
199
200 if (pf.threshold.mechanics)
201 driving_force_threshold -= pf.elastic_mult * elastic_df_m;
202 else
203 driving_force -= pf.elastic_mult * elastic_df_m;
204 }
205
206 //
207 // Update eta
208 //
209 // Final mobility and threshold values:
210 Set::Scalar L = NAN, threshold = NAN;
211 // (if we are NOT using anisotropic kinetics)
212 if (!anisotropic_kinetics.on || time < anisotropic_kinetics.tstart)
213 {
214 L = pf.L;
215 threshold = pf.threshold.value;
216 }
217 // (if we ARE using anisotropic kinetics)
218 else
219 {
220 Set::Scalar theta = atan2(Deta(1), Deta(0));
221 L = (4. / 3.) * anisotropic_kinetics.mobility(theta) / pf.l_gb;
222 threshold = anisotropic_kinetics.threshold(theta);
223 }
224
225 Set::Scalar totaldf = 0.0;
226 if (pf.threshold.on)
227 {
228 if (driving_force_threshold > threshold)
229 {
230 if (pf.threshold.type == ThresholdType::Continuous)
231 totaldf -= L * (driving_force_threshold - threshold);
232 else if (pf.threshold.type == ThresholdType::Chop)
233 totaldf -= L * (driving_force_threshold);
234 }
235 else if (driving_force_threshold < -threshold)
236 {
237 if (pf.threshold.type == ThresholdType::Continuous)
238 totaldf -= L * (driving_force_threshold + threshold);
239 else if (pf.threshold.type == ThresholdType::Chop)
240 totaldf -= L * (driving_force_threshold);
241 }
242 }
243 totaldf -= L * driving_force;
244
245 // Final Eta Update
246 etanew(i, j, k, m) = eta(i,j,k,m) + dt * totaldf;
247 *df_max_handle = std::max(df_max, std::fabs(totaldf));
248 }
249 });
250 }
251
252 if (shearcouple.on && time >= mechanics.tstart) UpdateEigenstrain(lev);
253 //this->DynamicTimestep_SyncDrivingForce(lev,df_max);
254}
255
256template <class model_type>
258{
259 if (this->m_type == Base::Mechanics<model_type>::Disable) return;
260 eta_mf[lev]->FillBoundary();
261 eta_old_mf[lev]->FillBoundary();
262
263 amrex::Box domain = this->geom[lev].Domain();
264 domain.convert(amrex::IntVect::TheNodeVector());
265
266 for (amrex::MFIter mfi(*this->model_mf[lev], false); mfi.isValid(); ++mfi)
267 {
268 amrex::Box bx = mfi.grownnodaltilebox() & domain;
269 Set::Patch<const Set::Scalar> etaold = eta_old_mf.Patch(lev,mfi);
270 Set::Patch<const Set::Scalar> etanew = eta_mf.Patch(lev,mfi);
271 Set::Patch<model_type> model = model_mf.Patch(lev,mfi);
272 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
273 {
274 for (int m = 0; m < number_of_grains; m++)
275 for (int n = 0; n < number_of_grains; n++)
276 {
277 if (m==n) continue;
278 Set::Scalar emnew = Numeric::Interpolate::CellToNodeAverage(etanew, i, j, k, m);//, sten);
279 Set::Scalar emold = Numeric::Interpolate::CellToNodeAverage(etaold, i, j, k, m);//, sten);
280 Set::Scalar ennew = Numeric::Interpolate::CellToNodeAverage(etanew, i, j, k, n);//, sten);
281 Set::Scalar enold = Numeric::Interpolate::CellToNodeAverage(etaold, i, j, k, n);//, sten);
282 Set::Scalar gmnew = (emnew * emnew) / (emnew * emnew + ennew * ennew);
283 Set::Scalar gmold = (emold * emold) / (emold * emold + enold * enold);
284 model(i, j, k).F0 -= 0.5 * (gmnew - gmold) * shearcouple.Fgb[m*number_of_grains + n];
285 }
286 });
287 }
288
289 Util::RealFillBoundary(*this->model_mf[lev],this->geom[lev]);
290}
291
292template<class model_type>
294{
295 BL_PROFILE("PhaseFieldMicrostructure::Initialize");
297 ic->Initialize(lev, eta_mf);
298 ic->Initialize(lev, eta_old_mf);
299}
300
301template<class model_type>
302void PhaseFieldMicrostructure<model_type>::TagCellsForRefinement(int lev, amrex::TagBoxArray& a_tags, Set::Scalar time, int ngrow)
303{
304 BL_PROFILE("PhaseFieldMicrostructure::TagCellsForRefinement");
306 const amrex::Real* DX = this->geom[lev].CellSize();
307 const Set::Vector dx(DX);
308 const Set::Scalar dxnorm = dx.lpNorm<2>();
309
310 for (amrex::MFIter mfi(*eta_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
311 {
312 const amrex::Box& bx = mfi.tilebox();
313 amrex::Array4<const amrex::Real> const& etanew = (*eta_mf[lev]).array(mfi);
314 amrex::Array4<char> const& tags = a_tags.array(mfi);
315
316 for (int n = 0; n < number_of_grains; n++)
317 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
318 Set::Vector grad = Numeric::Gradient(etanew, i, j, k, n, DX);
319
320 if (dxnorm * grad.lpNorm<2>() > ref_threshold)
321 tags(i, j, k) = amrex::TagBox::SET;
322 });
323 }
324}
325
326template<class model_type>
328{
329 this->DynamicTimestep_Update();
330}
331
332template<class model_type>
334{
335 BL_PROFILE("PhaseFieldMicrostructure::UpdateModel");
336 if (a_step % this->m_interval) return;
337
338 for (int lev = 0; lev <= this->finest_level; ++lev)
339 {
340 amrex::Box domain = this->geom[lev].Domain();
341 domain.convert(amrex::IntVect::TheNodeVector());
342
343 eta_mf[lev]->FillBoundary();
344
345 for (MFIter mfi(*this->model_mf[lev], false); mfi.isValid(); ++mfi)
346 {
347 amrex::Box bx = mfi.grownnodaltilebox() & domain;
348
349 amrex::Array4<model_type> const& model = this->model_mf[lev]->array(mfi);
350 amrex::Array4<const Set::Scalar> const& eta = eta_mf[lev]->array(mfi);
351
352 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
353 {
354 std::vector<Set::Scalar> etas(number_of_grains);
355 for (int n = 0; n < number_of_grains; n++)
356 etas[n] = Numeric::Interpolate::CellToNodeAverage(eta, i, j, k, n);
357 model_type mix = model_type::Combine(mechanics.model, etas, mechanics.model_mix_order);
358 if (a_step == 0 || !shearcouple.on) // Do a complete model initialization
359 model(i,j,k) = mix ;
360 else // Otherwise, we will set the modulus only and leave the eigenstrain alone
361 {
362 Set::Matrix F0 = model(i,j,k).F0;
363 model(i,j,k) = mix;
364 model(i,j,k).F0 = F0;
365 }
366 });
367 }
368
369 if (mechanics.model_neumann_boundary)
370 {
371 Util::AssertException(INFO,TEST(AMREX_SPACEDIM==2),"neumann boundary works in 2d only");
372 for (MFIter mfi(*this->model_mf[lev], false); mfi.isValid(); ++mfi)
373 {
374 amrex::Box bx = mfi.grownnodaltilebox() & domain;
375 amrex::Array4<model_type> const& model = this->model_mf[lev]->array(mfi);
376 const Dim3 lo = amrex::lbound(domain), hi = amrex::ubound(domain);
377
378 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
379 {
380 if (i==lo.x && j==lo.y)
381 model(i,j,k) = 0.5*(model(i+1,j,k)+model(i,j+1,k));
382 else if (i==lo.x && j==hi.y)
383 model(i,j,k) = 0.5*(model(i+1,j,k)+model(i,j-1,k));
384 else if (i==hi.x && j==lo.y)
385 model(i,j,k) = 0.5*(model(i-1,j,k)+model(i,j+1,k));
386 else if (i==hi.x && j==hi.y)
387 model(i,j,k) = 0.5*(model(i-1,j,k)+model(i,j-1,k));
388
389 else if (i==lo.x)
390 model(i,j,k) = model(i+1,j,k);
391 else if (i==hi.x)
392 model(i,j,k) = model(i-1,j,k);
393 else if (j==lo.y)
394 model(i,j,k) = model(i,j+1,k);
395 else if (j==hi.y)
396 model(i,j,k) = model(i,j-1,k);
397 });
398 }
399 }
400
401 Util::RealFillBoundary(*this->model_mf[lev], this->geom[lev]);
402 }
403
404}
405
406template<class model_type>
408{
409 BL_PROFILE("PhaseFieldMicrostructure::TimeStepBegin");
410 for (int lev = 0; lev < totaldf_mf.size(); lev++) totaldf_mf[lev]->setVal(0.0);
411
412 // Insertion of disconnections
413 if (disconnection.on)
414 {
415 disconnection.model.Nucleate(eta_mf,this->Geom(),this->timestep,time, iter);
416 UpdateEigenstrain();
417 }
418
420
421 if (anisotropy.on && time >= anisotropy.tstart)
422 {
423 Util::AssertException( INFO,TEST(this->dynamictimestep.on == false),
424 "Cannot use anisotropy with dynamic timestep yet");
425 this->SetTimestep(anisotropy.timestep);
426 if (anisotropy.elastic_int > 0)
427 if (iter % anisotropy.elastic_int) return;
428 }
429}
430
431template<class model_type>
433 const amrex::MFIter& mfi, const amrex::Box& box)
434{
435 BL_PROFILE("PhaseFieldMicrostructure::Integrate");
436 Base::Mechanics<model_type>::Integrate(amrlev, time, step, mfi, box);
437
438 Model::Interface::GB::SH gbmodel(0.0, 0.0, anisotropy.sigma0, anisotropy.sigma1);
439 const amrex::Real* DX = this->geom[amrlev].CellSize();
440 Set::Scalar dv = AMREX_D_TERM(DX[0], *DX[1], *DX[2]);
441
442 amrex::Array4<amrex::Real> const& eta = (*eta_mf[amrlev]).array(mfi);
443 amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
444#if AMREX_SPACEDIM == 2
445 auto sten = Numeric::GetStencil(i, j, k, box);
446#endif
447
448 volume += eta(i, j, k, 0) * dv;
449
450 Set::Vector grad = Numeric::Gradient(eta, i, j, k, 0, DX);
451 Set::Scalar normgrad = grad.lpNorm<2>();
452
453 if (normgrad > 1E-8)
454 {
455 Set::Vector normal = grad / normgrad;
456
457 Set::Scalar da = normgrad * dv;
458 area += da;
459
460 if (!anisotropy.on || time < anisotropy.tstart)
461 {
462 gbenergy += pf.sigma0 * da;
463 Set::Scalar k = 0.75 * pf.sigma0 * pf.l_gb;
464 realgbenergy += 0.5 * k * normgrad * normgrad * dv;
465 regenergy = 0.0;
466 }
467 else
468 {
469#if AMREX_SPACEDIM == 2
470 Set::Scalar theta = atan2(grad(1), grad(0));
471 Set::Scalar sigma = boundary->W(theta);
472 gbenergy += sigma * da;
473
474 Set::Scalar k = 0.75 * sigma * pf.l_gb;
475 realgbenergy += 0.5 * k * normgrad * normgrad * dv;
476
477 Set::Matrix DDeta = Numeric::Hessian(eta, i, j, k, 0, DX, sten);
478 Set::Vector tangent(normal[1], -normal[0]);
479 Set::Scalar k2 = (DDeta * tangent).dot(tangent);
480 regenergy += 0.5 * anisotropy.beta * k2 * k2;
481#elif AMREX_SPACEDIM == 3
482 gbenergy += gbmodel.W(normal) * da;
483#endif
484 }
485 }
486 });
487}
488
492
493
494} // namespace Integrator
#define TEST(x)
Definition Util.H:21
#define INFO
Definition Util.H:20
virtual void TimeStepBegin(Set::Scalar a_time, int a_step) override
Definition Mechanics.H:163
void Integrate(int amrlev, Set::Scalar, int, const amrex::MFIter &mfi, const amrex::Box &a_box) override
Definition Mechanics.H:380
void Initialize(int lev) override
Use the #ic object to initialize::Temp.
Definition Mechanics.H:143
void Advance(int lev, Set::Scalar time, Set::Scalar dt) override
Definition Mechanics.H:241
void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar, int) override
Definition Mechanics.H:439
Solve the Allen-Cahn evolution equation for microstructure with parameters , where n corresponds to t...
void Advance(int lev, Real time, Real dt) override
Evolve phase field in time.
virtual void UpdateModel(int, Set::Scalar) override
void TagCellsForRefinement(int lev, amrex::TagBoxArray &tags, amrex::Real time, int ngrow) override
void Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &box) override
virtual void TimeStepBegin(amrex::Real time, int iter) override
virtual void TimeStepComplete(amrex::Real time, int iter) override
A 2D interface model class.
Definition SH.H:36
Set::Scalar W(const Set::Scalar a_theta, const Set::Scalar a_phi) const
Definition SH.H:60
Collection of numerical integrator objects.
Definition AllenCahn.H:41
static AMREX_FORCE_INLINE std::array< StencilType, AMREX_SPACEDIM > GetStencil(const int i, const int j, const int k, const amrex::Box domain)
Definition Stencil.H:36
AMREX_FORCE_INLINE Set::Matrix NodeGradientOnCell(const amrex::Array4< const Set::Vector > &f, const int &i, const int &j, const int &k, const Set::Scalar dx[AMREX_SPACEDIM])
Definition Stencil.H:811
AMREX_FORCE_INLINE Set::Matrix Hessian(const amrex::Array4< const Set::Scalar > &f, const int &i, const int &j, const int &k, const int &m, const Set::Scalar dx[AMREX_SPACEDIM], std::array< StencilType, AMREX_SPACEDIM > stencil=DefaultType)
Definition Stencil.H:946
AMREX_FORCE_INLINE Set::Vector Gradient(const amrex::Array4< const Set::Scalar > &f, const int &i, const int &j, const int &k, const int &m, const Set::Scalar dx[AMREX_SPACEDIM], std::array< StencilType, AMREX_SPACEDIM > stencil=DefaultType)
Definition Stencil.H:672
amrex::Real Scalar
Definition Base.H:19
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, 1 > Vector
Definition Base.H:20
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, AMREX_SPACEDIM > Matrix
Definition Base.H:23
AMREX_FORCE_INLINE void RealFillBoundary(amrex::FabArray< amrex::BaseFab< T > > &a_mf, const amrex::Geometry &)
Definition Util.H:322
AMREX_FORCE_INLINE void AssertException(std::string file, std::string func, int line, std::string smt, bool pass, Args const &... args)
Definition Util.H:233
void Abort(const char *msg)
Definition Util.cpp:170
static AMREX_FORCE_INLINE T NodeToCellAverage(const amrex::Array4< const T > &f, const int &i, const int &j, const int &k, const int &m)
Definition Stencil.H:1331
static AMREX_FORCE_INLINE T CellToNodeAverage(const amrex::Array4< const T > &f, const int &i, const int &j, const int &k, const int &m, std::array< StencilType, AMREX_SPACEDIM > stencil=DefaultType)
Definition Stencil.H:1293