96 if(!DN.crossSlipModel || !DN.glideSolver || DN.isClimbStep())
100 auto phaseStart=std::chrono::steady_clock::now();
101 const auto reportPhase=[&](
const char* phase)
103 const auto now=std::chrono::steady_clock::now();
104 std::cout<<
"CrossSlip: "<<phase<<
" ["<<std::chrono::duration<double>(now-phaseStart).count()<<
" sec]"<<std::endl;
105 phaseStart=std::chrono::steady_clock::now();
107 CrossSlipBranchContainerType alignedBranches;
108 std::set<size_t> reservedNodes;
109 for(
const auto& branch : csNodes)
111 if(branch.first.size()<(branch.thermal? 2 : 3))
115 const auto loop=branch.first.back()->loop();
116 if(!loop->glidePlane)
120 const auto& system=loop->grain.slipSystems().at(branch.second);
121 VectorDim midpoint=VectorDim::Zero();
122 double branchLength=0.0;
123 for(
size_t k=1;
k<branch.first.size();++
k)
125 const auto&
a=branch.first[
k-1]->get_P();
126 const auto&
b=branch.first[
k]->get_P();
127 const double length=(
b-
a).norm();
128 midpoint+=0.5*length*(
a+
b);
129 branchLength+=length;
131 if(branchLength<=FLT_EPSILON)
135 midpoint/=branchLength;
136 const long spacing=std::max(1L,
static_cast<long>(20.0/system->n.planeSpacing()));
137 const long height=std::lround(
double(system->n.closestPlaneIndexOfPoint(midpoint))/spacing)*spacing;
138 const VectorDim planePoint=height*system->n.planeSpacing()*system->unitNormal;
140 planePoint,system->unitNormal);
143 std::map<size_t,VectorDim> targets;
145 std::set<LoopType*> affected;
146 for(
const auto& node : branch.first)
148 const auto net=node->networkNode;
149 const VectorDim delta=intersection.
P+(node->get_P()-intersection.
P).dot(intersection.
d)*intersection.
d-node->get_P();
150 const VectorDim target=net->get_P()+delta;
151 const auto prior=targets.find(net->sID);
152 if(reservedNodes.count(net->sID) || (prior!=targets.end() && (prior->second-target).norm()>FLT_EPSILON))
158 for(
const auto* plane : net->glidePlanes())
160 normals.push_back(plane->unitNormal);
162 for(
int row=0;row<DN.nodalVelocityConstraints.rows();++row)
164 normals.push_back(DN.nodalVelocityConstraints.row(row));
166 if(!target.allFinite() || !net->isMovableTo(target) ||
167 (
oneSidedMotion<dim>(delta,normals,net->physicalBoundaryNormals())-delta).norm()>FLT_EPSILON)
172 for(
const auto* occurrence : net->loopNodes())
174 affected.insert(occurrence->loop().get());
176 targets.emplace(net->sID,target);
178 for(
const auto* affectedLoop : affected)
184 std::vector<std::pair<VectorDim,const LoopNodeType* const>> polygon;
185 for(
const auto* link : affectedLoop->linkSequence())
187 const auto& node=link->source;
188 if(!node->isPeriodicSubdivision())
190 VectorDim position=node->get_P();
191 const auto target=targets.find(node->networkNode->sID);
192 if(target!=targets.end())
194 position+=target->second-node->networkNode->get_P();
196 polygon.emplace_back(position,node.get());
199 const auto contour=affectedLoop->periodicGlidePlane->polygonPatchIntersection(polygon,
true);
200 std::vector<double> coverage(polygon.size(),0.0);
201 for(
size_t k=0;
k<contour.size();++
k)
203 const auto& metadata=std::get<6>(contour[
k]);
204 if(metadata.incomingOriginalSegment)
206 coverage.at(metadata.incomingOriginalSegment->sourceIndex)+=
207 (std::get<0>(contour[
k])-std::get<0>(contour[(
k+contour.size()-1)%contour.size()])).norm();
214 for(
size_t k=0;
k<polygon.size();++
k)
216 const double length=(polygon[(
k+1)%polygon.size()].
first-polygon[
k].
first).norm();
217 if(std::abs(length-coverage[
k])>1.e-8*std::max(1.0,length))
225 double alignedLength=0.0;
226 for(
size_t k=1;
k<branch.first.size();++
k)
228 alignedLength+=std::abs((branch.first[
k]->get_P()-branch.first[
k-1]->get_P()).dot(intersection.
d));
230 if(alignedLength+FLT_EPSILON<branch.thermal->length)
239 for(
const auto& [
id,target] : targets)
241 const auto net=DN.networkNodes().get(
id);
242 const VectorDim delta=target-net->get_P();
243 for(
auto* occurrence : net->loopNodes())
249 reservedNodes.insert(
id);
251 alignedBranches.push_back(branch);
254 reportPhase(
"alignment validation and commit");
255 std::cout<<
"CrossSlip: "<<alignedBranches.size()<<
" aligned branches"<<std::endl;
256 if(alignedBranches.empty())
260 DN.updateBoundaryNodes();
261 reportPhase(
"boundary update");
262 std::vector<std::shared_ptr<LoopType>> newLoops;
263 for(
auto& branch : alignedBranches)
268 for(
auto& anchor : branch.first)
270 const auto parent=anchor->loop();
271 std::shared_ptr<LoopNodeType> live;
272 for(
const auto* link :
parent->linkSequence())
274 if(link->source->networkNode==anchor->networkNode &&
275 (link->source->get_P()-anchor->get_P()).norm()<FLT_EPSILON)
283 throw std::runtime_error(
"Cross-slip alignment lost a branch anchor");
287 const auto parent=branch.first.back()->loop();
293 const auto first=branch.first.front();
294 const auto last=branch.first.back();
296 auto* cursor=
first.get();
298 while(cursor!=last.get())
300 if(!cursor || !cursor->next.first || ++walked>
parent->loopLinks().size())
302 throw std::runtime_error(
"Thermal branch lost connectivity");
304 total+=(cursor->next.first->get_P()-cursor->get_P()).norm();
305 cursor=cursor->next.first;
307 const double length=std::min(branch.thermal->length,total);
308 const double center=std::clamp(branch.thermal->centerFraction*total,0.5*length,total-0.5*length);
309 std::deque<std::shared_ptr<LoopNodeType>> nucleus;
310 for(
const double distance : {center-0.5*length,center,center+0.5*length})
315 std::shared_ptr<LoopNodeType> selected;
316 while(cursor!=last.get())
318 if(!cursor || !cursor->next.first || ++walked>
parent->loopLinks().size())
320 throw std::runtime_error(
"Thermal nucleus lost connectivity");
322 const auto* link=cursor->next.second;
323 const auto source=link->source;
324 const auto sink=link->sink;
325 const double chord=(sink->get_P()-source->get_P()).norm();
326 if(distance<=offset+FLT_EPSILON)
331 if(chord>FLT_EPSILON && distance<offset+chord-FLT_EPSILON)
333 const double u=(distance-offset)/chord;
334 const auto a=source->networkNode;
335 const auto b=sink->networkNode;
336 const VectorDim position=(1.0-
u)*
a->get_P()+
u*
b->get_P();
337 const auto net=DN.networkNodes().create(position,(1.0-
u)*
a->get_V()+
u*
b->get_V(),
338 (1.0-
u)*
a->climbVelocityScalar+
u*
b->climbVelocityScalar,
339 (1.0-
u)*
a->velocityReduction()+
u*
b->velocityReduction());
340 selected=DN.loopNodes().create(
parent,net,link);
341 DN.expandLoopLink(*link,selected);
347 nucleus.push_back(selected? selected : last);
349 branch.first=std::move(nucleus);
351 const auto& system=
parent->grain.slipSystems().at(branch.second);
352 const auto anchor=branch.first.back();
353 const VectorDim originShift=anchor->networkNode->get_P()-anchor->get_P();
354 const auto plane=DN.ddBase.dislocationDynamics().glidePlaneFactory.getFromKey(
356 const auto seed=DN.loops().create(system->s.cartesian(),plane);
357 std::deque<std::shared_ptr<LoopNodeType>> nodes;
364 throw std::runtime_error(
"Cross-slip branch lost its loop connectivity");
368 const VectorDim shift=
current->periodicPlanePatch()->shift-originShift;
369 const auto patch=seed->periodicGlidePlane->getPatch(shift);
370 std::pair<std::shared_ptr<PeriodicPlaneEdge<dim>>,std::shared_ptr<PeriodicPlaneEdge<dim>>>
edges;
371 if(
current->periodicPlaneEdge.first)
373 edges.first=patch->edgeOnFaces(
current->periodicPlaneEdge.first->meshIntersection->faces);
376 throw std::runtime_error(
"Cross-slip patch missing first boundary face");
379 if(
current->periodicPlaneEdge.second)
381 edges.second=patch->edgeOnFaces(
current->periodicPlaneEdge.second->meshIntersection->faces);
384 throw std::runtime_error(
"Cross-slip patch missing second boundary face");
387 const VectorDim position=
current->networkNode->get_P()-shift;
388 nodes.emplace_back(DN.loopNodes().create(seed,
current->networkNode,position,patch,
edges));
389 if(
current==branch.first.front().get())
395 nodes.emplace_front(DN.loopNodes().create(seed,DN.networkNodes().clone(
current->networkNode->sID),position,patch,
edges));
399 DN.insertLoop(seed,nodes);
400 newLoops.push_back(seed);
402 reportPhase(
"seed insertion");
405 for(
const auto& entry : DN.loops())
407 entry.second.lock()->updateGeometry();
409 reportPhase(
"loop geometry");
411 for(
const auto& entry : DN.networkLinks())
413 entry.second.lock()->createQuadraturePoints(
false);
415 reportPhase(
"quadrature creation");
419 std::vector<std::shared_ptr<NetworkLinkType>> links;
420 links.reserve(DN.networkLinks().size());
421 for(
const auto& entry : DN.networkLinks())
423 links.push_back(entry.second.lock());
426#pragma omp parallel for
428 for(
size_t k=0;
k<links.size();++
k)
430 links[
k]->updateQuadraturePoints(
false);
433 reportPhase(
"field refresh");
436 const Eigen::VectorXd velocities=DN.glideSolver->getNodeVelocities();
437 if(velocities.size()!=
static_cast<Eigen::Index
>(dim*DN.networkNodes().size()))
439 throw std::runtime_error(
"Cross-slip velocity solve returned wrong size");
442 for(
const auto& entry : DN.networkNodes())
444 entry.second.lock()->set_V(velocities.template segment<dim>(dim*
k),
false);
448 reportPhase(
"velocity solve and storage");
449 for(
const auto& entry : DN.loops())
451 entry.second.lock()->updateRates();
453 reportPhase(
"rate update");