Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testCrossSlipTopology.cpp
Go to the documentation of this file.
6#include <filesystem>
7#include <fstream>
8#include <iomanip>
9#include <chrono>
10#include <iostream>
11#include <set>
12#include <array>
13#include <sstream>
14#ifdef _OPENMP
15#include <omp.h>
16#endif
18namespace model
19{
21{
22 template<class N> static void queue(N& network,const std::deque<std::shared_ptr<typename N::LoopNodeType>>& nodes,int slip)
23 {
24 network.crossSlipMaker.csNodes.clear();
25 network.crossSlipMaker.csNodes.emplace_back(nodes,slip);
26 }
27};
28}
29
30
31void require(bool condition, const char* message)
32{
33 if(!condition)
34 {
35 throw std::runtime_error(message);
36 }
37}
38
39
40// Compare the production parallel post-event refresh with the original serial
41// refresh on the identical topology, avoiding restart and allocation differences.
43{
44 const auto capture=[&]()
45 {
46 std::vector<Eigen::Matrix<double,22,1>> values;
47 for(const auto& entry : network.networkLinks())
48 {
49 for(const auto& point : entry.second.lock()->quadraturePoints())
50 {
51 Eigen::Matrix<double,22,1> value;
52 value.head<9>()=Eigen::Map<const Eigen::Matrix<double,9,1>>(point.stress.data());
53 value.segment<3>(9)=point.pkForce;
54 value.segment<3>(12)=point.lineTensionForce;
55 value.segment<3>(15)=point.stackingFaultForce;
56 value.segment<3>(18)=point.velocity;
57 value(21)=point.coreEnergyPerLength;
58 values.push_back(value);
59 }
60 }
61 return values;
62 };
63 const auto parallel=capture();
64 require(!parallel.empty(),"Cross-slip refresh fixture has no quadrature points");
65 const auto velocities=network.glideSolver->getNodeVelocities().eval();
66 for(const auto& entry : network.networkLinks())
67 {
68 entry.second.lock()->createQuadraturePoints(false);
69 }
70 for(const auto& entry : network.networkLinks())
71 {
72 entry.second.lock()->updateQuadraturePoints(false);
73 }
74 const auto serial=capture();
75 require(serial.size()==parallel.size(),"Parallel refresh changed quadrature count");
76 for(size_t k=0;k<serial.size();++k)
77 {
78 require((serial[k].array()==parallel[k].array()).all(),"Parallel refresh changed quadrature fields or forces");
79 }
80 require((velocities.array()==network.glideSolver->getNodeVelocities().array()).all(),
81 "Parallel refresh changed nodal velocities");
82}
83
84int main(int argc, char** argv)
85{
86 try
87 {
88#ifdef _OPENMP
89 omp_set_num_threads(4);
90#endif
91 require(argc == 2, "Expected repository root");
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");
99
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");
102 {
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");
109 outputDD<<contents;
110 }
111 std::filesystem::create_directories(folder / "config");
112 model::SimplicialMesh<3> mesh((input/"mesh.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),{-1});
113 std::array<int,3> seeds{-1,-1,-1};
114 for(const auto& region : mesh.regions())
115 {
116 for(const auto& face : region.second->faces())
117 {
118 const auto& pair=face.second->periodicFacePair;
119 if(pair.second)
120 {
121 for(int axis=0;axis<3;++axis)
122 {
123 if(pair.first.normalized().cross(Eigen::Vector3d::Unit(axis)).norm()<1.e-8)
124 {
125 seeds[axis]=face.first;
126 }
127 }
128 }
129 }
130 }
131 require(seeds[0]>=0 && seeds[1]>=0,"Missing periodic seeds");
132 model::StaticID<model::Polyhedron<3,3-1>>::force_count(0);
133 const double length=3671.9477120460206;
134 for(int scenario=0;scenario<6;++scenario)
135 {
136 const bool full=scenario==1;
137 const bool periodic=scenario!=2;
138 {
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=";
143 if(periodic)
144 {
145 p<<(full ? -1 : seeds[1]);
146 }
147 p<<";\n";
148 }
149 model::DefectiveCrystalBase<3> base(folder.string());
150 model::MicrostructureContainer<3> container(base);
152 using V=Eigen::Vector3d;
153 Network network(container);
154 const auto grain=base.poly.grain(1);
155 const auto slip=grain->slipSystems().begin()->second;
156 int other=-1;
157 for(size_t k=0;k<grain->slipSystems().size();++k)
158 {
159 const auto candidate=grain->slipSystems().at(k);
160 if((candidate->s-slip->s).squaredNorm()==0 && candidate->unitNormal.cross(slip->unitNormal).norm()>0.1)
161 {
162 other=k;
163 break;
164 }
165 }
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;
173 model::PlanePlaneIntersection<3> intersection(plane->P,plane->unitNormal,height*css->n.planeSpacing()*css->unitNormal,css->unitNormal);
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)
177 {
178 along=-along;
179 }
180 V side=plane->unitNormal.cross(along).normalized();
181 if(scenario==4)
182 {
183 origin=-0.15*length*along;
184 if(side.x()>0.0)
185 {
186 side=-side;
187 }
188 }
189 if(scenario==5)
190 {
191 origin+=(0.5*length+2.0-origin.y())/along.y()*along;
192 }
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};
197 if(scenario==5)
198 {
199 polygon[1]-=4.0/side.y()*side;
200 }
201 if(scenario==3)
202 {
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;
206 }
207 model::MicrostructureGenerator generator(base);
208 require(generator.insertJunctionLoop(polygon,base.dislocationDynamics().periodicGlidePlaneFactory.get(plane->key),b,plane->unitNormal,plane->P,1,model::DislocationLoopIO<3>::GLISSILELOOP),"Parent insertion failed");
209 network.initializeConfiguration(generator.config(),std::ofstream(),std::ofstream());
210
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]})
214 {
215 for(const auto* link : parent->linkSequence())
216 {
217 if((link->source->get_P()-point).norm()<1.e-6 && !link->source->isPeriodicSubdivision())
218 {
219 branch.push_back(link->source);
220 break;
221 }
222 }
223 }
224 require(branch.size()==3,"Missing branch anchors");
225 // A second loop shares the final endpoint. Its plane either permits
226 // alignment, or locks the displaced endpoint against that motion.
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)));
230 const auto extra=network.loops().create(b,extraPlane);
231 V u=normal.cross(along).normalized();
232 if(scenario==4 && u.x()>0.0)
233 {
234 u=-u;
235 }
236 const auto e1=network.networkNodes().create(V(endpoint->get_P()-30.0*along));
237 const auto e2=network.networkNodes().create(V(endpoint->get_P()-30.0*along+20.0*u));
238 std::vector<std::shared_ptr<Network::LoopNodeType>> ring;
239 for(const auto& node : std::vector{endpoint,e1,e2})
240 {
241 ring.push_back(network.loopNodes().create(extra,node,node->get_P(),extra->periodicGlidePlane->getPatch(V::Zero()),std::make_pair(nullptr,nullptr)));
242 }
243 network.insertLoop(extra,ring);
244 network.updateGeometry();
245 const auto snapshot=[&]()
246 {
247 std::ostringstream out;
248 out<<std::setprecision(17);
249 for(const auto& entry : network.networkNodes())
250 {
251 const auto node=entry.second.lock();
252 out<<node->sID<<' '<<node->get_P().transpose()<<' '<<node->get_V().transpose()<<'\n';
253 }
254 for(const auto& entry : network.loops())
255 {
256 out<<entry.first<<'\n';
257 for(const auto* link : entry.second.lock()->linkSequence())
258 {
259 out<<link->source->sID<<' '<<link->source->networkNode->sID<<' '<<link->source->get_P().transpose()<<' '<<link->sink->sID<<'\n';
260 }
261 }
262 return out.str();
263 };
264 if(scenario!=3 && scenario!=5)
265 {
266 for(const auto& entry : network.networkLinks())
267 {
268 const auto segment=entry.second.lock();
269 segment->createQuadraturePoints(false);
270 for(auto& point : segment->quadraturePoints())
271 {
272 point.pkForce=css->unitNormal.cross(b.normalized());
273 }
274 }
275 std::deque<model::CrossSlipBranch<model::TypeTraits<Network>::dim>> discovered;
276 parent->crossSlipBranches(discovered);
277 bool found=false;
278 for(const auto& candidate : discovered)
279 {
280 bool hasFirst=false,hasLast=false;
281 for(const auto& node : candidate.first)
282 {
283 hasFirst=hasFirst || node==branch.front();
284 hasLast=hasLast || node==branch.back();
285 }
286 found=found || (hasFirst && hasLast);
287 }
288 if(!found)
289 {
290 std::cerr<<"scenario "<<scenario<<" desired slip "<<other<<" discovered "<<discovered.size()<<"\n";
291 for(const auto* link : parent->linkSequence())
292 {
293 if(link->networkLink())
294 {
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";
297 }
298 }
299 }
300 require(found,"Branch discovery lost periodic or physical endpoint");
301 }
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();
306 model::CrossSlipTestAccess::queue(network,branch,other);
307 network.crossSlipMaker.execute();
308 if(scenario==3)
309 {
310 require(snapshot()==before,"Rejected alignment changed network");
311 continue;
312 }
313 checkSerialRefresh(network);
314 if(scenario==5)
315 {
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");
318 }
319 branch.clear();
320 ring.clear();
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())
325 {
326 const auto loop=entry.second.lock();
327 if(loop!=parent && loop!=extra)
328 {
329 seed=loop;
330 }
331 }
332 require(seed && seed->loopLinks().size()>=4 && seed->slipSystem(),"Invalid zero-area seed");
333 if(scenario==4)
334 {
335 bool physical=false;
336 for(const auto* node : endpoint->loopNodes())
337 {
338 if(node->loop()==seed)
339 {
340 physical=node->isPhysicalBoundaryNode();
341 }
342 }
343 require(physical,"Cross-slip lost physical endpoint metadata");
344 }
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())
348 {
349 seams+=link->source->isPeriodicSubdivision();
350 if(link->networkLink() && link->networkLink()->loopLinks().size()==2)
351 {
352 require(link->networkLink()->hasZeroBurgers(),"Parent branch Burgers vector not cancelled");
353 ++cancelled;
354 }
355 require((link->source->get_P()+link->source->periodicPlanePatch()->shift-link->source->networkNode->get_P()).norm()<1.e-6,"Incorrect periodic mapping");
356 }
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();
360 size_t k=0;
361 for(const auto& entry : network.networkNodes())
362 {
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");
367 ++k;
368 }
369 // Drive only the free side of the seed on its new plane. This
370 // checks the ordinary motion/boundary walk after seed creation.
371 for(const auto& entry : network.networkNodes())
372 {
373 entry.second.lock()->set_V(V::Zero(),false);
374 }
375 for(const auto* link : seed->linkSequence())
376 {
377 const auto node=link->source->networkNode;
378 if(node->loopNodes().size()==1)
379 {
380 node->set_V(css->unitNormal.cross(along).normalized(),false);
381 }
382 }
383 network.moveNodes(0.1);
384 require(seed->currentAreaVector().norm()>1.e-6,"Seed failed to expand under motion");
385 require(endpoint->loopNodes().size()==3,"Motion lost shared endpoint");
386 }
387 std::filesystem::remove_all(folder);
388 std::cout<<"PASS: cross-slip seed, periodic mapping, shared endpoint, fresh velocities and atomic rejection\n";
389 return 0;
390 }
391 catch(const std::exception& error)
392 {
393 std::cerr<<error.what()<<'\n';
394 return 1;
395 }
396}
Eigen::Vector3d V
std::shared_ptr< DislocationGlideSolverBase< dim > > glideSolver
void moveNodes(const double &dt_in)
DislocationCrossSlip< dim > crossSlipMaker
void initializeConfiguration(const DefectiveCrystalConfigIO< dim > &configIO, const std::ofstream &f_file, const std::ofstream &F_labels) override
const std::shared_ptr< BaseCrossSlipModel< dim > > crossSlipModel
const LoopContainerType & loops() const
Definition LoopNetwork.h:98
const NetworkNodeContainerType & networkNodes() const
const LoopNodeContainerType & loopNodes() const
NetworkLinkContainerType & networkLinks()
void insertLoop(const std::shared_ptr< LoopType > &loop, const std::vector< std::shared_ptr< LoopNodeType > > nodes)
A class template that implements a counter of the number of instances of Derived type that are create...
Definition StaticID.h:29
function int_R syms a b c u real f
Definition int_R.m:4
a
Definition int_R.m:14
b
Definition int_R.m:16
LatticePlaneKey< dim > GlidePlaneKey
int regions
Definition triangle.cpp:696
static void queue(N &network, const std::deque< std::shared_ptr< typename N::LoopNodeType > > &nodes, int slip)
SharedPtrType create(const Args &...args)
SharedPtrType get(const KeyType &key) const
void periodic(const Trial &field, double relative=1.e-10)
model::DislocationNetwork< 3 > Network
void checkSerialRefresh(model::DislocationNetwork< 3 > &network)
void require(bool condition, const char *message)
int end[2]
void out
int parent
void argv
REAL u[4]
int axis
void b int argc
vertex p[3]
int k