22 check(
argc == 2,
"Expected repository root");
23 const std::filesystem::path root(
argv[1]);
24 const auto dir = std::filesystem::temp_directory_path() /
25 (
"model-ellipsoid-generator-" +
26 std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
27 for(
const auto sub : {
"inputFiles",
"config",
"aux",
"F"})
29 std::filesystem::create_directories(dir / sub);
31 const auto input = dir /
"inputFiles";
32 std::filesystem::copy_file(root /
"Library/Materials/W.txt", input /
"W.txt");
33 std::filesystem::copy_file(root /
"Library/Meshes/unitCube24.msh", input /
"mesh.msh");
35 std::ofstream
f(input /
"DefectiveCrystal.txt");
36 f <<
"physics=InclusionMicrostructure;\nuseFEM=0;\nNsteps=1;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary="
37 "0;\nstartAtTimeStep=0;\n";
40 std::ofstream
f(input /
"polycrystal.txt");
41 f <<
"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\n"
42 <<
"C2G1=0.6 -0.8 0 0.8 0.6 0 0 0 1;\nF=1000 0 0 0 1000 0 0 0 1000;\nX0=0 0 0;\nperiodicFaceIDs=;\n";
47 spec.center = base.mesh.xCenter();
48 const M axes =
V(30, 20, 10).asDiagonal();
50 C2G << .6, -.8, 0, .8, .6, 0, 0, 0, 1;
51 const M globalAxes = C2G * axes;
52 spec.crystalAxes_SI = axes * base.poly.b_SI;
53 spec.crystalEigenDistortion << .01, .002, 0, .002, .01, 0, 0, 0, .01;
54 const M crystalStrain = Eigen::Map<const M>(spec.crystalEigenDistortion.data()).transpose();
55 const M globalStrain = C2G * crystalStrain * C2G.transpose();
60 "Wrong crystal-to-global rotation or unit conversion");
63 spec.allowOverlap =
true;
66 spec.center = base.mesh.xMax() + V::Ones() * 1000;
69 spec.allowOutside =
true;
72 spec.center =
V(490, 0, 0);
73 spec.allowOutside =
false;
76 spec.allowOutside =
true;
79 spec.crystalAxes_SI.setZero();
80 bool rejected =
false;
85 catch(
const std::invalid_argument &)
95 const double volumeSI = base.mesh.volume() * std::pow(base.poly.b_SI, 3);
106 const V planeNormal = C2G.col(0);
107 std::mt19937 expectedRandom;
108 std::normal_distribution<double> expectedNormal(0.0, 1.0);
109 bool independentScales =
false;
111 bool variedSizes =
false;
114 const double planeIndex = inclusion.C.dot(planeNormal) / 100;
115 check(std::abs(planeIndex - std::round(planeIndex)) < 1.e-10,
"Center missed crystal-frame plane");
116 const M localAxes = C2G.transpose() * inclusion.axes;
117 const V lengths = localAxes.colwise().norm();
118 check((localAxes * lengths.cwiseInverse().asDiagonal()).isApprox(M::Identity(), 1.e-12),
119 "Density generation rotated crystal semiaxes incorrectly");
120 check(inclusion.eT.isApprox(globalStrain),
"Density tensor rotation failed");
121 for(
int k = 0;
k < 3; ++
k)
125 const double variance = std::log1p(cv * cv);
126 const double expectedLength =
127 std::exp(std::log(mean) - variance / 2 + std::sqrt(variance) * expectedNormal(expectedRandom));
128 check(std::abs(lengths(
k) * base.poly.b_SI / expectedLength - 1) < 1.e-12,
129 "Arithmetic SI mean/std were not converted to lognormal parameters");
131 const V factors = lengths.cwiseQuotient(
V(30, 20, 10));
132 independentScales = independentScales || std::abs(factors(0) - factors(1)) > 1.e-6;
133 variedSizes = variedSizes || !lengths.isApprox(firstLengths);
135 check(independentScales && variedSizes,
"Semiaxes were not perturbed independently");
143 "Zero-spread sizes incorrect");
145 bool invalidDensity =
false;
150 catch(
const std::invalid_argument &)
152 invalidDensity =
true;
155 "Invalid density distribution changed configuration");
160 bool exhausted =
false;
165 catch(
const std::runtime_error &)
170 "Impossible density placement did not stop");
173 std::ofstream
f(input /
"ellipsoidDensity.txt");
174 f << std::setprecision(17) <<
"type=EllipsoidalInclusions;\nstyle=density;\ntargetDensity=" << .5 / volumeSI
176 <<
"crystalAxes=" << M::Identity() <<
";\n"
177 <<
"axesLognormalDistributionMean_SI=" <<
V(30, 20, 10).transpose() * base.poly.b_SI
178 <<
";\naxesLognormalDistributionStd_SI=0 0 0;\n"
179 <<
"crystalEigenDistortion=.01 .002 0 .002 .01 0 0 0 .01;\n"
180 <<
"patternVector_SI=0 0 0;\nallowOverlap=1;\nallowOutside=1;\n"
181 <<
"velocityReductionFactor=.75;\nphaseID=0;\nmaxAttempts=100;\n";
182 std::ofstream list(input /
"initialMicrostructure.txt");
183 list <<
"microstructureFile=ellipsoidDensity.txt;\n";
189 "Density file dispatch failed");
191 (root /
"Library/Microstructures/ellipsoidalInclusionsDensity.txt").
string());
193 "Density library template parsing failed");
197 spherical.
centers = V::Zero().transpose();
198 spherical.
radii_SI = {5 * base.poly.b_SI};
207 "Individual sphere tensor rotation failed");
209 density.
targetDensity = 0.5 / (base.mesh.volume() * std::pow(base.poly.b_SI, 3));
220 "Density sphere tensor rotation failed");
222 polyhedral.
mshFile = (input /
"mesh.msh").
string();
223 polyhedral.
X0 = V::Zero().transpose();
224 polyhedral.
F = M::Identity() * 10;
231 "Polyhedron tensor rotation failed");
233 check(
sphere == 3,
"Sphere ID collided with ellipsoid");
236 std::map<size_t, V> nodes{{0, V::Zero()}, {1, V::UnitX()}, {2, V::UnitY()}};
237 std::map<size_t, std::vector<size_t>> faces{{0, {0, 1, 2}}};
241 std::ofstream
f(input /
"ellipsoid.txt");
242 f << std::setprecision(17)
243 <<
"type=EllipsoidalInclusions;\nstyle=individual;\ncenter=" << base.mesh.xCenter().transpose() <<
";\n"
244 <<
"crystalAxes_SI=" << axes * base.poly.b_SI <<
";\n"
245 <<
"crystalEigenDistortion=.01 .002 0 .002 .01 0 0 0 "
246 ".01;\nvelocityReductionFactor=.75;\nphaseID=0;\nallowOverlap=0;\nallowOutside=0;\n";
247 std::ofstream list(input /
"initialMicrostructure.txt");
248 list <<
"microstructureFile=ellipsoid.txt;\n";
261 auto inclusions = std::make_shared<model::InclusionMicrostructure<3>>(simulation);
263 std::ofstream unused;
265 restored.
sphericalInclusions().emplace_back(7, base.mesh.xCenter() +
V(100, 0, 0), 5.0, M::Identity() * .002,
268 check(inclusions->eshelbyInclusions().size() == 2,
"Simulation failed to load mixed inclusions");
270 const auto loaded = std::dynamic_pointer_cast<model::EllipsoidalInclusion<3>>(
271 inclusions->eshelbyInclusions().at(record.inclusionID));
272 check(loaded && loaded->sID == record.inclusionID && loaded->phaseID == record.phaseID &&
273 loaded->mobilityReduction == record.mobilityReduction && loaded->secondPhase,
274 "Loaded ellipsoid metadata incorrect");
276 record.mobilityReduction, record.phaseID, loaded->secondPhase);
279 base.poly.nu, base.poly.mu, 1.0, 0, loaded->secondPhase);
280 for(
const V &point : {record.C,
V(record.C +
V(70, 45, 20))})
282 M expected = M::Zero();
283 for(
const auto &shift : base.periodicShifts)
285 expected += reference.
stress(point + shift) + sphereReference.
stress(point + shift);
287 check(expected.norm() > 0 && simulation.
stress(point,
nullptr,
nullptr,
nullptr).isApprox(expected, 1.e-12),
288 "Ellipsoid stress missing from simulation sum");
292 simulation.
output(saved, aux, unused, unused);
294 "Simulation output lost an inclusion");
298 "Simulation output changed ellipsoid data");
302 const V probe = record.C +
V(70, 45, 20);
303 const M before = simulation.
stress(probe,
nullptr,
nullptr,
nullptr);
305 check(simulation.
stress(probe,
nullptr,
nullptr,
nullptr).isApprox(before, 1.e-12),
306 "Restart changed the stress field");
307 for(
const int phaseID : {-1, 999})
310 bool invalidPhase =
false;
315 catch(
const std::runtime_error &)
319 check(invalidPhase,
"Invalid ellipsoidal phase accepted");
323 bool duplicate =
false;
328 catch(
const std::runtime_error &)
332 check(duplicate,
"Duplicate inclusion IDs accepted");
335 check(inclusions->eshelbyInclusions().empty() && simulation.
stress(probe,
nullptr,
nullptr,
nullptr).isZero(),
336 "Empty reload retained stress");
337 std::filesystem::remove_all(dir);
339 <<
"PASS generator insertion, IDs, flags, validation, file dispatch, simulation stress and restart IO\n";
342 catch(
const std::exception &error)
344 std::cerr << error.what() <<
'\n';