Alamo
Fracture.H
Go to the documentation of this file.
1//
2// This class implements second and fourth order phase field brittle fracture with near singular solver.
3// This class inherits from the base class :code:`Base/Mechanics.H`.
4// See `this link <https://doi.org/10.1007/s00466-023-02325-8>`_
5// for more information on this implementation.
6//
7// The energy functional for a second order model is given by
8// .. math::
9//
10// \mathcal{L}= \int_\Omega \left[\left(g(c) + \eta \right)W_0^+ + W_0^-\right] dV + \int_\Omega G_c \left[ \frac{w(c)}{4\xi} + \xi |\nabla c|^2\right] dV
11//
12// where :math:`g(c)` and :math:`w(c)` are interpolation functions specified by the user.
13// The fracture energy :math:`G_c` and crack length scale :math:`xi` are also read from input file.
14// The tension compression asymmetry is accounted by splitting the strain energy :math:`W_0` as
15// .. math::
16//
17// W_0^\pm = \frac{1}{2} \lambda (\operatorname{tr}\bm{\varepsilon}_\pm)^2 + \mu \operatorname{tr}\left(\bm{\varepsilon}_\pm^2\right), \quad \bm{\varepsilon_\pm} = \sum_{i=1}^d \left(\varepsilon_i \right)_\pm \hat{\bm{v}}_i\otimes\hat{\bm{v}}_i
18//
19// where :math:`\lambda` and :math:`\mu` are material models for linear elastic isotropic material, :math:`\bm{\varepsilon}_\pm` are the positive and negative components of the strain tensor :math:`\bm{\varepsilon}` computed through eigenvalue decomposition.
20// The fourth order model adds a laplacian term to the free energy functional.
21// The code performs a staggered solve where it solves the elastic problem implicitly and crack problem explicitly.
22//
23// Class methods:
24//
25// #. :code:`Fracture()`:
26// Basic constructor. Does nothing, and leaves all values initiated as NAN.
27// #. :code:`Fracture(IO::ParmParse &pp)`:
28// Calls the parser.
29// #. :code:`static void Parse(Fracture &value, IO::ParmParse &pp)`
30// Parses input file, ICs, BCs, and sets up multifabs appropriately
31// #. :code:`void Initialize(int lev) override`
32// Calls IC and sets up the initial crack geometry.
33// #. :code:`virtual void UpdateModel(int a_step) override`
34// Performs degradation by updating the :math:`\psi` field using :math:`g(c)`.
35// #. :code:`void TimeStepBegin(Set::Scalar a_time, int a_step) override`
36// Solves the elastic problem, performs eigen value decomposition of strain, and computes the positive part of the strain energy.
37// #. :code:`void Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt)`
38// Advances the crack field by computing the variational derivative of energy functional with :math:`c`.
39// #. :code:`void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override`
40// Refines the grid based on the crack field.
41// #. :code:`void Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override`
42// Performs spatial integration of the driving force to check for convergence of crack problem
43// #. :code:`void TimeStepComplete(Set::Scalar /*time*/, int /* iter*/)`
44// Checks whether the solver should work on crack problem or elastic problem.
45//
46#ifndef INTEGRATOR_FRACTURE_H
47#define INTEGRATOR_FRACTURE_H
48
49#include "IO/ParmParse.H"
51
54
55#include "BC/Constant.H"
56#include "IC/BMP.H"
57#include "IC/Ellipse.H"
58// #include "IC/Ellipsoid.H"
59#include "IC/Expression.H"
60#include "IC/IC.H"
61#include "IC/Laminate.H"
62#include "IC/Notch.H"
63#include "IC/PNG.H"
65
66#include "Numeric/Stencil.H"
67
68#include <cmath>
69#include <eigen3/Eigen/Dense>
70
71namespace Integrator
72{
75
76class Fracture : virtual public Base::Mechanics<brittle_model>
77{
78public:
79 static constexpr const char *name = "fracture";
81
83 {
84 Parse(*this, pp);
85 }
86
87 static void
89 {
91
92 // Material field related parsing
93 pp.query("material.refinement_threshold", value.material.m_eta_ref_threshold);
94
95 // Driving force threshold
96 pp.query_default("driving_force_refinement_threshold", value.crack.driving_force_refinement_threshold, 1E100);
97
98 // Let's figure out if we are doing multi-material simulations.
99 std::string mat_ic_type;
100 if (pp.contains("material.ic.type"))
101 {
102 // IC determining material distribution
104 value.material.is_ic = true;
105 }
106 else
107 {
108 value.material.is_ic = false;
109 }
110
111 // Let's query material properties for different materials in the system
112 pp.queryclass_enumerate<brittle_model>("material.model", value.material.models);
113 value.material.num_mat = value.material.models.size();
114
115 // This is the fab that stores the material field.
116 // For a simple, single material simulation, this will be a uniform field with value 1 everywhere
117 value.RegisterNodalFab(value.material.eta_mf, value.material.num_mat, 2, "eta", true);
118
119 // Crack related parsing
120 pp.query_default("crack.refinement_threshold", value.crack.refinement_threshold, 0.0001); // mesh refinement criteria
121 pp.query_default("crack.df.beta", value.crack.beta, 0.0); // constant multiplier for fourth orther model with bilaplacian
122 pp.query_default("crack.df.tol_rel", value.crack.tol_rel, 1E-3); // relative tolerance for convergence of driving force
123 pp.query_default("crack.df.tol_abs", value.crack.tol_abs, 1E-3); // absolute tolerance for convergence of driving force
124 pp.query_default("crack.df.max_iter", value.crack.max_iter, 500.); // Maximum number of solver iterations
125 pp.query_default("crack.df.mult_Gc", value.crack.mult_Gc, 1.0); // constant multiplier for controlling fracture energy of interface
126 pp.query_default("crack.df.mult_lap", value.crack.mult_lap, 1.0); // constant multiplier for second order model with laplacian
127 pp.query_default("crack.df.el_mult", value.crack.el_mult, 1.0); // unit conversion multiplier between elastic problem and crack problem
128
129 // Let's figure out if there is an initial crack or void
130 pp.select_enumerate<IC::Constant,IC::Notch, IC::Ellipse, IC::Expression>("crack.ic", value.crack.ic, pp.forward_args(value.geom));
131
132 value.RegisterNodalFab(value.crack.c_mf, 1, 2, "crack", true);
133 value.RegisterNodalFab(value.crack.c_old_mf, 1, 2, "crack_old", true);
134 value.RegisterNodalFab(value.crack.driving_force_mf, 5, 2, "driving_force", true);
135 value.RegisterNodalFab(value.crack.energy_pristine_mf, 3, 2, "energy_pristine", true);
136 value.RegisterNodalFab(value.crack.history_var_mf, 3, 2, "history_variable", true);
137 value.RegisterIntegratedVariable(&(value.crack.driving_force_norm), "driving_force_norm");
138 value.RegisterIntegratedVariable(&(value.crack.crack_l2_err), "crack_l2_err");
139 value.RegisterIntegratedVariable(&(value.crack.crack_norm), "crack_norm");
140
141 // By default the psi variable will be on for fracture simulations
142 value.psi_on = true;
143 value.bc_psi = new BC::Constant(1, pp, "crack.bc"); // boundary conditions for crack
144 value.RegisterNewFab(value.psi_mf, value.bc_psi, 1, 2, "psi", true);
145
146 // This is needed for the specific crack model we are implementing
147 pp.queryclass_enumerate<pfczm_crack_type>("crack.model",value.crack.cracktype);
148
149
150 if (IO::ParmParse::InTraversalMode()) return;
151
152 Util::Assert(INFO, TEST(value.material.models.size() > 0));
153 Util::Assert(INFO, TEST(value.material.models.size() > 0));
154 }
155
156 void
157 Initialize(int lev) override
158 {
160
161 crack.c_mf[lev]->setVal(1.0);
162 crack.c_old_mf[lev]->setVal(1.0);
163
164 // For now, we are setting psi field to 1. This prevents nans.
165 // We will modify the psi field later.
166 psi_mf[lev]->setVal(1.0);
167
168 if (crack.is_ic)
169 {
170 for (unsigned int i = 0; i < crack.ic.size(); i++)
171 {
172 // This initializes the crack field.
173 crack.ic[i]->Add(lev, crack.c_mf);
174 crack.ic[i]->Add(lev, crack.c_old_mf);
175 }
176 }
177
178 // This initializes the material field
179 material.eta_mf[lev]->setVal(1.0);
180 if (material.is_ic)
181 material.ic->Initialize(lev, material.eta_mf);
182 else
183 material.eta_mf[lev]->setVal(1.0);
184
185 // set history variable to zero
186 crack.energy_pristine_mf[lev]->setVal(0.0);
187 crack.history_var_mf[lev]->setVal(0.0);
188 }
189
190 virtual void
191 UpdateModel(int a_step, Set::Scalar /*a_time*/) override
192 {
194 return;
195
196 // This sets the model field for the very first time step
197 // We should not be resetting model field everytime.
198 if (a_step == 0)
199 {
200 for (int lev = 0; lev <= finest_level; ++lev)
201 {
202 material.eta_mf[lev]->FillBoundary();
203
204 for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
205 {
206 amrex::Box bx = mfi.grownnodaltilebox();
207 amrex::Array4<brittle_model> const &model = model_mf[lev]->array(mfi);
208 amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
209 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
210 model(i, j, k) = brittle_model::Zero();
211 for (int n = 0; n < material.num_mat; n++)
212 model(i, j, k) += eta(i, j, k, n) * material.models[n];
213 });
214 }
215 Util::RealFillBoundary(*model_mf[lev], geom[lev]);
216 }
217 }
218
219 // This is where we perform "degradation"
220 // Essentially we will set the psi field based on the c^2 value.
221 for (int lev = 0; lev <= finest_level; ++lev)
222 {
223 crack.c_mf[lev]->FillBoundary();
224 for (MFIter mfi(*psi_mf[lev], false); mfi.isValid(); ++mfi)
225 {
226 amrex::Box bx = mfi.growntilebox();
227 amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
228 amrex::Array4<const Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
229 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
230 // psi(i,j,k,0) = crack.cracktype[0].g_phi(c(i,j,k,0));
231 psi(i, j, k, 0) = crack.cracktype[0].g_phi(Numeric::Interpolate::NodeToCellAverage(c, i, j, k, 0));
232 });
233 }
234 psi_mf[lev]->FillBoundary();
235 }
236 }
237
238 void
239 TimeStepBegin(Set::Scalar /*a_time*/, int a_step) override
240 {
241 // Deciding whether to do an elastic solve or not
242 Util::Message(INFO, crack.driving_force_norm, " ", crack.driving_force_reference, " ", crack.driving_force_reference_prev, " ", crack.tol_rel);
243 if ((crack.driving_force_norm / crack.driving_force_reference < crack.tol_rel) || crack.crack_prop_iter > crack.max_iter)
244 {
245 crack.crack_prop_iter = 0;
247 }
248 if ((crack.driving_force_norm < crack.tol_abs) || crack.crack_prop_iter > crack.max_iter)
249 {
251 crack.crack_prop_iter = 0;
252 }
254 {
255 Util::Message(INFO, "Load step = ", loadstep + 1);
257 crack.crack_prop_iter = 0;
258 increase_load_step = false;
259 set_new_reference = true;
260 // if (loadstep == 0) set_initial_df_once = true;
261 loadstep++;
262 }
263
264 // This means that the crack field has not converged.
266 {
267 crack.crack_prop_iter++;
268 return;
269 }
270
271 // Doing an elastic solve
273
274 // Computing pristine energy based on eigen value decomposition
275 for (int lev = 0; lev <= disp_mf.finest_level; lev++)
276 {
277 amrex::Box domain = geom[lev].Domain();
278 domain.convert(amrex::IntVect::TheNodeVector());
279 Set::Vector DX(geom[lev].CellSize());
280
281 for (MFIter mfi(*disp_mf[lev], false); mfi.isValid(); ++mfi)
282 {
283 amrex::Box bx = mfi.grownnodaltilebox(); // & domain;
284 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
285 amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
286 amrex::Array4<Set::Matrix> const &stress = stress_mf[lev]->array(mfi);
287 amrex::Array4<Set::Matrix> const &strain = strain_mf[lev]->array(mfi);
288 amrex::Array4<Set::Scalar> const &energy = crack.energy_pristine_mf[lev]->array(mfi);
289 amrex::Array4<Set::Scalar> const &history_var = crack.history_var_mf[lev]->array(mfi);
290
291 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
292 //==========================================================
293 // The formulation below uses decomposition of stress
294 // Set::Matrix sig = stress(i,j,k);
295 // Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
296 // Set::Vector eValues = eigensolver.eigenvalues();
297 // Set::Matrix eVectors = eigensolver.eigenvectors();
298
299 // Set::Matrix sig_p = Set::Matrix::Zero();
300 // Set::Matrix sig_n = Set::Matrix::Zero();
301
302 // for (int n = 0; n < AMREX_SPACEDIM; n++)
303 // {
304 // if (eValues(n) > 0.0) sig_p += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
305 // else sig_n += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
306 // }
307
308 // Set::Matrix sig_n_dev = sig_n - (1.0/3.0)*sig_n.trace()*Set::Matrix::Identity();
309
310 // for (int n = 0; n < material.num_mat; n++)
311 // {
312 // // computing energy based on tensile stress
313 // energy(i,j,k,0) += eta(i,j,k,n)*material.models[n].W2(sig_p);
314
315 // // compute energy from deviatoric portion
316 // energy(i,j,k,1) += eta(i,j,k,n) * material.models[n].W2(sig_n_dev);
317 // }
318
319 // // Only update energy if it is increasing. (H^+ according to Miehe)
320 // if (energy(i,j,k,0) < energy_old(i,j,k,0)) energy(i,j,k,0) = energy_old(i,j,k,0);
321 // if (energy(i,j,k,1) < energy_old(i,j,k,1)) energy(i,j,k,1) = energy_old(i,j,k,1);
322
323 //==========================================================
324 // The formulation below uses Ambati's decomposition of strain
325 // Perform eigenvalue decomposition of strain
326 Set::Matrix eps = strain(i, j, k);
327 Set::Matrix sig = stress(i, j, k);
328 energy(i, j, k, 0) = 0.0;
329 energy(i, j, k, 1) = 0.0;
330 energy(i, j, k, 2) = 0.0;
331
332 if (!crack.cracktype[0].mixed_mode())
333 {
334 // perform eigenvalue decomposition of strain tensor.
335 Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(eps);
336 Set::Vector eValues = eigensolver.eigenvalues();
337 Set::Matrix eVectors = eigensolver.eigenvectors();
338
339 // Reconstruct positive and negative counterparts of strain
340 Set::Matrix eps_p = Set::Matrix::Zero();
341 Set::Matrix eps_n = Set::Matrix::Zero();
342
343 for (int n = 0; n < AMREX_SPACEDIM; n++)
344 {
345 if (eValues(n) > 0.0)
346 eps_p += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
347 else
348 eps_n += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
349 }
350
351 for (int n = 0; n < material.num_mat; n++)
352 energy(i, j, k, 0) += eta(i, j, k, n) * material.models[n].W(eps_p);
353
354 if (energy(i, j, k, 0) > history_var(i, j, k, 0))
355 history_var(i, j, k, 0) = energy(i, j, k, 0);
356 }
357 else
358 {
359 // Here we implement the 2023 model by Wang et al (DOI: 10.1016/j.apm.2022.12.006)
360 Set::Vector crack_grad = Numeric::Gradient(c, i, j, k, 0, DX.data());
361 if (crack_grad.lpNorm<2>() > 1.e-4)
362 {
363
364 // perform eigenvalue decomposition of stress tensor.
365 Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
366 Set::Vector eValues = eigensolver.eigenvalues();
367 Set::Matrix eVectors = eigensolver.eigenvectors();
368
369 // Let's order the evalues
370 Set::Scalar sig1 = 0., sig2 = 0.;
371 Set::Vector vec1 = Set::Vector::Zero(), vec2 = Set::Vector::Zero();
372 if (eValues(0) > eValues(1))
373 {
374 sig1 = eValues(0);
375 sig2 = eValues(1);
376 vec1 = eVectors.col(0);
377 vec2 = eVectors.col(1);
378 }
379 else
380 {
381 sig1 = eValues(1);
382 sig2 = eValues(0);
383 vec1 = eVectors.col(1);
384 vec2 = eVectors.col(0);
385 }
386
387 Set::Scalar irwing_length = crack.cracktype[0].l_w();
388 Set::Scalar c_alpha = crack.cracktype[0].c_alpha();
389
390 Set::Scalar mohr_center = 0.5 * (sig1 + sig2);
391 Set::Scalar mohr_radius = 0.5 * std::abs(sig1 - sig2);
392
393 Set::Scalar f_theta = 0.0;
394
395 if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::WANG2023)
396 {
397 // Storing useful quantities for later.
398 Set::Scalar sig_t = crack.cracktype[0].sig_t();
399 Set::Scalar tau_f = crack.cracktype[0].tau_s();
400 Set::Scalar chi = crack.cracktype[0].chi();
401 Set::Scalar beta_bar = crack.cracktype[0].beta_bar();
402
403 Set::Scalar beta = 1.0;
404
405 // Now let's compute a candidate theta
406 // Ideally, theta = 0 is always a valid solution and corresponds to sig_nn = sig1, tau_nm = 0
407 // Set::Scalar theta = 0.0; // removed because: set but not used
408 beta = sig1 < 0 ? beta_bar : 1.0;
409 f_theta = (beta * (crack.el_mult * crack.el_mult * sig1 * sig1 / (sig_t * sig_t))) - 1;
410
411 // Now we check for other feasible solutions. First we check for beta = 1.
412 Set::Scalar sig_nn1 = 0., tau_nm1 = 0.;
413 Set::Scalar theta_candidate1 = 0.0, f_candidate1 = 0.;
414 Set::Scalar temp = std::abs((mohr_center / mohr_radius) * (chi * chi / (1.0 - (chi * chi))));
415 if (temp >= 0.0 && temp <= 1.0)
416 {
417 theta_candidate1 = 0.5 * std::acos(temp);
418
419 // Now check if this actually leas to a positive sig_nn value
420 sig_nn1 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate1));
421 tau_nm1 = mohr_radius * std::sin(2.0 * theta_candidate1);
422
423 if (sig_nn1 >= 0.0)
424 {
425 f_candidate1 = (crack.el_mult * crack.el_mult * sig_nn1 * sig_nn1 / (sig_t * sig_t)) + (crack.el_mult * crack.el_mult * tau_nm1 * tau_nm1 / (tau_f * tau_f)) - 1.0;
426
427 // We only use this solution if the value of the failure surface is larger.
428 if (f_candidate1 > f_theta)
429 {
430 f_theta = f_candidate1;
431 beta = 1.0;
432 }
433 }
434 }
435
436 // Now we check for the third feasible solution for beta = beta_bar.
437 Set::Scalar sig_nn2 = 0., tau_nm2 = 0.;
438 Set::Scalar theta_candidate2 = 0.0, f_candidate2 = 0.;
439 temp = std::abs((mohr_center / mohr_radius) * (beta_bar * chi * chi / (1.0 - (beta_bar * chi * chi))));
440 if (temp >= 0.0 && temp <= 1.0)
441 {
442 theta_candidate2 = 0.5 * std::acos(temp);
443
444 // Now check if this actually leas to a negative sig_nn value
445 sig_nn2 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate2));
446 tau_nm2 = mohr_radius * std::sin(2.0 * theta_candidate2);
447
448 if (sig_nn2 <= 0.0)
449 {
450 f_candidate2 = (beta_bar * crack.el_mult * crack.el_mult * sig_nn2 * sig_nn2 / (sig_t * sig_t)) + (crack.el_mult * crack.el_mult * tau_nm2 * tau_nm2 / (tau_f * tau_f)) - 1.0;
451
452 // We only use this solution if the value of the failure surface is larger.
453 if (f_candidate2 > f_theta)
454 {
455 f_theta = f_candidate2;
456 beta = beta_bar;
457 }
458 }
459 }
460 }
461 else if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::MC)
462 {
463 Set::Scalar cohesion = crack.cracktype[0].cohesion();
464 Set::Scalar friction = crack.cracktype[0].friction();
465 // In this case, the angle theta is straightforward.
466 // theta = (pi / 4) - (\phi / 2)
467 Set::Scalar sig_nn = 0., tau_nm = 0.;
468 Set::Scalar theta = std::atan(1) - std::atan(friction);
469
470 sig_nn = crack.el_mult * (mohr_center + (mohr_radius * std::cos(2.0 * theta)));
471 tau_nm = crack.el_mult * (mohr_radius * std::sin(2.0 * theta));
472
473 f_theta = (tau_nm + (friction * sig_nn)) * (tau_nm + (friction * sig_nn)) / (cohesion * cohesion);
474 f_theta = f_theta - 1.0;
475
476 // Util::Message(INFO, "sig1 = ", sig1, ", sig2 = ", sig2, ", f_theta = ", f_theta);
477 }
478
479 energy(i,j,k,0) = (f_theta > 0) ? (c_alpha / (2.0 * irwing_length)) * ((f_theta)) : 0.0;
480 energy(i,j,k,1) = 0.0;
481
482 if (energy(i, j, k, 0) + energy(i, j, k, 1) > history_var(i, j, k, 0) + history_var(i, j, k, 1))
483 {
484 history_var(i, j, k, 0) = energy(i, j, k, 0);
485 history_var(i, j, k, 1) = energy(i, j, k, 1);
486 }
487 }
488 }
489 //=====================================================================
490 });
491 }
492 Util::RealFillBoundary(*crack.energy_pristine_mf[lev], geom[lev]);
493 Util::RealFillBoundary(*crack.history_var_mf[lev], geom[lev]);
494 }
495
498 }
499
500 void
501 Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt) override
502 {
503 // advance for crack field
504 // Util::RealFillBoundary(*crack.c_old_mf[a_lev],geom[a_lev]);
505 crack.c_mf[a_lev]->FillBoundary();
506 // return;
507 std::swap(crack.c_old_mf[a_lev], crack.c_mf[a_lev]);
508
509 const Set::Scalar *DX = geom[a_lev].CellSize();
510 amrex::Box domain(geom[a_lev].Domain());
511 domain.convert(amrex::IntVect::TheNodeVector());
512 const amrex::Dim3 lo = amrex::lbound(domain), hi = amrex::ubound(domain);
513
514 // Trying out the predictor corrector approach.
515 //======================================================================
516 // Predictor step
517 //======================================================================
518 for (amrex::MFIter mfi(*crack.c_mf[a_lev], true); mfi.isValid(); ++mfi)
519 {
520 amrex::Box bx = mfi.tilebox();
521 // bx.grow(1);
522 // bx = bx & domain;
523
524 amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[a_lev]->array(mfi);
525 amrex::Array4<const Set::Scalar> const &energy = crack.history_var_mf[a_lev]->array(mfi);
526 amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[a_lev]->array(mfi);
527
528 amrex::Array4<Set::Scalar> const &c = crack.c_mf[a_lev]->array(mfi);
529 amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[a_lev]->array(mfi);
530
531 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
532#if AMREX_SPACEDIM != 2
533 Util::Abort(INFO, "This does not work for 1D or 3D yet.");
534#endif
535
536 if (i == lo.x && j == lo.y)
537 c(i, j, k, 0) = c(i + 1, j + 1, k, 0);
538 else if (i == lo.x && j == hi.y)
539 c(i, j, k, 0) = c(i + 1, j - 1, k, 0);
540 else if (i == hi.x && j == lo.y)
541 c(i, j, k, 0) = c(i - 1, j + 1, k, 0);
542 else if (i == hi.x && j == hi.y)
543 c(i, j, k, 0) = c(i - 1, j - 1, k, 0);
544 else if (i == lo.x)
545 c(i, j, k) = c(i + 1, j, k, 0);
546 else if (j == lo.y)
547 c(i, j, k) = c(i, j + 1, k, 0);
548 else if (i == hi.x)
549 c(i, j, k) = c(i - 1, j, k, 0);
550 else if (j == hi.y)
551 c(i, j, k) = c(i, j - 1, k, 0);
552
553 // Next, we are doing crack evolution
554 else
555 {
556 Set::Scalar rhs = 0.0;
557 Set::Scalar bilap = 0.0;
558 if (crack.beta > 0.0)
559 {
560 bilap = Numeric::Stencil<Set::Scalar, 4, 0, 0>::D(c_old, i, j, k, 0, DX) + Numeric::Stencil<Set::Scalar, 2, 2, 0>::D(c_old, i, j, k, 0, DX) * 2.0 + Numeric::Stencil<Set::Scalar, 0, 4, 0>::D(c_old, i, j, k, 0, DX);
561 }
562
563 // if (std::isnan(bilap)) Util::Message(INFO, "Bilaplacian is nan at (",i,", ",j,")");
564
565 Set::Scalar laplacian = Numeric::Laplacian(c_old, i, j, k, 0, DX);
566 if (std::isnan(laplacian))
567 Util::Message(INFO, "Laplacian is nan at (", i, ", ", j, ")");
568
569 Set::Scalar Gc = 0.0;
570 Set::Scalar Zeta = 0.0;
571 Set::Scalar Threshold = 0.0;
572 Set::Scalar Mobility = 0.0;
573 Set::Scalar _temp_product = 1.0, _temp_product2 = 1.0;
574
575 for (int m = 0; m < material.num_mat; m++)
576 {
577 Gc += eta(i, j, k, m) * crack.cracktype[m].Gc(c_old(i, j, k, 0));
578 Zeta += eta(i, j, k, m) * crack.cracktype[m].Zeta(c_old(i, j, k, 0));
579 Threshold += eta(i, j, k, m) * crack.cracktype[m].DrivingForceThreshold(c_old(i, j, k, 0));
580 Mobility += eta(i, j, k, m) * crack.cracktype[m].Mobility(c_old(i, j, k, 0));
581 _temp_product *= eta(i, j, k, m);
582 _temp_product2 *= 0.5;
583 }
584 if (material.num_mat > 1)
585 Gc *= (1.0 - _temp_product * (1. - crack.mult_Gc) / _temp_product2);
586
587 // =====================================================================
588 // Set::Scalar en_cell = energy(i, j, k, 0); // set but not used
589 if (!crack.cracktype[0].mixed_mode())
590 {
591 df(i, j, k, 0) = crack.cracktype[0].Dg_phi(c_old(i, j, k)) * (energy(i, j, k, 0) + energy(i, j, k, 1)) * crack.el_mult / Gc;
592 }
593 else
594 {
595 df(i, j, k, 0) = crack.cracktype[0].Dg_phi(c_old(i, j, k)) * (energy(i, j, k, 0) + energy(i, j, k, 1)); // * crack.el_mult;
596 }
597 rhs += df(i, j, k, 0);
598
599 df(i, j, k, 1) = crack.cracktype[0].Dw_phi(c_old(i, j, k, 0), 0.0) / (Zeta);
600 rhs += df(i, j, k, 1);
601
602 df(i, j, k, 2) = 2.0 * Zeta * laplacian * crack.mult_lap;
603 if (std::isnan(df(i, j, k, 2)))
604 Util::Message(INFO, "DF(2) is nan at (", i, ",", j, ")");
605 rhs -= df(i, j, k, 2);
606
607 df(i, j, k, 3) = crack.beta * (0.5 * Zeta * Zeta * Zeta) * bilap;
608 if (std::isnan(df(i, j, k, 3)))
609 Util::Message(INFO, "DF(3) is nan at (", i, ",", j, ")");
610 rhs += df(i, j, k, 3);
611
612 df(i, j, k, 4) = std::max(0., rhs - Threshold);
613 c(i, j, k, 0) = c_old(i, j, k, 0) - a_dt * df(i, j, k, 4) * Mobility; //*(4.*c_old(i,j,k,0) - 4.*c_old(i,j,k,0)*c_old(i,j,k,0));
614
615 if (c(i, j, k, 0) < 0.0)
616 c(i, j, k, 0) = 0.0;
617 if (c(i, j, k, 0) > 1.0)
618 c(i, j, k, 0) = 1.0;
619 }
620 });
621 }
622 crack.c_mf[a_lev]->FillBoundary();
623 crack.driving_force_mf[a_lev]->FillBoundary();
624
625 Base::Mechanics<brittle_model>::Advance(a_lev, a_time, a_dt);
626 }
627
628 void
629 TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override
630 {
631 Base::Mechanics<brittle_model>::TagCellsForRefinement(lev, a_tags, a_time, a_ngrow);
632
633 Set::Vector DX(geom[lev].CellSize());
634 Set::Scalar DXnorm = DX.lpNorm<2>();
635
636 for (amrex::MFIter mfi(*crack.c_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
637 {
638 amrex::Box bx = mfi.tilebox();
639 bx.convert(amrex::IntVect::TheCellVector());
640 amrex::Array4<char> const &tags = a_tags.array(mfi);
641 amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
642 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
643 Set::Vector grad = Numeric::NodeGradientOnCell(c, i, j, k, DX.data());
644 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
645 tags(i, j, k) = amrex::TagBox::SET;
646 });
647 }
648
649 for (amrex::MFIter mfi(*crack.driving_force_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
650 {
651 amrex::Box bx = mfi.tilebox();
652 bx.convert(amrex::IntVect::TheCellVector());
653 amrex::Array4<char> const &tags = a_tags.array(mfi);
654 amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[lev]->array(mfi);
655 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
656 Set::Vector grad = Numeric::Gradient(df, i, j, k, 0, DX.data());
657 if (grad.lpNorm<2>() * DXnorm > crack.driving_force_refinement_threshold)
658 tags(i, j, k) = amrex::TagBox::SET;
659 });
660 }
661
662 for (amrex::MFIter mfi(*psi_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
663 {
664 amrex::Box bx = mfi.tilebox();
665 bx.convert(amrex::IntVect::TheCellVector());
666 amrex::Array4<char> const &tags = a_tags.array(mfi);
667 amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
668 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
669 Set::Vector grad = Numeric::Gradient(psi, i, j, k, 0, DX.data());
670 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
671 tags(i, j, k) = amrex::TagBox::SET;
672 });
673 }
674
675 if (material.num_mat > 1)
676 {
677 for (amrex::MFIter mfi(*material.eta_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
678 {
679 amrex::Box bx = mfi.tilebox();
680 bx.convert(amrex::IntVect::TheCellVector());
681 amrex::Array4<char> const &tags = a_tags.array(mfi);
682 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
683 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
684 Set::Matrix grad = Numeric::Gradient(eta, i, j, k, DX.data());
685 if (grad.lpNorm<2>() * DXnorm > material.m_eta_ref_threshold)
686 tags(i, j, k) = amrex::TagBox::SET;
687 });
688 }
689 }
690 }
691
692 void
693 Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override
694 {
695 Set::Vector DX(geom[amrlev].CellSize());
696 const Set::Scalar DV = AMREX_D_TERM(DX[0], *DX[1], *DX[2]);
697 amrex::Array4<const Set::Scalar> const &df = crack.driving_force_mf[amrlev]->array(mfi);
698 // amrex::Array4<const Set::Scalar> const &c = crack.c_mf[amrlev]->array(mfi); // not used
699 // amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[amrlev]->array(mfi); // not used
700 amrex::ParallelFor(a_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
701 crack.driving_force_norm += df(i, j, k, 4) * DV;
702 });
703
704 Base::Mechanics<brittle_model>::Integrate(amrlev, time, step, mfi, a_box);
705 }
706
707 void
708 TimeStepComplete(Set::Scalar /*time*/, int /* iter*/) override
709 {
711 crack.driving_force_reference = crack.driving_force_norm;
712
714 {
715 Util::Message(INFO, "First setting the reference value");
716 crack.driving_force_reference_prev = crack.driving_force_reference;
717 set_initial_df_once = false;
718 }
719
720 if (crack.driving_force_reference / crack.driving_force_reference_prev < 0.1)
721 {
722 increase_load_step = true;
723 // crack.driving_force_reference_prev = crack.driving_force_reference;
724 }
726 {
727 crack.driving_force_reference_prev = crack.driving_force_reference;
728 set_new_reference = false;
729 }
730
731 elastic_do_solve_now = false;
732 }
733
734 // Member variables
735protected:
736 struct
737 {
743 std::vector<pfczm_crack_type> cracktype;
744
745 std::vector<std::string> ic_type;
746 std::vector<IC::IC<Set::Scalar> *> ic;
747 bool is_ic = true;
748
754
762
766
769 struct
770 {
773 std::vector<brittle_model> models;
775 bool is_ic = false;
776 int num_mat = 1;
778
782 bool set_new_reference = true;
783 int loadstep = 0;
784
786
788 using Base::Mechanics<brittle_model>::finest_level;
789 using Base::Mechanics<brittle_model>::geom;
793};
794}
795
796#endif
#define TEST(x)
Definition Util.H:25
#define INFO
Definition Util.H:24
Definition BC.H:43
Definition BMP.H:22
Pure abstract IC object from which all other IC objects inherit.
Definition IC.H:23
Initialize Laminates in a matrix.
Definition Laminate.H:16
Definition PNG.H:26
int queryclass_enumerate(std::string a_name, std::vector< T > &value, int number=1, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:1503
int query(std::string name, T &value, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:340
static ForwardArgs< ForwardArgStorage< Args >... > forward_args(Args &&... args)
Definition ParmParse.H:153
int query_default(std::string name, T &value, T defaultvalue, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:492
void select_enumerate(std::string a_name, std::vector< PTRTYPE * > &value, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:2069
static bool InTraversalMode()
Definition ParmParse.cpp:14
bool contains(std::string name, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:318
void select(std::string name, PTRTYPE *&ic_eta, const std::source_location &location=std::source_location::current())
Definition ParmParse.H:1883
virtual void TimeStepBegin(Set::Scalar a_time, int a_step) override
Definition Mechanics.H:191
void Integrate(int amrlev, Set::Scalar, int, const amrex::MFIter &mfi, const amrex::Box &a_box) override
Definition Mechanics.H:418
static void Parse(Mechanics &value, IO::ParmParse &pp)
Definition Mechanics.H:42
void Initialize(int lev) override
Use the #ic object to initialize::Temp.
Definition Mechanics.H:171
void Advance(int lev, Set::Scalar time, Set::Scalar dt) override
Definition Mechanics.H:278
void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar, int) override
Definition Mechanics.H:477
Set::Field< Set::Scalar > psi_mf
Definition Mechanics.H:503
Set::Field< brittle_model > model_mf
Definition Mechanics.H:501
Set::Scalar mult_lap
Definition Fracture.H:751
std::vector< pfczm_crack_type > cracktype
Definition Fracture.H:743
Set::Scalar scaleModulusMax
Definition Fracture.H:749
Set::Field< Set::Scalar > history_var_mf
Definition Fracture.H:741
Set::Field< Set::Scalar > eta_mf
Definition Fracture.H:771
IC::IC< Set::Scalar > * ic
Definition Fracture.H:774
BC::BC< Set::Scalar > * bc_psi
Definition Fracture.H:785
struct Integrator::Fracture::@7 material
Set::Scalar crack_l2_err
Definition Fracture.H:763
Set::Scalar el_mult
Definition Fracture.H:753
void TimeStepBegin(Set::Scalar, int a_step) override
Definition Fracture.H:239
Set::Field< Set::Scalar > c_old_mf
Definition Fracture.H:739
void TimeStepComplete(Set::Scalar, int) override
Definition Fracture.H:708
virtual void UpdateModel(int a_step, Set::Scalar) override
Definition Fracture.H:191
void Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt) override
Definition Fracture.H:501
Set::Scalar crack_prop_iter
Definition Fracture.H:765
Set::Field< Set::Scalar > energy_pristine_mf
Definition Fracture.H:740
struct Integrator::Fracture::@6 crack
Set::Scalar tol_rel
Definition Fracture.H:759
std::vector< brittle_model > models
Definition Fracture.H:773
Set::Scalar tol_abs
Definition Fracture.H:760
Set::Scalar m_eta_ref_threshold
Definition Fracture.H:772
Fracture(IO::ParmParse &pp)
Definition Fracture.H:82
Set::Scalar max_iter
Definition Fracture.H:761
Set::Scalar driving_force_reference_prev
Definition Fracture.H:756
void Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override
Definition Fracture.H:693
static constexpr const char * name
Definition Fracture.H:79
static void Parse(Fracture &value, IO::ParmParse &pp)
Definition Fracture.H:88
std::vector< std::string > ic_type
Definition Fracture.H:745
Set::Scalar refinement_threshold
Definition Fracture.H:750
Set::Scalar crack_norm
Definition Fracture.H:764
void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override
Definition Fracture.H:629
Set::Scalar mult_Gc
Definition Fracture.H:752
Set::Scalar driving_force_reference
Definition Fracture.H:755
Set::Field< Set::Scalar > driving_force_mf
Definition Fracture.H:742
Set::Scalar driving_force_refinement_threshold
Definition Fracture.H:758
std::vector< IC::IC< Set::Scalar > * > ic
Definition Fracture.H:746
Set::Field< Set::Scalar > c_mf
Definition Fracture.H:738
void Initialize(int lev) override
Definition Fracture.H:157
Set::Scalar driving_force_norm
Definition Fracture.H:757
Set::Scalar beta
Definition Fracture.H:767
void RegisterNodalFab(Set::Field< Set::Scalar > &new_fab, int ncomp, int nghost, std::string name, bool writeout, bool evolving=true, std::vector< std::string > suffix={})
Add a new node-based scalar field.
void RegisterNewFab(Set::Field< Set::Scalar > &new_fab, BC::BC< Set::Scalar > *new_bc, int ncomp, int nghost, std::string name, bool writeout, bool evolving=true, std::vector< std::string > suffix={})
Add a new cell-based scalar field.
bool integrate_variables_before_advance
Definition Integrator.H:407
void RegisterIntegratedVariable(Set::Scalar *integrated_variable, std::string name, bool extensive=true)
Register a variable to be integrated over the spatial domain using the Integrate function.
bool integrate_variables_after_advance
Definition Integrator.H:408
int finest_level
Definition Set.H:67
Collection of numerical integrator objects.
Definition AllenCahn.H:43
AMREX_FORCE_INLINE Set::Vector NodeGradientOnCell(const amrex::Array4< const Set::Scalar > &f, const int &i, const int &j, const int &k, const Set::Scalar dx[AMREX_SPACEDIM])
Definition Stencil.H:875
AMREX_GPU_HOST_DEVICE 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:687
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Set::Scalar Laplacian(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:561
amrex::Real Scalar
Definition Base.H:19
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, 1 > Vector
Definition Base.H:21
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, AMREX_SPACEDIM > Matrix
Definition Base.H:24
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void Assert(const char *file, const char *func, int line, const char *smt, bool pass, Args const &... args)
Definition Util.H:60
void Message(std::string file, std::string func, int line, Args const &... args)
Definition Util.H:140
AMREX_GPU_HOST_DEVICE void Abort(const char *msg)
Definition Util.cpp:406
AMREX_FORCE_INLINE void RealFillBoundary(amrex::FabArray< amrex::BaseFab< T > > &a_mf, const amrex::Geometry &, const int nghost=2)
Definition Util.H:350
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:1483