89 omp_set_num_threads(4);
92 const std::filesystem::path root(
argv[1]);
93 const auto folder = std::filesystem::temp_directory_path() /
94 (
"cross-slip-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
95 const auto input = folder /
"inputFiles";
96 std::filesystem::create_directories(input);
97 std::filesystem::create_directories(folder /
"F");
98 std::filesystem::copy_file(root /
"Library/Materials/W.txt", input /
"W.txt");
100 std::filesystem::copy_file(root /
"Library/Meshes/bicrystal_48.msh", input /
"mesh.msh");
101 std::filesystem::copy_file(root /
"Library/DislocationDynamics/DD.txt", input /
"DD.txt");
103 std::ifstream inputDD(input/
"DD.txt");
104 std::string contents((std::istreambuf_iterator<char>(inputDD)),std::istreambuf_iterator<char>());
105 const auto pos=contents.find(
"crossSlipModel=0;");
106 require(pos!=std::string::npos,
"Missing cross-slip setting");
107 contents.replace(pos,17,
"crossSlipModel=1;");
108 std::ofstream outputDD(input/
"DD.txt");
111 std::filesystem::create_directories(folder /
"config");
113 std::array<int,3> seeds{-1,-1,-1};
116 for(
const auto& face : region.second->faces())
118 const auto& pair=face.second->periodicFacePair;
123 if(pair.first.normalized().cross(Eigen::Vector3d::Unit(
axis)).norm()<1.e-8)
125 seeds[
axis]=face.first;
131 require(seeds[0]>=0 && seeds[1]>=0,
"Missing periodic seeds");
133 const double length=3671.9477120460206;
134 for(
int scenario=0;scenario<6;++scenario)
136 const bool full=scenario==1;
139 std::ofstream
f(input/
"DefectiveCrystal.txt");
140 f<<
"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize="<<(full ?
"0 0 0" :
"0")<<
";\nEwaldLengthFactor=0;\n";
141 std::ofstream
p(input/
"polycrystal.txt");
142 p<<std::setprecision(17)<<
"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\nC2G2=0.8 0 0.6 0 1 0 -0.6 0 0.8;\nF="<<length<<
" 0 0 0 "<<length<<
" 0 0 0 "<<length<<
";\nX0=0 0 0;\nperiodicFaceIDs=";
145 p<<(full ? -1 : seeds[1]);
152 using V=Eigen::Vector3d;
154 const auto grain=base.poly.grain(1);
155 const auto slip=grain->slipSystems().begin()->second;
157 for(
size_t k=0;
k<grain->slipSystems().size();++
k)
159 const auto candidate=grain->slipSystems().at(
k);
160 if((candidate->s-slip->s).squaredNorm()==0 && candidate->unitNormal.cross(slip->unitNormal).norm()>0.1)
166 require(other>=0,
"No cross-slip system");
167 const auto css=grain->slipSystems().at(other);
168 const V b=slip->s.cartesian();
169 const V center=scenario==4 ? V::Zero().eval() :
V(length*
V(-0.5,
periodic ? 0.49 : 0.0,0));
170 const auto plane=base.dislocationDynamics().glidePlaneFactory.getFromKey(
model::GlidePlaneKey<3>(center,slip->n));
171 const long spacing=std::max(1L,
static_cast<long>(20.0/css->n.planeSpacing()));
172 const long height=std::lround(
double(css->n.closestPlaneIndexOfPoint(center))/spacing)*spacing;
174 V origin=intersection.
P+(center-intersection.
P).dot(intersection.
d)*intersection.
d;
175 V along=intersection.
d;
176 if(scenario==4 && along.x()<0.0)
180 V side=plane->unitNormal.cross(along).normalized();
183 origin=-0.15*length*along;
191 origin+=(0.5*length+2.0-origin.y())/along.y()*along;
193 const V a=origin-0.15*length*along;
194 const V middle=origin;
195 const V end=origin+0.15*length*along;
196 std::vector<V> polygon{
a,middle,
end,
end+100.0*side,
a+100.0*side};
199 polygon[1]-=4.0/side.y()*side;
203 polygon[0]+=0.2*side;
204 polygon[1]+=0.2*side;
205 polygon[2]+=2.0*css->n.planeSpacing()/css->unitNormal.dot(side)*side;
211 const auto parent=network.
loops().begin()->second.lock();
212 std::deque<std::shared_ptr<Network::LoopNodeType>> branch;
213 for(
const auto& point : std::vector<V>{polygon[0],polygon[1],polygon[2]})
215 for(
const auto* link :
parent->linkSequence())
217 if((link->source->get_P()-point).norm()<1.e-6 && !link->source->isPeriodicSubdivision())
219 branch.push_back(link->source);
224 require(branch.size()==3,
"Missing branch anchors");
227 const auto endpoint=branch.back()->networkNode;
228 const V normal=scenario==3 ? css->unitNormal : plane->unitNormal;
229 const auto extraPlane=base.dislocationDynamics().glidePlaneFactory.getFromKey(
model::GlidePlaneKey<3>(endpoint->get_P(),grain->reciprocalLatticeDirection(normal)));
231 V u=normal.cross(along).normalized();
232 if(scenario==4 &&
u.x()>0.0)
238 std::vector<std::shared_ptr<Network::LoopNodeType>> ring;
239 for(
const auto& node : std::vector{endpoint,e1,e2})
241 ring.push_back(network.
loopNodes().
create(extra,node,node->get_P(),extra->periodicGlidePlane->getPatch(V::Zero()),std::make_pair(
nullptr,
nullptr)));
245 const auto snapshot=[&]()
247 std::ostringstream
out;
248 out<<std::setprecision(17);
251 const auto node=entry.second.lock();
252 out<<node->sID<<
' '<<node->get_P().transpose()<<
' '<<node->get_V().transpose()<<
'\n';
254 for(
const auto& entry : network.
loops())
256 out<<entry.first<<
'\n';
257 for(
const auto* link : entry.second.lock()->linkSequence())
259 out<<link->source->sID<<
' '<<link->source->networkNode->sID<<
' '<<link->source->get_P().transpose()<<
' '<<link->sink->sID<<
'\n';
264 if(scenario!=3 && scenario!=5)
268 const auto segment=entry.second.lock();
269 segment->createQuadraturePoints(
false);
270 for(
auto& point : segment->quadraturePoints())
272 point.pkForce=css->unitNormal.cross(
b.normalized());
275 std::deque<model::CrossSlipBranch<model::TypeTraits<Network>::dim>> discovered;
276 parent->crossSlipBranches(discovered);
278 for(
const auto& candidate : discovered)
280 bool hasFirst=
false,hasLast=
false;
281 for(
const auto& node : candidate.first)
283 hasFirst=hasFirst || node==branch.front();
284 hasLast=hasLast || node==branch.back();
286 found=found || (hasFirst && hasLast);
290 std::cerr<<
"scenario "<<scenario<<
" desired slip "<<other<<
" discovered "<<discovered.size()<<
"\n";
291 for(
const auto* link :
parent->linkSequence())
293 if(link->networkLink())
295 const auto selected=network.
crossSlipModel->isCrossSlipLink(*link->networkLink());
296 std::cerr<<link->source->get_P().transpose()<<
" -> "<<link->sink->get_P().transpose()<<
" length "<<link->networkLink()->chordLength()<<
" selection "<<selected.first<<
" "<<selected.second.second<<
"\n";
300 require(found,
"Branch discovery lost periodic or physical endpoint");
302 const auto before=snapshot();
303 const auto endpointID=endpoint->sID;
304 const auto middleNode=branch[1]->networkNode;
305 const V oldMiddle=middleNode->get_P();
310 require(snapshot()==before,
"Rejected alignment changed network");
316 require((middleNode->get_P()-oldMiddle).norm()>0.5*length,
"Alignment did not cross the periodic face");
317 require(network.
networkNodes().
get(middleNode->sID)==middleNode,
"Alignment lost network identity at seam");
321 require(network.
loops().size()==3,
"Cross-slip seed was discarded");
322 require(network.
networkNodes().
get(endpointID)==endpoint && endpoint->loopNodes().size()==3,
"Shared endpoint was detached");
323 std::shared_ptr<Network::LoopType> seed;
324 for(
const auto& entry : network.
loops())
326 const auto loop=entry.second.lock();
327 if(loop!=
parent && loop!=extra)
332 require(seed && seed->loopLinks().size()>=4 && seed->slipSystem(),
"Invalid zero-area seed");
336 for(
const auto* node : endpoint->loopNodes())
338 if(node->loop()==seed)
340 physical=node->isPhysicalBoundaryNode();
343 require(physical,
"Cross-slip lost physical endpoint metadata");
345 require(seed->currentAreaVector().norm()<1.e-6,
"Seed added slipped area before motion");
346 size_t seams=0,cancelled=0;
347 for(
const auto* link : seed->linkSequence())
349 seams+=link->source->isPeriodicSubdivision();
350 if(link->networkLink() && link->networkLink()->loopLinks().size()==2)
352 require(link->networkLink()->hasZeroBurgers(),
"Parent branch Burgers vector not cancelled");
355 require((link->source->get_P()+link->source->periodicPlanePatch()->shift-link->source->networkNode->get_P()).norm()<1.e-6,
"Incorrect periodic mapping");
357 require(cancelled>=2,
"No cancellation on original branch");
358 require((seams>0)==(
periodic && scenario!=4),
"Periodic crossing was not preserved");
359 const auto expected=network.
glideSolver->getNodeVelocities();
363 const auto node=entry.second.lock();
364 const V stored=node->get_V();
365 node->set_V(expected.segment<3>(3*
k),
false);
366 require((stored-node->get_V()).norm()<1.e-7,
"Stale nodal velocity after cross-slip");
373 entry.second.lock()->set_V(V::Zero(),
false);
375 for(
const auto* link : seed->linkSequence())
377 const auto node=link->source->networkNode;
378 if(node->loopNodes().size()==1)
380 node->set_V(css->unitNormal.cross(along).normalized(),
false);
384 require(seed->currentAreaVector().norm()>1.e-6,
"Seed failed to expand under motion");
385 require(endpoint->loopNodes().size()==3,
"Motion lost shared endpoint");
387 std::filesystem::remove_all(folder);
388 std::cout<<
"PASS: cross-slip seed, periodic mapping, shared endpoint, fresh velocities and atomic rejection\n";
391 catch(
const std::exception& error)
393 std::cerr<<error.what()<<
'\n';