Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
PrismaticLoopGenerator.cpp
Go to the documentation of this file.
1/* This file is part of MoDELib, the Mechanics Of Defects Evolution Library.
2 *
3 *
4 * MoDELib is distributed without any warranty under the
5 * GNU General Public License (GPL) v2 <http://www.gnu.org/licenses/>.
6 */
7
8#ifndef model_PrismaticLoopGenerator_cpp_
9#define model_PrismaticLoopGenerator_cpp_
10
12#include <numbers>
13#include <chrono>
14#include <random>
15#include <cmath>
16#include <list>
17#include <assert.h>
18#include <Eigen/LU>
19#include <Eigen/QR>
20#include <Eigen/Cholesky>
21#include <limits>
22#include <cmath>
23#include <numbers> // std::numbers
24
25//#include <Simplex.h>
26#include <SimplicialMesh.h>
27#include <Polycrystal.h>
29#include <LatticeModule.h>
30//#include <PlaneMeshIntersection.h>
37
39#include <TextFileParser.h>
41#include <MeshBoundarySegment.h>
42//#include <ConfinedDislocationObject.h>
43#include <GlidePlaneModule.h>
44#include <MeshModule.h>
45#include <Plane.h>
48#include <PlanesIntersection.h>
49#include <LatticePlane.h>
50
51namespace model
52{
53
54//PrismaticLoopGenerator::PrismaticLoopGenerator(const std::string& fileName) :
56//{
57//
58//}
59
61{
62
63 std::cout<<magentaBoldColor<<"Generating prismatic loop density"<<defaultColor<<std::endl;
64 if(spec.targetDensity>0.0)
65 {
66 std::normal_distribution<double> radiusDistribution(spec.radiusDistributionMean/mg.ddBase.poly.b_SI,spec.radiusDistributionStd/mg.ddBase.poly.b_SI);
67 std::mt19937 generator;
68
69 std::uniform_int_distribution<> allowedSlipSystemDist(0,spec.allowedSlipSystemIDs.size()-1);
70
71 std::set<int> allowedGrainIDsSet;
72 for(const auto& gID : spec.allowedGrainIDs)
73 {
74 allowedGrainIDsSet.insert(gID);
75 }
76 const double allowAllGrains(allowedGrainIDsSet.size() ? (*allowedGrainIDsSet.begin())<0 : true);
77
78 double density=0.0;
79 while(density<spec.targetDensity)
80 {
81 const std::pair<LatticeVector<3>, int> rp(mg.ddBase.poly.randomLatticePointInMesh());
82 const LatticeVector<3> L0=rp.first;
83 const size_t grainID=rp.second;
84
85 if(allowAllGrains || allowedGrainIDsSet.find(grainID)!=allowedGrainIDsSet.end())
86 {
87 std::uniform_int_distribution<> ssDist(0,mg.ddBase.poly.grain(grainID)->slipSystems().size()-1);
88
89 const int allowedIndex(allowedSlipSystemDist(generator));
90 const int allowedSlipID(spec.allowedSlipSystemIDs[allowedIndex]);
91 const int rSS(allowedSlipID<0 ? ssDist(generator) : allowedSlipID); // a random SlipSystem
92 const double radius(radiusDistribution(generator));
93 try
94 {
95 density+=generateSingle(mg,rSS,L0.cartesian(),radius,50.0)/mg.ddBase.mesh.volume()/std::pow(mg.ddBase.poly.b_SI,2);
96 std::cout<<"prismatic loop density="<<density<<std::endl;
97 }
98 catch(const std::exception& e)
99 {
100 std::cout<<e.what()<<std::endl;
101 }
102 }
103
104 }
105 }
106}
107
109{
110 std::cout<<magentaBoldColor<<"Generating individual prismatic loops"<<defaultColor<<std::endl;
111 if(spec.slipSystemIDs.size())
112 {
113 if(spec.slipSystemIDs.size()!=spec.loopRadii.size())
114 {
115 throw std::runtime_error("spec.slipSystemIDs.size()="+std::to_string(spec.slipSystemIDs.size())+" NOT EQUAL TO spec.loopRadii.size()="+std::to_string(spec.loopRadii.size()));
116 }
117 if(int(spec.slipSystemIDs.size())!=spec.loopCenters.rows())
118 {
119 throw std::runtime_error("spec.slipSystemIDs.size()="+std::to_string(spec.slipSystemIDs.size())+" NOT EQUAL TO spec.loopCenters.rows()="+std::to_string(spec.loopCenters.rows()));
120 }
121 if(spec.slipSystemIDs.size()!=spec.glideSteps.size())
122 {
123 throw std::runtime_error("spec.slipSystemIDs.size()="+std::to_string(spec.slipSystemIDs.size())+" NOT EQUAL TO spec.glideSteps.size()="+std::to_string(spec.glideSteps.size()));
124 }
125
126 for(size_t k=0;k<spec.slipSystemIDs.size();++k)
127 {
128 generateSingle(mg,spec.slipSystemIDs[k],spec.loopCenters.row(k),spec.loopRadii[k]/mg.ddBase.poly.b_SI,spec.glideSteps[k]);
129 }
130 }
131}
132
133double PrismaticLoopGenerator::generateSingle(MicrostructureGenerator& mg,const int& rSS,const VectorDimD& guessCenter,const double& radius,const double& L)
134{
135 std::pair<bool,const Simplex<3,3>*> found(mg.ddBase.mesh.search(guessCenter));
136 if(found.first)
137 {
138 const int grainID(found.second->region->regionID);
139 // Select the host grain from the center, also for polycrystalline meshes.
140 const auto& grain(mg.ddBase.poly.grain(grainID));
141
142 if(rSS>=0 && rSS<int(grain->slipSystems().size()))
143 {
144 // sort slip systems in the zone axis with incrasing angle from a reference
145 const RationalLatticeDirection<3> b(grain->slipSystems().at(rSS)->s);
146 const VectorDimD unitAxis(b.cartesian().normalized());
147 const VectorDimD refNormal(Plane<3>::getL2G(unitAxis).col(0));
148 std::map<double,ReciprocalLatticeDirection<3>> ssMap;
149 for(const auto& ss : grain->slipSystems())
150 {
151 if((ss.second->s-b).squaredNorm()<FLT_EPSILON)
152 {
153 const double cosAngle(ss.second->unitNormal.dot(refNormal));
154 const double sinAngle(ss.second->unitNormal.dot(unitAxis.cross(refNormal)));
155 const double refAngle1(std::atan2(sinAngle,cosAngle));
156 const double refAngle2(std::atan2(-sinAngle,-cosAngle));
157
158 if(std::fabs(refAngle1)<std::fabs(refAngle2))
159 {
160 ssMap.emplace(refAngle1,ss.second->n*(+1));
161 }
162 else
163 {
164 ssMap.emplace(refAngle2,ss.second->n*(-1));
165 }
166 }
167 }
168
169 // Recenter and define sessile plane
170
171 const VectorDimD center(grain->snapToLattice(guessCenter).cartesian());
172 const ReciprocalLatticeDirection<3> axis(grain->reciprocalLatticeDirection(b.cartesian()));
173 const int sessilePlaneHeight(axis.planeIndexOfPoint(center));
174// const LatticePlaneKey sessilePlaneKey(axis,sessilePlaneHeight,grain->grainID);
175 const LatticePlaneKey sessilePlaneKey(sessilePlaneHeight,axis);
176 const auto sessilePlane(mg.ddBase.dislocationDynamics().periodicGlidePlaneFactory.getFromKey(sessilePlaneKey));
177
178 // Define prismatic planes.
179 std::vector<LatticePlane> planes;
180
181 for(const auto& pair : ssMap)
182 {// first half of the prism
183 const int planeHeight(pair.second.closestPlaneIndexOfPoint(center+pair.second.cartesian().normalized()*radius));
184 planes.emplace_back(planeHeight,pair.second);
185 }
186 for(const auto& pair : ssMap)
187 {// second half of the prism
188 const int planeHeight(pair.second.closestPlaneIndexOfPoint(center-pair.second.cartesian().normalized()*radius));
189 planes.emplace_back(planeHeight,pair.second);
190 }
191
192 // Intersect two consecutive prismatic planes and sessile plane to find polygon points
193 std::vector<VectorDimD> sessileLoopNodePos;
194 for(size_t k1=0;k1<planes.size();++k1)
195 {
196 size_t k2(k1==planes.size()-1? 0 : k1+1);
197 const auto& plane1(planes[k1]);
198 const auto& plane2(planes[k2]);
199
200 Eigen::Matrix<double,3,3> N;
201 N.col(0)=sessilePlane->referencePlane->unitNormal;
202 N.col(1)=plane1.n.cartesian().normalized();
203 N.col(2)=plane2.n.cartesian().normalized();
204
205 Eigen::Matrix<double,3,3> P;
206 P.col(0)=sessilePlane->referencePlane->P;
207 P.col(1)=plane1.planeOrigin();
208 P.col(2)=plane2.planeOrigin();
209
210 PlanesIntersection<3> pi(N,P,FLT_EPSILON);
211 const std::pair<bool,VectorDimD> intPair(pi.snap(VectorDimD::Zero()));
212 if(intPair.first)
213 {
214 sessileLoopNodePos.push_back(intPair.second);
215 }
216 else
217 {
218 throw std::runtime_error("Cannot determine prismatic polygon.");
219 }
220
221 }
222
223 // Map points along rays inside the convex prism. Crossing a physical
224 // face rejects the prism; same-grain periodic faces translate the ray.
225 const auto mappedShift=[&](const VectorDimD& target)
226 {
227 VectorDimD shift=VectorDimD::Zero();
228 const VectorDimD direction=target-center;
229 double parameter=0.0;
230 for(size_t crossing=0;crossing<10000;++crossing)
231 {
232 const Polyhedron<3,3-1>* exitFace=nullptr;
233 double exitParameter=1.0;
234 for(const auto& entry : grain->region.faces())
235 {
236 const auto* face=entry.second.get();
237 const VectorDimD normal=face->outNormal(grainID);
238 const double speed=normal.dot(direction);
239 if(speed>FLT_EPSILON)
240 {
241 const double candidate=normal.dot(face->center()-center-shift)/speed;
242 if(candidate>=parameter-1.e-9 && candidate<exitParameter-1.e-9)
243 {
244 exitParameter=candidate;
245 exitFace=face;
246 }
247 }
248 }
249 if(!exitFace)
250 {
251 return shift;
252 }
253 const auto* partner=exitFace->periodicFacePair.second;
254 if(!partner || exitFace->regionIDs!=partner->regionIDs)
255 {
256 throw std::runtime_error("Prismatic loop crosses a physical grain boundary; entire prism rejected.");
257 }
258 shift+=partner->center()-exitFace->center();
259 parameter=exitParameter;
260 }
261 throw std::runtime_error("Prismatic loop exceeded periodic crossing limit; entire prism rejected.");
262 };
263 struct PreparedNode
264 {
265 VectorDimD position;
266 VectorDimD shift;
267 std::pair<short int,short int> edges;
268 };
269 struct PreparedLoop
270 {
271 std::shared_ptr<PeriodicGlidePlane<3>> plane;
272 DislocationLoopIO<3>::DislocationLoopType type;
273 std::vector<PreparedNode> nodes;
274 };
275 // Validate every face of the closed prism before modifying the IO.
276 // The patch walk crosses only same-grain periodic faces. Any area
277 // removed by its physical-boundary clipping rejects the whole prism.
278 const auto prepare=[&](const std::vector<VectorDimD>& polygon,
279 const std::shared_ptr<PeriodicGlidePlane<3>>& plane,
281 {
282 std::vector<PolyPoint> anchors(polygon.size());
283 std::vector<std::pair<VectorDimD,const PolyPoint* const>> points;
284 for(size_t k=0;k<polygon.size();++k)
285 {
286 points.emplace_back(polygon[k],&anchors[k]);
287 }
288 const auto intersections=plane->polygonPatchIntersection(points,true);
289 PreparedLoop result{plane,type,{}};
290 for(const auto& point : intersections)
291 {
292 result.nodes.push_back({plane->referencePlane->globalPosition(std::get<0>(point)),
293 std::get<1>(point),std::get<2>(point)});
294 }
295 VectorDimD originalArea=VectorDimD::Zero();
296 VectorDimD retainedArea=VectorDimD::Zero();
297 for(size_t k=0;k<polygon.size();++k)
298 {
299 originalArea+=0.5*(polygon[k]-polygon.front()).cross(polygon[(k+1)%polygon.size()]-polygon.front());
300 }
301 for(size_t k=0;k<result.nodes.size();++k)
302 {
303 retainedArea+=0.5*(result.nodes[k].position-polygon.front()).cross(
304 result.nodes[(k+1)%result.nodes.size()].position-polygon.front());
305 }
306 if(result.nodes.size()<3 || originalArea.norm()<=FLT_EPSILON ||
307 (retainedArea-originalArea).norm()>1.e-8*originalArea.norm())
308 {
309 throw std::runtime_error("Prismatic loop crosses a physical grain boundary; entire prism rejected.");
310 }
311 return result;
312 };
313 std::vector<PreparedLoop> prepared;
314 prepared.push_back(prepare(sessileLoopNodePos,sessilePlane,DislocationLoopIO<3>::SESSILELOOP));
315 const VectorDimD step(L*b.cartesian());
316 std::vector<VectorDimD> top;
317 for(const auto& point : sessileLoopNodePos)
318 {
319 mappedShift(point);
320 mappedShift(point+step);
321 top.push_back(point+step);
322 }
323 const VectorDimD topShift=mappedShift(center+step);
324 for(auto& point : top)
325 {
326 point+=topShift;
327 }
328 const auto topPlane=mg.ddBase.dislocationDynamics().periodicGlidePlaneFactory.getFromKey(
329 LatticePlaneKey(axis.planeIndexOfPoint(center+step+topShift),axis));
330 // The cap is validation-only: inserting it would cancel the physical loop.
331 prepare(top,topPlane,DislocationLoopIO<3>::SESSILELOOP);
332 double loopLength=0.0;
333 for(size_t k1=0;k1<planes.size();++k1)
334 {
335 const size_t k2=(k1+1)%planes.size();
336 loopLength+=(sessileLoopNodePos[k2]-sessileLoopNodePos[k1]).norm();
337 std::vector<VectorDimD> side{sessileLoopNodePos[k2],sessileLoopNodePos[k1],
338 sessileLoopNodePos[k1]+step,sessileLoopNodePos[k2]+step};
339 const VectorDimD sideShift=mappedShift(0.5*(sessileLoopNodePos[k1]+sessileLoopNodePos[k2]+step));
340 for(auto& point : side)
341 {
342 point+=sideShift;
343 }
344 const LatticePlaneKey key(planes[k2].n.planeIndexOfPoint(side.front()),planes[k2].n);
345 prepared.push_back(prepare(side,mg.ddBase.dislocationDynamics().periodicGlidePlaneFactory.getFromKey(key),
347 }
348 // Commit all prepared loops together; also roll back unexpected
349 // insertion failures, including the position-to-network-node index.
350 const size_t oldLoops=mg.config().loops().size();
351 const size_t oldNodes=mg.config().nodes().size();
352 const size_t oldLoopNodes=mg.config().loopNodes().size();
353 const size_t oldLinks=mg.config().loopLinks().size();
354 auto oldNodeMap=mg.uniqueNetworkNodeMap;
355 try
356 {
357 for(const auto& loop : prepared)
358 {
359 const size_t id=mg.insertLoop(b.cartesian(),loop.plane->referencePlane->unitNormal,
360 loop.plane->referencePlane->P,grainID,loop.type);
361 std::vector<size_t> nodeIDs;
362 for(const auto& node : loop.nodes)
363 {
364 const VectorDimD position=node.position+node.shift;
365 auto foundNode=mg.uniqueNetworkNodeMap.find(position);
366 if(foundNode==mg.uniqueNetworkNodeMap.end())
367 {
368 const size_t networkID=mg.insertNetworkNode(position);
369 foundNode=mg.uniqueNetworkNodeMap.emplace(position,networkID).first;
370 }
371 nodeIDs.push_back(mg.insertLoopNode(id,node.position,foundNode->second,node.shift,node.edges));
372 }
373 mg.insertLoopLinks(id,nodeIDs);
374 }
375 }
376 catch(...)
377 {
378 mg.config().loopLinks().resize(oldLinks);
379 mg.config().loopNodes().resize(oldLoopNodes);
380 mg.config().nodes().resize(oldNodes);
381 mg.config().loops().resize(oldLoops);
382 mg.uniqueNetworkNodeMap.swap(oldNodeMap);
383 throw;
384 }
385 return loopLength;
386 }
387 else
388 {
389 if(rSS<0)
390 {
391 std::cout<<"Skipping slip system "<<rSS<<std::endl;
392 }
393 else
394 {
395 throw std::runtime_error("slipSystem "+std::to_string(rSS)+" not found, skipping.");
396 }
397 }
398 }
399 else
400 {
401 std::cout<<"Center outside mesh, skipping prismatic loop"<<std::endl;
402 }
403 return 0.0;
404}
405
406}
407#endif
const std::vector< DislocationLoopLinkIO< dim > > & loopLinks() const
const std::vector< DislocationLoopIO< dim > > & loops() const
const std::vector< DislocationNodeIO< dim > > & nodes() const
const std::vector< DislocationLoopNodeIO< dim > > & loopNodes() const
size_t insertLoopNode(const size_t &loopID, const VectorDimD &loopNodePos, const size_t &networkNodeID, const VectorDimD &loopNodeShift, const std::pair< short int, short int > &periodicEdgeIDs)
std::vector< size_t > insertLoopLinks(const size_t &loopID, const std::vector< size_t > &loopNodeIDs)
std::map< VectorDimD, size_t, CompareVectorsByComponent< double, dim, float > > uniqueNetworkNodeMap
DefectiveCrystalBase< 3 > & ddBase
size_t insertLoop(const VectorDimD &b, const VectorDimD &unitNormal, const VectorDimD &P0, const size_t &grainID, const DislocationLoopType &loopType)
PrismaticLoopGenerator(const PrismaticLoopDensitySpecification &spec, MicrostructureGenerator &mg)
std::pair< bool, const Simplex< _dim, _dim > * > search(const Eigen::Matrix< double, dim, 1 > &P) const
const double & volume() const
b
Definition int_R.m:16
polygon(ax, points, fill='#edf4f8', z=2)
static std::string defaultColor
static std::string magentaBoldColor
const Polycrystal< dim > & poly
const SimplicialMesh< dim > & mesh
DislocationDynamicsBase< dim > & dislocationDynamics() const
static MatrixDim getL2G(const VectorDim &z)
Definition Plane.cpp:110
void parameter(std::string &text, const std::string &key, const std::string &value)
m edges
newsubseg ss[0]
int axis
int k
newbad key
int e
result
struct otri top