8#ifndef model_DislocationNodeContraction_cpp_
9#define model_DislocationNodeContraction_cpp_
26#define VerboseNodeContraction(N,x) if(verboseNodeContraction>=N){std::cout<<x;}
28#define VerboseNodeContraction(N,x)
43 std::vector<std::set<std::shared_ptr<typename DislocationNodeContraction<dim>::NetworkNodeType>>> DislocationNodeContraction<dim>::contractBoundary(std::shared_ptr<NetworkNodeType> nA,
44 std::shared_ptr<NetworkNodeType> nB)
46 using NodeSet=std::set<std::shared_ptr<NetworkNodeType>>;
47 std::vector<NodeSet> groups;
48 const auto collect=[&](
const auto& chainA,
const auto& chainB)
50 for(
const auto*
a : chainA)
52 for(
const auto*
b : chainB)
56 if(
a->isPeriodicSubdivision() &&
b->isPeriodicSubdivision() &&
57 a->networkNode!=
b->networkNode &&
58 a->networkNode->meshFaces()==
b->networkNode->meshFaces() &&
59 (
a->networkNode->get_P()-
b->networkNode->get_P()).norm()<FLT_EPSILON)
61 NodeSet merged{
a->networkNode,
b->networkNode};
62 for(
auto it=groups.begin();it!=groups.end();)
65 for(
const auto& node : *it)
67 if(merged.count(node))
75 merged.insert(it->begin(),it->end());
83 groups.push_back(std::move(merged));
88 for(
const auto*
a : nA->loopNodes())
90 if(
a->periodicPlaneEdge.first)
94 for(
const auto*
b : nB->loopNodes())
96 if(
b->periodicPlaneEdge.first)
100 const auto ap=
a->periodicPrev();
101 const auto an=
a->periodicNext();
102 const auto bp=
b->periodicPrev();
103 const auto bn=
b->periodicNext();
104 if(ap && bp && ap->networkNode==bp->networkNode)
106 collect(
a->boundaryPrev(),
b->boundaryPrev());
108 if(an && bn && an->networkNode==bn->networkNode)
110 collect(
a->boundaryNext(),
b->boundaryNext());
112 if(ap && bn && ap->networkNode==bn->networkNode)
114 collect(
a->boundaryPrev(),
b->boundaryNext());
116 if(an && bp && an->networkNode==bp->networkNode)
118 collect(
a->boundaryNext(),
b->boundaryPrev());
126 bool DislocationNodeContraction<dim>::canReach(
const std::shared_ptr<NetworkNodeType>& node,
const VectorDim& target)
const
128 if(!
target.allFinite() || !node->isMovableTo(target))
133 typename NetworkNodeType::VectorOfNormalsType normals;
134 for(
const auto* plane : node->glidePlanes())
136 normals.push_back(
plane->unitNormal);
138 for(
int row=0;row<
DN.nodalVelocityConstraints.rows();++row)
140 normals.push_back(
DN.nodalVelocityConstraints.row(row));
143 if((actual-requested).norm()>FLT_EPSILON)
147 for(
const auto* ln : node->loopNodes())
149 if(ln->periodicPlaneEdge.first && requested.norm()>FLT_EPSILON)
155 if(!
DN.ddBase.mesh.searchRegion(ln->loop()->grain.grainID,target).first)
164 bool DislocationNodeContraction<dim>::commitContraction(std::shared_ptr<NetworkNodeType>
a,
165 std::shared_ptr<NetworkNodeType>
b,
const VectorDim& target,
const bool reconcileBoundary)
167 if(!
a || !
b ||
a==
b ||
a->loopNodes().empty() ||
b->loopNodes().empty() ||
168 !
b->isContractableTo(
a) || !canReach(
a,target) || !canReach(
b,target))
175 using NodePtr=std::shared_ptr<LoopNodeType>;
181 std::vector<Subdivision> subdivisions;
182 std::set<size_t> visitedLoops;
183 const double tolerance=1.e-8*(
DN.ddBase.mesh.xMax()-
DN.ddBase.mesh.xMin()).norm();
184 for(
const auto& endpoint : {
a,
b})
186 for(
const auto* occurrence : endpoint->loopNodes())
188 const auto loop=occurrence->loop();
189 if(!visitedLoops.insert(
loop->sID).second)
193 std::vector<NodePtr> nodes;
194 for(
const auto* link :
loop->linkSequence())
196 nodes.push_back(link->source);
199 std::vector<size_t> anchors;
200 for(
size_t i=0;
i<nodes.size();++
i)
202 if(!nodes[
i]->isPeriodicSubdivision())
204 anchors.push_back(
i);
207 for(
size_t k=0;
k<anchors.size();++
k)
209 const size_t begin=anchors[
k];
210 const size_t end=anchors[(
k+1)%anchors.size()];
211 const VectorDim chord=nodes[
end]->get_P()-nodes[begin]->get_P();
212 std::vector<Subdivision> edgeSubdivisions;
215 const double length=chord.norm();
216 for(
size_t i=(begin+1)%nodes.size();
i!=
end;
i=(
i+1)%nodes.size())
219 const double parameter=chord.squaredNorm()>0 ?
offset.dot(chord)/chord.squaredNorm() : 0;
220 if((offset-
parameter*chord).norm()>tolerance ||
226 edgeSubdivisions.push_back({nodes[
i],nodes[begin],nodes[
end],
parameter});
230 subdivisions.insert(subdivisions.end(),edgeSubdivisions.begin(),edgeSubdivisions.end());
236 for(
const auto& subdivision : edgeSubdivisions)
238 subdivision.node->periodicContourVertex=
true;
244 for(
const auto& node : {
a,
b})
246 for(
auto* ln : node->loopNodes())
248 const VectorDim shift=ln->periodicPlanePatch() ? ln->periodicPlanePatch()->shift : VectorDim::Zero();
249 static_cast<typename LoopNodeType::SplineNodeType&
>(*ln).set_P(target-shift);
257 for(
const auto& subdivision : subdivisions)
259 const VectorDim position=(1-subdivision.parameter)*subdivision.source->get_P()+
260 subdivision.parameter*subdivision.sink->get_P();
261 static_cast<typename LoopNodeType::SplineNodeType&
>(*subdivision.node).set_P(position);
263 subdivisions.clear();
264 const auto groups=contractBoundary(
a,
b);
265 if(!
DN.contractNetworkNodes(
a,
b))
267 throw std::logic_error(
"Validated node contraction unexpectedly rejected.");
269 for(
const auto& group : groups)
271 const auto survivor=*std::min_element(group.begin(),group.end(),[](
const auto&
x,
const auto&
y)
273 return x->sID<y->sID;
275 for(
const auto& node : group)
277 if(node!=survivor && !node->loopNodes().empty() && !survivor->loopNodes().empty())
279 DN.contractNetworkNodes(survivor,node);
283 if(reconcileBoundary)
285 DN.updateBoundaryNodes();
291 bool DislocationNodeContraction<dim>::contractSecondAndVirtual(std::shared_ptr<NetworkNodeType> nA,
292 std::shared_ptr<NetworkNodeType> nB,
const bool reconcileBoundary)
294 return commitContraction(nA,nB,nA->get_P(),reconcileBoundary);
298 bool DislocationNodeContraction<dim>::contractYoungest(std::shared_ptr<NetworkNodeType> nA,
299 std::shared_ptr<NetworkNodeType> nB,
const bool reconcileBoundary)
301 return nA->sID<nB->sID? contractSecondAndVirtual(nA,nB,reconcileBoundary) : contractSecondAndVirtual(nB,nA,reconcileBoundary);
306 bool DislocationNodeContraction<dim>::contractToPosition(std::shared_ptr<NetworkNodeType> nA,
307 std::shared_ptr<NetworkNodeType> nB,
309 const double& maxRange,
const bool reconcileBoundary)
312 if((nA->get_P()-X).norm()+(nB->get_P()-X).norm()>=maxRange)
316 return nA->sID<nB->sID ? commitContraction(nA,nB,X,reconcileBoundary) : commitContraction(nB,nA,
X,reconcileBoundary);
322 std::shared_ptr<NetworkNodeType> nB,
const bool reconcileBoundary)
324 if(!nA || !nB || nA==nB || nA->loopNodes().empty() || nB->loopNodes().empty())
328 VerboseNodeContraction(1,
"DislocationNodeContraction::contract " << nA->sID <<
" " << nB->sID << std::endl;);
329 if ( !nA->isBoundaryNode()
330 && !nA->isGrainBoundaryNode()
331 && !nB->isBoundaryNode()
332 && !nB->isGrainBoundaryNode()
336 const bool nAisMovable = canReach(nA,nB->get_P());
337 const bool nBisMovable = canReach(nB,nA->get_P());
339 if (nAisMovable && nBisMovable)
341 VerboseNodeContraction(1,
"DislocationNodeContraction case 1a" << std::endl;);
342 return contractYoungest(nA, nB,reconcileBoundary);
344 else if (nAisMovable && !nBisMovable)
346 VerboseNodeContraction(1,
"DislocationNodeContraction case 1b" << std::endl;);
347 return contractSecondAndVirtual(nB, nA,reconcileBoundary);
349 else if (!nAisMovable && nBisMovable)
351 VerboseNodeContraction(1,
"DislocationNodeContraction case 1c" << std::endl;);
352 return contractSecondAndVirtual(nA, nB,reconcileBoundary);
356 VerboseNodeContraction(1,
"DislocationNodeContraction case 1d" << std::endl;);
357 const auto planesA(nA->glidePlanes());
358 const auto planesB(nB->glidePlanes());
359 Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic>
N(Eigen::Matrix<double,dim,Eigen::Dynamic>::Zero(dim,planesA.size()+planesB.size()));
360 Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> P(Eigen::Matrix<double,dim,Eigen::Dynamic>::Zero(dim,planesA.size()+planesB.size()));
363 for(
const auto& plane : planesA)
365 N.col(
k)=plane->unitNormal;
369 for(
const auto& plane : planesB)
371 N.col(
k)=plane->unitNormal;
377 const std::pair<bool,VectorDim> snapped(pInt.
snap(0.5*(nA->get_P()+nB->get_P())));
380 const double maxRange = 4.0 * (nA->get_P() - nB->get_P()).norm();
381 return contractToPosition(nA,nB,snapped.second,maxRange,reconcileBoundary);
391 if ((nA->get_P() - nB->get_P()).norm() <= FLT_EPSILON)
393 VerboseNodeContraction(1,
"DislocationNodeContraction case 2a" << std::endl;);
394 return contractYoungest(nA, nB,reconcileBoundary);
398 VerboseNodeContraction(1,
"DislocationNodeContraction case 2b" << std::endl;);
TraitsType::LoopNodeType LoopNodeType
const int verboseNodeContraction
DislocationNodeContraction(DislocationNetwork< dim > &DN_in)
bool contract(std::shared_ptr< NetworkNodeType > nA, std::shared_ptr< NetworkNodeType > nB, const bool reconcileBoundary=true)
void retainPeriodicContourBends(const Nodes &nodes, const double tolerance)
Eigen::Matrix< double, dim, 1 > oneSidedMotion(const Eigen::Matrix< double, dim, 1 > &requested, const std::vector< Eigen::Matrix< double, dim, 1 > > &equalities, const std::vector< Eigen::Matrix< double, dim, 1 > > &outward)
std::pair< bool, VectorDim > snap(const VectorDim &x) const
void parameter(std::string &text, const std::string &key, const std::string &value)