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
50
53
54#include "BC/Constant.H"
55#include "IC/BMP.H"
56#include "IC/Ellipse.H"
57// #include "IC/Ellipsoid.H"
58#include "IC/Expression.H"
59#include "IC/IC.H"
60#include "IC/Laminate.H"
61#include "IC/Notch.H"
62#include "IC/PNG.H"
64
65#include "Numeric/Stencil.H"
66
67#include <cmath>
68#include <eigen3/Eigen/Dense>
69
70namespace Integrator
71{
74
75class Fracture : virtual public Base::Mechanics<brittle_model>
76{
77public:
78 static constexpr const char *name = "fracture";
80
82 {
83 Parse(*this, pp);
84 }
85
86 static void
88 {
90
91 // Material field related parsing
92 pp.query("material.refinement_threshold", value.material.m_eta_ref_threshold);
93
94 // Driving force threshold
95 pp.query_default("driving_force_refinement_threshold", value.crack.driving_force_refinement_threshold, 1E100);
96
97 // Let's figure out if we are doing multi-material simulations.
98 std::string mat_ic_type;
99 if (pp.contains("material.ic.type"))
100 {
101 // IC determining material distribution
103 value.material.is_ic = true;
104 }
105 else
106 {
107 value.material.is_ic = false;
108 }
109
110 // Let's query material properties for different materials in the system
111 pp.queryclass_enumerate<brittle_model>("material.model", value.material.models);
112 value.material.num_mat = value.material.models.size();
113 Util::Assert(INFO, TEST(value.material.models.size() > 0));
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, 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 Util::Assert(INFO, TEST(value.material.models.size() > 0));
149 }
150
151 void
152 Initialize(int lev) override
153 {
155
156 crack.c_mf[lev]->setVal(1.0);
157 crack.c_old_mf[lev]->setVal(1.0);
158
159 // For now, we are setting psi field to 1. This prevents nans.
160 // We will modify the psi field later.
161 psi_mf[lev]->setVal(1.0);
162
163 if (crack.is_ic)
164 {
165 for (unsigned int i = 0; i < crack.ic.size(); i++)
166 {
167 // This initializes the crack field.
168 crack.ic[i]->Add(lev, crack.c_mf);
169 crack.ic[i]->Add(lev, crack.c_old_mf);
170 }
171 }
172
173 // This initializes the material field
174 material.eta_mf[lev]->setVal(1.0);
175 if (material.is_ic)
176 material.ic->Initialize(lev, material.eta_mf);
177 else
178 material.eta_mf[lev]->setVal(1.0);
179
180 // set history variable to zero
181 crack.energy_pristine_mf[lev]->setVal(0.0);
182 crack.history_var_mf[lev]->setVal(0.0);
183 }
184
185 virtual void
186 UpdateModel(int a_step, Set::Scalar /*a_time*/) override
187 {
189 return;
190
191 // This sets the model field for the very first time step
192 // We should not be resetting model field everytime.
193 if (a_step == 0)
194 {
195 for (int lev = 0; lev <= finest_level; ++lev)
196 {
197 material.eta_mf[lev]->FillBoundary();
198
199 for (MFIter mfi(*model_mf[lev], false); mfi.isValid(); ++mfi)
200 {
201 amrex::Box bx = mfi.grownnodaltilebox();
202 amrex::Array4<brittle_model> const &model = model_mf[lev]->array(mfi);
203 amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
204 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
205 model(i, j, k) = brittle_model::Zero();
206 for (int n = 0; n < material.num_mat; n++)
207 model(i, j, k) += eta(i, j, k, n) * material.models[n];
208 });
209 }
210 Util::RealFillBoundary(*model_mf[lev], geom[lev]);
211 }
212 }
213
214 // This is where we perform "degradation"
215 // Essentially we will set the psi field based on the c^2 value.
216 for (int lev = 0; lev <= finest_level; ++lev)
217 {
218 crack.c_mf[lev]->FillBoundary();
219 for (MFIter mfi(*psi_mf[lev], false); mfi.isValid(); ++mfi)
220 {
221 amrex::Box bx = mfi.growntilebox();
222 amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
223 amrex::Array4<const Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
224 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
225 // psi(i,j,k,0) = crack.cracktype[0].g_phi(c(i,j,k,0));
226 psi(i, j, k, 0) = crack.cracktype[0].g_phi(Numeric::Interpolate::NodeToCellAverage(c, i, j, k, 0));
227 });
228 }
229 psi_mf[lev]->FillBoundary();
230 }
231 }
232
233 void
234 TimeStepBegin(Set::Scalar /*a_time*/, int a_step) override
235 {
236 // Deciding whether to do an elastic solve or not
237 Util::Message(INFO, crack.driving_force_norm, " ", crack.driving_force_reference, " ", crack.driving_force_reference_prev, " ", crack.tol_rel);
238 if ((crack.driving_force_norm / crack.driving_force_reference < crack.tol_rel) || crack.crack_prop_iter > crack.max_iter)
239 {
240 crack.crack_prop_iter = 0;
242 }
243 if ((crack.driving_force_norm < crack.tol_abs) || crack.crack_prop_iter > crack.max_iter)
244 {
246 crack.crack_prop_iter = 0;
247 }
249 {
250 Util::Message(INFO, "Load step = ", loadstep + 1);
252 crack.crack_prop_iter = 0;
253 increase_load_step = false;
254 set_new_reference = true;
255 // if (loadstep == 0) set_initial_df_once = true;
256 loadstep++;
257 }
258
259 // This means that the crack field has not converged.
261 {
262 crack.crack_prop_iter++;
263 return;
264 }
265
266 // Doing an elastic solve
268
269 // Computing pristine energy based on eigen value decomposition
270 for (int lev = 0; lev <= disp_mf.finest_level; lev++)
271 {
272 amrex::Box domain = geom[lev].Domain();
273 domain.convert(amrex::IntVect::TheNodeVector());
274 Set::Vector DX(geom[lev].CellSize());
275
276 for (MFIter mfi(*disp_mf[lev], false); mfi.isValid(); ++mfi)
277 {
278 amrex::Box bx = mfi.grownnodaltilebox(); // & domain;
279 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
280 amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
281 amrex::Array4<Set::Matrix> const &stress = stress_mf[lev]->array(mfi);
282 amrex::Array4<Set::Matrix> const &strain = strain_mf[lev]->array(mfi);
283 amrex::Array4<Set::Scalar> const &energy = crack.energy_pristine_mf[lev]->array(mfi);
284 amrex::Array4<Set::Scalar> const &history_var = crack.history_var_mf[lev]->array(mfi);
285
286 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
287 //==========================================================
288 // The formulation below uses decomposition of stress
289 // Set::Matrix sig = stress(i,j,k);
290 // Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
291 // Set::Vector eValues = eigensolver.eigenvalues();
292 // Set::Matrix eVectors = eigensolver.eigenvectors();
293
294 // Set::Matrix sig_p = Set::Matrix::Zero();
295 // Set::Matrix sig_n = Set::Matrix::Zero();
296
297 // for (int n = 0; n < AMREX_SPACEDIM; n++)
298 // {
299 // if (eValues(n) > 0.0) sig_p += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
300 // else sig_n += eValues(n)*(eVectors.col(n)*eVectors.col(n).transpose());
301 // }
302
303 // Set::Matrix sig_n_dev = sig_n - (1.0/3.0)*sig_n.trace()*Set::Matrix::Identity();
304
305 // for (int n = 0; n < material.num_mat; n++)
306 // {
307 // // computing energy based on tensile stress
308 // energy(i,j,k,0) += eta(i,j,k,n)*material.models[n].W2(sig_p);
309
310 // // compute energy from deviatoric portion
311 // energy(i,j,k,1) += eta(i,j,k,n) * material.models[n].W2(sig_n_dev);
312 // }
313
314 // // Only update energy if it is increasing. (H^+ according to Miehe)
315 // if (energy(i,j,k,0) < energy_old(i,j,k,0)) energy(i,j,k,0) = energy_old(i,j,k,0);
316 // if (energy(i,j,k,1) < energy_old(i,j,k,1)) energy(i,j,k,1) = energy_old(i,j,k,1);
317
318 //==========================================================
319 // The formulation below uses Ambati's decomposition of strain
320 // Perform eigenvalue decomposition of strain
321 Set::Matrix eps = strain(i, j, k);
322 Set::Matrix sig = stress(i, j, k);
323 energy(i, j, k, 0) = 0.0;
324 energy(i, j, k, 1) = 0.0;
325 energy(i, j, k, 2) = 0.0;
326
327 if (!crack.cracktype[0].mixed_mode())
328 {
329 // perform eigenvalue decomposition of strain tensor.
330 Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(eps);
331 Set::Vector eValues = eigensolver.eigenvalues();
332 Set::Matrix eVectors = eigensolver.eigenvectors();
333
334 // Reconstruct positive and negative counterparts of strain
335 Set::Matrix eps_p = Set::Matrix::Zero();
336 Set::Matrix eps_n = Set::Matrix::Zero();
337
338 for (int n = 0; n < AMREX_SPACEDIM; n++)
339 {
340 if (eValues(n) > 0.0)
341 eps_p += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
342 else
343 eps_n += eValues(n) * (eVectors.col(n) * eVectors.col(n).transpose());
344 }
345
346 for (int n = 0; n < material.num_mat; n++)
347 energy(i, j, k, 0) += eta(i, j, k, n) * material.models[n].W(eps_p);
348
349 if (energy(i, j, k, 0) > history_var(i, j, k, 0))
350 history_var(i, j, k, 0) = energy(i, j, k, 0);
351 }
352 else
353 {
354 // Here we implement the 2023 model by Wang et al (DOI: 10.1016/j.apm.2022.12.006)
355 Set::Vector crack_grad = Numeric::Gradient(c, i, j, k, 0, DX.data());
356 if (crack_grad.lpNorm<2>() > 1.e-4)
357 {
358
359 // perform eigenvalue decomposition of stress tensor.
360 Eigen::SelfAdjointEigenSolver<Set::Matrix> eigensolver(sig);
361 Set::Vector eValues = eigensolver.eigenvalues();
362 Set::Matrix eVectors = eigensolver.eigenvectors();
363
364 // Let's order the evalues
365 Set::Scalar sig1 = 0., sig2 = 0.;
366 Set::Vector vec1 = Set::Vector::Zero(), vec2 = Set::Vector::Zero();
367 if (eValues(0) > eValues(1))
368 {
369 sig1 = eValues(0);
370 sig2 = eValues(1);
371 vec1 = eVectors.col(0);
372 vec2 = eVectors.col(1);
373 }
374 else
375 {
376 sig1 = eValues(1);
377 sig2 = eValues(0);
378 vec1 = eVectors.col(1);
379 vec2 = eVectors.col(0);
380 }
381
382 Set::Scalar irwing_length = crack.cracktype[0].l_w();
383 Set::Scalar c_alpha = crack.cracktype[0].c_alpha();
384
385 Set::Scalar mohr_center = 0.5 * (sig1 + sig2);
386 Set::Scalar mohr_radius = 0.5 * std::abs(sig1 - sig2);
387
388 Set::Scalar f_theta = 0.0;
389
390 if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::WANG2023)
391 {
392 // Storing useful quantities for later.
393 Set::Scalar sig_t = crack.cracktype[0].sig_t();
394 Set::Scalar tau_f = crack.cracktype[0].tau_s();
395 Set::Scalar chi = crack.cracktype[0].chi();
396 Set::Scalar beta_bar = crack.cracktype[0].beta_bar();
397
398 Set::Scalar beta = 1.0;
399
400 // Now let's compute a candidate theta
401 // Ideally, theta = 0 is always a valid solution and corresponds to sig_nn = sig1, tau_nm = 0
402 // Set::Scalar theta = 0.0; // removed because: set but not used
403 beta = sig1 < 0 ? beta_bar : 1.0;
404 f_theta = (beta * (crack.el_mult * crack.el_mult * sig1 * sig1 / (sig_t * sig_t))) - 1;
405
406 // Now we check for other feasible solutions. First we check for beta = 1.
407 Set::Scalar sig_nn1 = 0., tau_nm1 = 0.;
408 Set::Scalar theta_candidate1 = 0.0, f_candidate1 = 0.;
409 Set::Scalar temp = std::abs((mohr_center / mohr_radius) * (chi * chi / (1.0 - (chi * chi))));
410 if (temp >= 0.0 && temp <= 1.0)
411 {
412 theta_candidate1 = 0.5 * std::acos(temp);
413
414 // Now check if this actually leas to a positive sig_nn value
415 sig_nn1 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate1));
416 tau_nm1 = mohr_radius * std::sin(2.0 * theta_candidate1);
417
418 if (sig_nn1 >= 0.0)
419 {
420 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;
421
422 // We only use this solution if the value of the failure surface is larger.
423 if (f_candidate1 > f_theta)
424 {
425 f_theta = f_candidate1;
426 beta = 1.0;
427 }
428 }
429 }
430
431 // Now we check for the third feasible solution for beta = beta_bar.
432 Set::Scalar sig_nn2 = 0., tau_nm2 = 0.;
433 Set::Scalar theta_candidate2 = 0.0, f_candidate2 = 0.;
434 temp = std::abs((mohr_center / mohr_radius) * (beta_bar * chi * chi / (1.0 - (beta_bar * chi * chi))));
435 if (temp >= 0.0 && temp <= 1.0)
436 {
437 theta_candidate2 = 0.5 * std::acos(temp);
438
439 // Now check if this actually leas to a negative sig_nn value
440 sig_nn2 = mohr_center + (mohr_radius * std::cos(2.0 * theta_candidate2));
441 tau_nm2 = mohr_radius * std::sin(2.0 * theta_candidate2);
442
443 if (sig_nn2 <= 0.0)
444 {
445 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;
446
447 // We only use this solution if the value of the failure surface is larger.
448 if (f_candidate2 > f_theta)
449 {
450 f_theta = f_candidate2;
451 beta = beta_bar;
452 }
453 }
454 }
455 }
456 else if (crack.cracktype[0].failure_surface() == Model::Interface::Crack::PFCZM::FSType::MC)
457 {
458 Set::Scalar cohesion = crack.cracktype[0].cohesion();
459 Set::Scalar friction = crack.cracktype[0].friction();
460 // In this case, the angle theta is straightforward.
461 // theta = (pi / 4) - (\phi / 2)
462 Set::Scalar sig_nn = 0., tau_nm = 0.;
463 Set::Scalar theta = std::atan(1) - std::atan(friction);
464
465 sig_nn = crack.el_mult * (mohr_center + (mohr_radius * std::cos(2.0 * theta)));
466 tau_nm = crack.el_mult * (mohr_radius * std::sin(2.0 * theta));
467
468 f_theta = (tau_nm + (friction * sig_nn)) * (tau_nm + (friction * sig_nn)) / (cohesion * cohesion);
469 f_theta = f_theta - 1.0;
470
471 // Util::Message(INFO, "sig1 = ", sig1, ", sig2 = ", sig2, ", f_theta = ", f_theta);
472 }
473
474 energy(i,j,k,0) = (f_theta > 0) ? (c_alpha / (2.0 * irwing_length)) * ((f_theta)) : 0.0;
475 energy(i,j,k,1) = 0.0;
476
477 if (energy(i, j, k, 0) + energy(i, j, k, 1) > history_var(i, j, k, 0) + history_var(i, j, k, 1))
478 {
479 history_var(i, j, k, 0) = energy(i, j, k, 0);
480 history_var(i, j, k, 1) = energy(i, j, k, 1);
481 }
482 }
483 }
484 //=====================================================================
485 });
486 }
487 Util::RealFillBoundary(*crack.energy_pristine_mf[lev], geom[lev]);
488 Util::RealFillBoundary(*crack.history_var_mf[lev], geom[lev]);
489 }
490
493 }
494
495 void
496 Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt) override
497 {
498 // advance for crack field
499 // Util::RealFillBoundary(*crack.c_old_mf[a_lev],geom[a_lev]);
500 crack.c_mf[a_lev]->FillBoundary();
501 // return;
502 std::swap(crack.c_old_mf[a_lev], crack.c_mf[a_lev]);
503
504 const Set::Scalar *DX = geom[a_lev].CellSize();
505 amrex::Box domain(geom[a_lev].Domain());
506 domain.convert(amrex::IntVect::TheNodeVector());
507 const amrex::Dim3 lo = amrex::lbound(domain), hi = amrex::ubound(domain);
508
509 // Trying out the predictor corrector approach.
510 //======================================================================
511 // Predictor step
512 //======================================================================
513 for (amrex::MFIter mfi(*crack.c_mf[a_lev], true); mfi.isValid(); ++mfi)
514 {
515 amrex::Box bx = mfi.tilebox();
516 // bx.grow(1);
517 // bx = bx & domain;
518
519 amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[a_lev]->array(mfi);
520 amrex::Array4<const Set::Scalar> const &energy = crack.history_var_mf[a_lev]->array(mfi);
521 amrex::Array4<const Set::Scalar> const &eta = material.eta_mf[a_lev]->array(mfi);
522
523 amrex::Array4<Set::Scalar> const &c = crack.c_mf[a_lev]->array(mfi);
524 amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[a_lev]->array(mfi);
525
526 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
527#if AMREX_SPACEDIM != 2
528 Util::Abort(INFO, "This does not work for 1D or 3D yet.");
529#endif
530
531 if (i == lo.x && j == lo.y)
532 c(i, j, k, 0) = c(i + 1, j + 1, k, 0);
533 else if (i == lo.x && j == hi.y)
534 c(i, j, k, 0) = c(i + 1, j - 1, k, 0);
535 else if (i == hi.x && j == lo.y)
536 c(i, j, k, 0) = c(i - 1, j + 1, k, 0);
537 else if (i == hi.x && j == hi.y)
538 c(i, j, k, 0) = c(i - 1, j - 1, k, 0);
539 else if (i == lo.x)
540 c(i, j, k) = c(i + 1, j, k, 0);
541 else if (j == lo.y)
542 c(i, j, k) = c(i, j + 1, k, 0);
543 else if (i == hi.x)
544 c(i, j, k) = c(i - 1, j, k, 0);
545 else if (j == hi.y)
546 c(i, j, k) = c(i, j - 1, k, 0);
547
548 // Next, we are doing crack evolution
549 else
550 {
551 Set::Scalar rhs = 0.0;
552 Set::Scalar bilap = 0.0;
553 if (crack.beta > 0.0)
554 {
555 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);
556 }
557
558 // if (std::isnan(bilap)) Util::Message(INFO, "Bilaplacian is nan at (",i,", ",j,")");
559
560 Set::Scalar laplacian = Numeric::Laplacian(c_old, i, j, k, 0, DX);
561 if (std::isnan(laplacian))
562 Util::Message(INFO, "Laplacian is nan at (", i, ", ", j, ")");
563
564 Set::Scalar Gc = 0.0;
565 Set::Scalar Zeta = 0.0;
566 Set::Scalar Threshold = 0.0;
567 Set::Scalar Mobility = 0.0;
568 Set::Scalar _temp_product = 1.0, _temp_product2 = 1.0;
569
570 for (int m = 0; m < material.num_mat; m++)
571 {
572 Gc += eta(i, j, k, m) * crack.cracktype[m].Gc(c_old(i, j, k, 0));
573 Zeta += eta(i, j, k, m) * crack.cracktype[m].Zeta(c_old(i, j, k, 0));
574 Threshold += eta(i, j, k, m) * crack.cracktype[m].DrivingForceThreshold(c_old(i, j, k, 0));
575 Mobility += eta(i, j, k, m) * crack.cracktype[m].Mobility(c_old(i, j, k, 0));
576 _temp_product *= eta(i, j, k, m);
577 _temp_product2 *= 0.5;
578 }
579 if (material.num_mat > 1)
580 Gc *= (1.0 - _temp_product * (1. - crack.mult_Gc) / _temp_product2);
581
582 // =====================================================================
583 // Set::Scalar en_cell = energy(i, j, k, 0); // set but not used
584 if (!crack.cracktype[0].mixed_mode())
585 {
586 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;
587 }
588 else
589 {
590 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;
591 }
592 rhs += df(i, j, k, 0);
593
594 df(i, j, k, 1) = crack.cracktype[0].Dw_phi(c_old(i, j, k, 0), 0.0) / (Zeta);
595 rhs += df(i, j, k, 1);
596
597 df(i, j, k, 2) = 2.0 * Zeta * laplacian * crack.mult_lap;
598 if (std::isnan(df(i, j, k, 2)))
599 Util::Message(INFO, "DF(2) is nan at (", i, ",", j, ")");
600 rhs -= df(i, j, k, 2);
601
602 df(i, j, k, 3) = crack.beta * (0.5 * Zeta * Zeta * Zeta) * bilap;
603 if (std::isnan(df(i, j, k, 3)))
604 Util::Message(INFO, "DF(3) is nan at (", i, ",", j, ")");
605 rhs += df(i, j, k, 3);
606
607 df(i, j, k, 4) = std::max(0., rhs - Threshold);
608 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));
609
610 if (c(i, j, k, 0) < 0.0)
611 c(i, j, k, 0) = 0.0;
612 if (c(i, j, k, 0) > 1.0)
613 c(i, j, k, 0) = 1.0;
614 }
615 });
616 }
617 crack.c_mf[a_lev]->FillBoundary();
618 crack.driving_force_mf[a_lev]->FillBoundary();
619
620 Base::Mechanics<brittle_model>::Advance(a_lev, a_time, a_dt);
621 }
622
623 void
624 TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override
625 {
626 Base::Mechanics<brittle_model>::TagCellsForRefinement(lev, a_tags, a_time, a_ngrow);
627
628 Set::Vector DX(geom[lev].CellSize());
629 Set::Scalar DXnorm = DX.lpNorm<2>();
630
631 for (amrex::MFIter mfi(*crack.c_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
632 {
633 amrex::Box bx = mfi.tilebox();
634 bx.convert(amrex::IntVect::TheCellVector());
635 amrex::Array4<char> const &tags = a_tags.array(mfi);
636 amrex::Array4<Set::Scalar> const &c = crack.c_mf[lev]->array(mfi);
637 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
638 Set::Vector grad = Numeric::NodeGradientOnCell(c, i, j, k, DX.data());
639 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
640 tags(i, j, k) = amrex::TagBox::SET;
641 });
642 }
643
644 for (amrex::MFIter mfi(*crack.driving_force_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
645 {
646 amrex::Box bx = mfi.tilebox();
647 bx.convert(amrex::IntVect::TheCellVector());
648 amrex::Array4<char> const &tags = a_tags.array(mfi);
649 amrex::Array4<Set::Scalar> const &df = crack.driving_force_mf[lev]->array(mfi);
650 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
651 Set::Vector grad = Numeric::Gradient(df, i, j, k, 0, DX.data());
652 if (grad.lpNorm<2>() * DXnorm > crack.driving_force_refinement_threshold)
653 tags(i, j, k) = amrex::TagBox::SET;
654 });
655 }
656
657 for (amrex::MFIter mfi(*psi_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
658 {
659 amrex::Box bx = mfi.tilebox();
660 bx.convert(amrex::IntVect::TheCellVector());
661 amrex::Array4<char> const &tags = a_tags.array(mfi);
662 amrex::Array4<Set::Scalar> const &psi = psi_mf[lev]->array(mfi);
663 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
664 Set::Vector grad = Numeric::Gradient(psi, i, j, k, 0, DX.data());
665 if (grad.lpNorm<2>() * DXnorm > crack.refinement_threshold)
666 tags(i, j, k) = amrex::TagBox::SET;
667 });
668 }
669
670 if (material.num_mat > 1)
671 {
672 for (amrex::MFIter mfi(*material.eta_mf[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
673 {
674 amrex::Box bx = mfi.tilebox();
675 bx.convert(amrex::IntVect::TheCellVector());
676 amrex::Array4<char> const &tags = a_tags.array(mfi);
677 amrex::Array4<Set::Scalar> const &eta = material.eta_mf[lev]->array(mfi);
678 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
679 Set::Matrix grad = Numeric::Gradient(eta, i, j, k, DX.data());
680 if (grad.lpNorm<2>() * DXnorm > material.m_eta_ref_threshold)
681 tags(i, j, k) = amrex::TagBox::SET;
682 });
683 }
684 }
685 }
686
687 void
688 Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override
689 {
690 Set::Vector DX(geom[amrlev].CellSize());
691 const Set::Scalar DV = AMREX_D_TERM(DX[0], *DX[1], *DX[2]);
692 amrex::Array4<const Set::Scalar> const &df = crack.driving_force_mf[amrlev]->array(mfi);
693 // amrex::Array4<const Set::Scalar> const &c = crack.c_mf[amrlev]->array(mfi); // not used
694 // amrex::Array4<const Set::Scalar> const &c_old = crack.c_old_mf[amrlev]->array(mfi); // not used
695 amrex::ParallelFor(a_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) {
696 crack.driving_force_norm += df(i, j, k, 4) * DV;
697 });
698
699 Base::Mechanics<brittle_model>::Integrate(amrlev, time, step, mfi, a_box);
700 }
701
702 void
703 TimeStepComplete(Set::Scalar /*time*/, int /* iter*/) override
704 {
706 crack.driving_force_reference = crack.driving_force_norm;
707
709 {
710 Util::Message(INFO, "First setting the reference value");
711 crack.driving_force_reference_prev = crack.driving_force_reference;
712 set_initial_df_once = false;
713 }
714
715 if (crack.driving_force_reference / crack.driving_force_reference_prev < 0.1)
716 {
717 increase_load_step = true;
718 // crack.driving_force_reference_prev = crack.driving_force_reference;
719 }
721 {
722 crack.driving_force_reference_prev = crack.driving_force_reference;
723 set_new_reference = false;
724 }
725
726 elastic_do_solve_now = false;
727 }
728
729 // Member variables
730protected:
731 struct
732 {
738 std::vector<pfczm_crack_type> cracktype;
739
740 std::vector<std::string> ic_type;
741 std::vector<IC::IC<Set::Scalar> *> ic;
742 bool is_ic = true;
743
749
757
761
764 struct
765 {
768 std::vector<brittle_model> models;
770 bool is_ic = false;
771 int num_mat = 1;
773
777 bool set_new_reference = true;
778 int loadstep = 0;
779
781
783 using Base::Mechanics<brittle_model>::finest_level;
784 using Base::Mechanics<brittle_model>::geom;
788};
789}
790
791#endif
#define TEST(x)
Definition Util.H:22
#define INFO
Definition Util.H:21
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
bool contains(std::string name)
Definition ParmParse.H:173
void select(std::string name, PTRTYPE *&ic_eta, Args &&... args)
Definition ParmParse.H:1030
int queryclass_enumerate(std::string a_name, std::vector< T > &value, int number=1)
Definition ParmParse.H:738
void select_enumerate(std::string a_name, std::vector< PTRTYPE * > &value, Args &&... args)
Definition ParmParse.H:1093
int query_default(std::string name, T &value, T defaultvalue)
Definition ParmParse.H:293
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
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: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
Set::Field< Set::Scalar > psi_mf
Definition Mechanics.H:465
Set::Field< brittle_model > model_mf
Definition Mechanics.H:463
Set::Scalar mult_lap
Definition Fracture.H:746
std::vector< pfczm_crack_type > cracktype
Definition Fracture.H:738
Set::Scalar scaleModulusMax
Definition Fracture.H:744
Set::Field< Set::Scalar > history_var_mf
Definition Fracture.H:736
Set::Field< Set::Scalar > eta_mf
Definition Fracture.H:766
IC::IC< Set::Scalar > * ic
Definition Fracture.H:769
BC::BC< Set::Scalar > * bc_psi
Definition Fracture.H:780
Set::Scalar crack_l2_err
Definition Fracture.H:758
Set::Scalar el_mult
Definition Fracture.H:748
void TimeStepBegin(Set::Scalar, int a_step) override
Definition Fracture.H:234
Set::Field< Set::Scalar > c_old_mf
Definition Fracture.H:734
void TimeStepComplete(Set::Scalar, int) override
Definition Fracture.H:703
virtual void UpdateModel(int a_step, Set::Scalar) override
Definition Fracture.H:186
void Advance(int a_lev, amrex::Real a_time, amrex::Real a_dt) override
Definition Fracture.H:496
Set::Scalar crack_prop_iter
Definition Fracture.H:760
Set::Field< Set::Scalar > energy_pristine_mf
Definition Fracture.H:735
Set::Scalar tol_rel
Definition Fracture.H:754
std::vector< brittle_model > models
Definition Fracture.H:768
Set::Scalar tol_abs
Definition Fracture.H:755
Set::Scalar m_eta_ref_threshold
Definition Fracture.H:767
Fracture(IO::ParmParse &pp)
Definition Fracture.H:81
Set::Scalar max_iter
Definition Fracture.H:756
Set::Scalar driving_force_reference_prev
Definition Fracture.H:751
void Integrate(int amrlev, Set::Scalar time, int step, const amrex::MFIter &mfi, const amrex::Box &a_box) override
Definition Fracture.H:688
static constexpr const char * name
Definition Fracture.H:78
static void Parse(Fracture &value, IO::ParmParse &pp)
Definition Fracture.H:87
struct Integrator::Fracture::@6 material
std::vector< std::string > ic_type
Definition Fracture.H:740
Set::Scalar refinement_threshold
Definition Fracture.H:745
Set::Scalar crack_norm
Definition Fracture.H:759
void TagCellsForRefinement(int lev, amrex::TagBoxArray &a_tags, Set::Scalar a_time, int a_ngrow) override
Definition Fracture.H:624
struct Integrator::Fracture::@5 crack
Set::Scalar mult_Gc
Definition Fracture.H:747
Set::Scalar driving_force_reference
Definition Fracture.H:750
Set::Field< Set::Scalar > driving_force_mf
Definition Fracture.H:737
Set::Scalar driving_force_refinement_threshold
Definition Fracture.H:753
std::vector< IC::IC< Set::Scalar > * > ic
Definition Fracture.H:741
Set::Field< Set::Scalar > c_mf
Definition Fracture.H:733
void Initialize(int lev) override
Definition Fracture.H:152
Set::Scalar driving_force_norm
Definition Fracture.H:752
Set::Scalar beta
Definition Fracture.H:762
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:392
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:393
int finest_level
Definition Set.H:67
Collection of numerical integrator objects.
Definition AllenCahn.H:42
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:546
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:811
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:18
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, 1 > Vector
Definition Base.H:19
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, AMREX_SPACEDIM > Matrix
Definition Base.H:22
AMREX_FORCE_INLINE void RealFillBoundary(amrex::FabArray< amrex::BaseFab< T > > &a_mf, const amrex::Geometry &)
Definition Util.H:335
void Abort(const char *msg)
Definition Util.cpp:225
AMREX_FORCE_INLINE void Assert(std::string file, std::string func, int line, std::string smt, bool pass, Args const &... args)
Definition Util.H:55
void Message(std::string file, std::string func, int line, Args const &... args)
Definition Util.H:126
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:1351