8#ifndef model_PrismaticLoopGenerator_cpp_
9#define model_PrismaticLoopGenerator_cpp_
20#include <Eigen/Cholesky>
29#include <LatticeModule.h>
49#include <LatticePlane.h>
64 if(spec.targetDensity>0.0)
66 std::normal_distribution<double> radiusDistribution(spec.radiusDistributionMean/mg.
ddBase.
poly.b_SI,spec.radiusDistributionStd/mg.
ddBase.
poly.b_SI);
67 std::mt19937 generator;
69 std::uniform_int_distribution<> allowedSlipSystemDist(0,spec.allowedSlipSystemIDs.size()-1);
71 std::set<int> allowedGrainIDsSet;
72 for(
const auto& gID : spec.allowedGrainIDs)
74 allowedGrainIDsSet.insert(gID);
76 const double allowAllGrains(allowedGrainIDsSet.size() ? (*allowedGrainIDsSet.begin())<0 :
true);
79 while(density<spec.targetDensity)
81 const std::pair<LatticeVector<3>,
int> rp(mg.
ddBase.
poly.randomLatticePointInMesh());
83 const size_t grainID=rp.second;
85 if(allowAllGrains || allowedGrainIDsSet.find(grainID)!=allowedGrainIDsSet.end())
87 std::uniform_int_distribution<> ssDist(0,mg.
ddBase.
poly.grain(grainID)->slipSystems().size()-1);
89 const int allowedIndex(allowedSlipSystemDist(generator));
90 const int allowedSlipID(spec.allowedSlipSystemIDs[allowedIndex]);
91 const int rSS(allowedSlipID<0 ? ssDist(generator) : allowedSlipID);
92 const double radius(radiusDistribution(generator));
96 std::cout<<
"prismatic loop density="<<density<<std::endl;
98 catch(
const std::exception&
e)
100 std::cout<<
e.what()<<std::endl;
111 if(spec.slipSystemIDs.size())
113 if(spec.slipSystemIDs.size()!=spec.loopRadii.size())
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()));
117 if(
int(spec.slipSystemIDs.size())!=spec.loopCenters.rows())
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()));
121 if(spec.slipSystemIDs.size()!=spec.glideSteps.size())
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()));
126 for(
size_t k=0;
k<spec.slipSystemIDs.size();++
k)
128 generateSingle(mg,spec.slipSystemIDs[
k],spec.loopCenters.row(
k),spec.loopRadii[
k]/mg.
ddBase.
poly.b_SI,spec.glideSteps[
k]);
135 std::pair<bool,const Simplex<3,3>*> found(mg.
ddBase.
mesh.
search(guessCenter));
138 const int grainID(found.second->region->regionID);
140 const auto& grain(mg.
ddBase.
poly.grain(grainID));
142 if(rSS>=0 && rSS<
int(grain->slipSystems().size()))
146 const VectorDimD unitAxis(
b.cartesian().normalized());
148 std::map<double,ReciprocalLatticeDirection<3>> ssMap;
149 for(
const auto&
ss : grain->slipSystems())
151 if((
ss.second->s-
b).squaredNorm()<FLT_EPSILON)
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));
158 if(std::fabs(refAngle1)<std::fabs(refAngle2))
160 ssMap.emplace(refAngle1,
ss.second->n*(+1));
164 ssMap.emplace(refAngle2,
ss.second->n*(-1));
171 const VectorDimD center(grain->snapToLattice(guessCenter).cartesian());
172 const ReciprocalLatticeDirection<3>
axis(grain->reciprocalLatticeDirection(
b.cartesian()));
173 const int sessilePlaneHeight(
axis.planeIndexOfPoint(center));
175 const LatticePlaneKey sessilePlaneKey(sessilePlaneHeight,
axis);
179 std::vector<LatticePlane>
planes;
181 for(
const auto& pair : ssMap)
183 const int planeHeight(pair.second.closestPlaneIndexOfPoint(center+pair.second.cartesian().normalized()*radius));
184 planes.emplace_back(planeHeight,pair.second);
186 for(
const auto& pair : ssMap)
188 const int planeHeight(pair.second.closestPlaneIndexOfPoint(center-pair.second.cartesian().normalized()*radius));
189 planes.emplace_back(planeHeight,pair.second);
193 std::vector<VectorDimD> sessileLoopNodePos;
194 for(
size_t k1=0;k1<
planes.size();++k1)
196 size_t k2(k1==
planes.size()-1? 0 : k1+1);
197 const auto& plane1(planes[k1]);
198 const auto& plane2(planes[k2]);
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();
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();
210 PlanesIntersection<3> pi(
N,P,FLT_EPSILON);
211 const std::pair<bool,VectorDimD> intPair(pi.snap(VectorDimD::Zero()));
214 sessileLoopNodePos.push_back(intPair.second);
218 throw std::runtime_error(
"Cannot determine prismatic polygon.");
225 const auto mappedShift=[&](
const VectorDimD&
target)
227 VectorDimD
shift=VectorDimD::Zero();
230 for(
size_t crossing=0;crossing<10000;++crossing)
232 const Polyhedron<3,3-1>* exitFace=
nullptr;
233 double exitParameter=1.0;
234 for(
const auto& entry : grain->region.faces())
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)
241 const double candidate=
normal.dot(face->center()-center-shift)/speed;
242 if(candidate>=
parameter-1.e-9 && candidate<exitParameter-1.e-9)
244 exitParameter=candidate;
253 const auto* partner=exitFace->periodicFacePair.second;
254 if(!partner || exitFace->regionIDs!=partner->regionIDs)
256 throw std::runtime_error(
"Prismatic loop crosses a physical grain boundary; entire prism rejected.");
258 shift+=partner->center()-exitFace->center();
261 throw std::runtime_error(
"Prismatic loop exceeded periodic crossing limit; entire prism rejected.");
267 std::pair<short int,short int>
edges;
271 std::shared_ptr<PeriodicGlidePlane<3>>
plane;
272 DislocationLoopIO<3>::DislocationLoopType
type;
273 std::vector<PreparedNode> nodes;
278 const auto prepare=[&](
const std::vector<VectorDimD>&
polygon,
279 const std::shared_ptr<PeriodicGlidePlane<3>>&
plane,
282 std::vector<PolyPoint> anchors(
polygon.size());
283 std::vector<std::pair<VectorDimD,const PolyPoint* const>>
points;
286 points.emplace_back(polygon[
k],&anchors[
k]);
288 const auto intersections=
plane->polygonPatchIntersection(points,
true);
290 for(
const auto& point : intersections)
292 result.nodes.push_back({
plane->referencePlane->globalPosition(std::get<0>(point)),
293 std::get<1>(point),std::get<2>(point)});
295 VectorDimD originalArea=VectorDimD::Zero();
296 VectorDimD retainedArea=VectorDimD::Zero();
301 for(
size_t k=0;
k<
result.nodes.size();++
k)
303 retainedArea+=0.5*(
result.nodes[
k].position-
polygon.front()).cross(
306 if(
result.nodes.size()<3 || originalArea.norm()<=FLT_EPSILON ||
307 (retainedArea-originalArea).norm()>1.e-8*originalArea.norm())
309 throw std::runtime_error(
"Prismatic loop crosses a physical grain boundary; entire prism rejected.");
313 std::vector<PreparedLoop> prepared;
315 const VectorDimD step(L*
b.cartesian());
316 std::vector<VectorDimD>
top;
317 for(
const auto& point : sessileLoopNodePos)
320 mappedShift(point+step);
321 top.push_back(point+step);
323 const VectorDimD topShift=mappedShift(center+step);
324 for(
auto& point :
top)
329 LatticePlaneKey(
axis.planeIndexOfPoint(center+step+topShift),
axis));
332 double loopLength=0.0;
333 for(
size_t k1=0;k1<
planes.size();++k1)
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)
344 const LatticePlaneKey
key(planes[k2].
n.planeIndexOfPoint(side.front()),planes[k2].n);
357 for(
const auto& loop : prepared)
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)
364 const VectorDimD position=node.position+node.shift;
371 nodeIDs.push_back(mg.
insertLoopNode(
id,node.position,foundNode->second,node.shift,node.edges));
391 std::cout<<
"Skipping slip system "<<rSS<<std::endl;
395 throw std::runtime_error(
"slipSystem "+std::to_string(rSS)+
" not found, skipping.");
401 std::cout<<
"Center outside mesh, skipping prismatic loop"<<std::endl;
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
VectorDimD cartesian() const
size_t insertNetworkNode(const VectorDimD &networkNodePos)
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)
const DefectiveCrystalConfigIO< 3 > & config() const
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
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)
void parameter(std::string &text, const std::string &key, const std::string &value)