5#ifndef model_EllipsoidalInclusion_cpp_
6#define model_EllipsoidalInclusion_cpp_
18 Eigen::Matrix3d checkedStrain(
const Eigen::Matrix3d &
e,
const double nu,
const double mu)
22 throw std::invalid_argument(
"EllipsoidalInclusion: invalid strain or elastic constants");
24 const double scale =
e.cwiseAbs().maxCoeff();
25 if(scale > 0.0 && (
e / scale -
e.transpose() / scale).norm() > 1.0e-12)
27 throw std::invalid_argument(
"EllipsoidalInclusion: eigenstrain must be symmetric");
29 return (0.5 * (
e +
e.transpose())).eval();
37 const double &_nu,
const double &_mu,
38 const double &_mobilityReduction,
const int &_phaseID,
40 const double quadratureTolerance,
41 const unsigned int quadratureMaxDepth)
42 :
EshelbyInclusionBase<dim>(checkedStrain(_eT, _nu, _mu), _nu, _mu, _mobilityReduction, _phaseID, sph),
C(_C),
43 axes(_axes), lengthScale(0.0), rotation(_axes), axisProduct(0.0), tolerance(quadratureTolerance),
44 maxDepth(quadratureMaxDepth)
46 if(!
C.allFinite() || !
axes.allFinite() || !std::isfinite(tolerance) || tolerance < 1.0e-14 ||
47 tolerance > 1.0e-3 || maxDepth == 0 || maxDepth > 50)
49 throw std::invalid_argument(
"EllipsoidalInclusion: invalid geometry or quadrature settings");
52 for(
int i = 0;
i < 3; ++
i)
54 lengths(i) = rotation.col(i).stableNorm();
55 if(!(lengths(i) > 0.0) || !std::isfinite(lengths(i)))
57 throw std::invalid_argument(
"EllipsoidalInclusion: semiaxes must have positive finite lengths");
59 rotation.col(
i) /= lengths(
i);
61 if((rotation.transpose() * rotation - MatrixDim::Identity()).norm() > 1.0e-12)
63 throw std::invalid_argument(
"EllipsoidalInclusion: axes columns must be mutually orthogonal");
67 (rotation.col(1) - rotation.col(0) * rotation.col(0).dot(rotation.col(1))).normalized().eval();
68 VectorDim third = rotation.col(0).cross(rotation.col(1));
69 if(third.dot(rotation.col(2)) < 0.0)
73 rotation.col(2) = third;
74 lengthScale = lengths.maxCoeff();
75 lengths /= lengthScale;
77 if(lengths.minCoeff() < 1.0e-6)
79 throw std::invalid_argument(
"EllipsoidalInclusion: semiaxis ratio exceeds 1e6");
81 axisSquared = lengths.array().square().matrix();
82 axisProduct = lengths.prod();
83 interiorIntegrals = integrate(0.0);
84 interiorStressCache = std::make_shared<const InteriorStressCache>(
85 InteriorStressCache{this->eT, evaluateStress(VectorDim::Zero(),
true, this->eT)});
89 typename EllipsoidalInclusion<dim>::VectorDim EllipsoidalInclusion<dim>::localPosition(
const VectorDim &
x)
const
93 throw std::invalid_argument(
"EllipsoidalInclusion: nonfinite observation point");
95 const VectorDim r = rotation.transpose() * ((
x -
C) / lengthScale);
96 if(!
r.allFinite() || !std::isfinite(
r.squaredNorm()))
98 throw std::overflow_error(
"EllipsoidalInclusion: observation point exceeds numerical range");
105 const VectorDim r = localPosition(
x);
106 return (r.array().square() / axisSquared.array()).sum() < 1.0;
109 template <
int dim>
double EllipsoidalInclusion<dim>::ellipsoidalCoordinate(
const VectorDim &r)
const
113 const VectorDim rSquared = r.array().square().matrix();
114 const double radiusSquared = rSquared.sum();
115 const double smallestAxisSquared = axisSquared.minCoeff();
116 const double relativeTolerance = 8.0 * std::numeric_limits<double>::epsilon();
117 double lo = std::max(0.0, radiusSquared - axisSquared.maxCoeff());
118 double hi = radiusSquared;
120 double previousStep = hi - lo;
121 for(
unsigned int k = 0;
k < 256; ++
k)
123 const double positionTolerance = relativeTolerance * (smallestAxisSquared + ell);
124 if(hi - lo <= positionTolerance)
126 return lo + 0.5 * (hi - lo);
130 const double f = terms.sum() - 1.0;
131 const double minusDerivative = (terms.array() /
denominator.array()).sum();
132 const double correction =
f / minusDerivative;
134 if(
f == 0.0 || (std::abs(
f) <= relativeTolerance && std::abs(correction) <= positionTolerance))
146 const double newton = ell + correction;
147 double next = newton;
148 if(!std::isfinite(newton) || newton <= lo || newton >= hi || std::abs(correction) > 0.5 * previousStep)
151 next = lo + 0.5 * (hi - lo);
153 previousStep = std::abs(next - ell);
156 throw std::runtime_error(
"EllipsoidalInclusion: ellipsoidal coordinate did not converge");
160 typename EllipsoidalInclusion<dim>::Integrals EllipsoidalInclusion<dim>::integrate(
const double ell)
const
165 const double h = 1.0 + ell;
166 const auto integrand = [&](
double t) -> Integrals
168 const double t2 =
t *
t, oneMinusT2 = (1.0 -
t) * (1.0 +
t);
170 const VectorDim d = (oneMinusT2 + t2 * (axisSquared.array() + ell) /
h).matrix();
171 const double w = 2.0 * t2 / std::sqrt(d.prod());
173 for(
int i = 0;
i < 3; ++
i)
176 for(
int j = 0;
j < 3; ++
j)
178 f(3 + 3 *
i +
j) =
w * (oneMinusT2 + t2 * ell /
h) / (d(
i) * d(
j));
184 const double z[8] = {0.9914553711208126, 0.9491079123427585, 0.8648644233597691, 0.7415311855993945,
185 0.5860872354676911, 0.4058451513773972, 0.2077849550078985, 0.0};
186 const double wk[8] = {0.02293532201052922, 0.06309209262997855, 0.1047900103222502, 0.1406532597155259,
187 0.1690047266392679, 0.1903505780647854, 0.2044329400752989, 0.2094821410847278};
188 const double wg[4] = {0.1294849661688697, 0.2797053914892767, 0.3818300505051189, 0.4179591836734694};
189 unsigned int evaluations = 0;
190 std::function<Integrals(
double,
double,
unsigned int)> adaptive;
191 adaptive = [&](
double left,
double right,
unsigned int depth) -> Integrals
193 if(++evaluations > 200000)
195 throw std::runtime_error(
"EllipsoidalInclusion: quadrature work limit exceeded");
198 const Integrals center = integrand(mid);
199 Integrals fine = wk[7] * center, coarse = wg[3] * center;
200 for(
int k = 0;
k < 7; ++
k)
202 const Integrals pair = integrand(mid - half *
z[
k]) + integrand(mid + half *
z[
k]);
203 fine += wk[
k] * pair;
206 coarse += wg[(
k - 1) / 2] * pair;
211 if(!fine.allFinite())
213 throw std::runtime_error(
"EllipsoidalInclusion: nonfinite quadrature");
215 if(((fine - coarse).array().abs() <= tolerance * ((
right -
left) + fine.array().abs())).all())
219 if(depth == maxDepth)
221 throw std::runtime_error(
"EllipsoidalInclusion: quadrature failed to converge");
223 const Integrals
first = adaptive(
left, mid, depth + 1);
224 return (
first + adaptive(mid,
right, depth + 1)).eval();
226 return (adaptive(0.0, 1.0, 0) /
h / std::sqrt(
h)).eval();
232 const VectorDim r = localPosition(
x);
233 const bool inside = (r.array().square() / axisSquared.array()).sum() <= 1.0;
236 const auto cache = std::atomic_load(&interiorStressCache);
237 if((cache->eigenstrain.array() == this->eT.array()).all())
239 return cache->stress;
242 const MatrixDim eigenstrain = this->
eT;
243 const MatrixDim sigma = evaluateStress(r,
true, eigenstrain);
244 const auto updated = std::make_shared<const InteriorStressCache>(InteriorStressCache{eigenstrain, sigma});
245 std::atomic_store(&interiorStressCache, updated);
248 return evaluateStress(r,
false, this->
eT);
252 typename EllipsoidalInclusion<dim>::MatrixDim
253 EllipsoidalInclusion<dim>::evaluateStress(
const VectorDim &r,
const bool inside,
const MatrixDim &eigenstrain)
const
255 const MatrixDim e = checkedStrain(eigenstrain, this->nu, this->mu);
258 rotation.transpose() *
259 (2.0 * this->mu * (
e + this->nu / (1.0 - 2.0 * this->nu) *
e.trace() * MatrixDim::Identity())) * rotation;
262 throw std::overflow_error(
"EllipsoidalInclusion: eigenstress overflow");
264 if(
p.cwiseAbs().maxCoeff() == 0.0)
266 return MatrixDim::Zero();
268 const double ell = inside ? 0.0 : ellipsoidalCoordinate(r);
269 const Integrals values = inside ? interiorIntegrals : integrate(ell);
270 const double pi = std::acos(-1.0), factor = pi * axisProduct;
271 MatrixDim H = MatrixDim::Zero(),
T = MatrixDim::Zero();
272 for(
int i = 0;
i < 3; ++
i)
274 H(
i,
i) = -2.0 * factor * values(
i);
275 for(
int j = 0;
j < 3; ++
j)
277 T(
i,
j) = -4.0 * factor *
p(
i,
j) * values(3 + 3 *
i +
j);
279 for(
int j = 0;
j < 3; ++
j)
281 T(
i,
i) -= 2.0 * factor *
p(
j,
j) * values(3 + 3 *
i +
j);
286 const VectorDim b = (axisSquared.array() + ell).inverse().matrix();
289 const double d =
q.squaredNorm();
290 const double invDelta = std::sqrt(
b.prod());
294 const MatrixDim lH = (2.0 *
B - 2.0 * (Bq *
l.transpose() + l * Bq.transpose()) + 2.0 *
q.dot(Bq) * ll) / d;
295 H += 4.0 * factor * invDelta / d * (
q *
q.transpose());
298 const double trPB = (
p.diagonal().array() *
b.array()).sum();
299 const double v = 8.0 *
q.dot(
p *
q);
300 const double w = ell * invDelta;
302 const double Fs = invDelta * (1.0 - 0.5 * ell *
b.sum()) *
v +
w * (-16.0 * Bq.dot(
p *
q) - 4.0 * d * trPB);
303 T += 0.25 * factor * (Fx *
l.transpose() + l * Fx.transpose() + Fs * ll +
w *
v * lH);
307 2.0 * this->nu * (
p.cwiseProduct(H)).sum() * MatrixDim::Identity()) /
308 (8.0 * pi * (1.0 - this->nu));
313 sigma = (rotation *
sigma * rotation.transpose()).eval();
314 if(!
sigma.allFinite())
316 throw std::overflow_error(
"EllipsoidalInclusion: stress overflow");
318 return (0.5 * sigma + 0.5 *
sigma.transpose()).eval();
321 template class EllipsoidalInclusion<3>;
MatrixDim stress(const VectorDim &x) const override
EllipsoidalInclusion(const VectorDim &_C, const MatrixDim &_axes, const MatrixDim &_eT, const double &_nu, const double &_mu, const double &_mobilityReduction, const int &_phaseID, const std::shared_ptr< SecondPhase< dim > > &sph, const double quadratureTolerance=1.0e-3, const unsigned int quadratureMaxDepth=30)
bool contains(const VectorDim &x) const override
EshelbyInclusionBase(const MatrixDim &_eT, const double &_nu, const double &_mu, const double &_mobilityReduction, const int &_phaseID, const std::shared_ptr< SecondPhase< dim > > &sph)
initial point of the source segment B
the magnitude of the R vector q
final point of the source segment t
function int_R syms a b c u real f
Eigen::Matrix< double, 3, 1 > VectorDim
Eigen::Matrix< double, 3, 3 > MatrixDim