Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testJunctionFormationSafety.cpp
Go to the documentation of this file.
5#include <filesystem>
6#include <fstream>
7#include <iomanip>
8#include <chrono>
9#include <iostream>
10#include <set>
11
12void require(bool condition, const char* message)
13{
14 if(!condition)
15 {
16 throw std::runtime_error(message);
17 }
18}
19
20namespace model
21{
23 {
26 using Queue=std::deque<Formation::IntersectionTypeContainerType>;
27 static Queue find(Network& network)
28 {
29 Queue result(1);
30 network.junctionsMaker.findIntersections(result,1);
31 return result;
32 }
33 static size_t contract(Network& network,const Queue& queue)
34 {
35 return network.junctionsMaker.contractJunctions(queue);
36 }
37 static size_t refresh(Network& network,const Queue& queue)
38 {
39 size_t count=0;
40 for(const auto& item : queue.front())
41 {
42 const auto a=network.networkLinks().get(std::get<0>(item));
43 const auto b=network.networkLinks().get(std::get<1>(item));
44 if(a && b)
45 {
46 Queue refreshed(1);
47 network.junctionsMaker.findPairIntersections(refreshed,a.get(),b.get());
48 count+=refreshed.front().size();
49 }
50 }
51 return count;
52 }
53 static void glissile(Network& network)
54 {
55 network.junctionsMaker.glissileJunctions(0.0);
56 }
57 };
58}
59
60int main(int argc, char** argv)
61{
62 try
63 {
64 require(argc >= 2, "Expected repository root");
65 const std::filesystem::path root(argv[1]);
66 const auto folder = std::filesystem::temp_directory_path() /
67 ("junction-safety-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
68 const auto input = folder / "inputFiles";
69 std::filesystem::create_directories(input);
70 std::filesystem::create_directories(folder / "F");
71 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
72 const auto meshFile = root / "Library/Meshes/unitCube24.msh";
73 std::filesystem::copy_file(meshFile, input / "mesh.msh");
74 model::SimplicialMesh<3> mesh(meshFile.string(), Eigen::Matrix3d::Identity(), Eigen::Vector3d::Zero(), {-1});
75 std::vector<int> seeds;
76 for(int axis = 0; axis < 3; ++axis)
77 {
78 for(const auto& region : mesh.regions())
79 {
80 for(const auto& face : region.second->faces())
81 {
82 const auto& pair = face.second->periodicFacePair;
83 if(pair.second && pair.first[axis] > 0.9 &&
84 pair.first.normalized().cross(Eigen::Vector3d::Unit(axis)).norm() < 1.e-8)
85 {
86 seeds.push_back(face.first);
87 }
88 }
89 }
90 }
91 require(seeds.size() == 3, "Expected three cube periodic directions");
92 model::StaticID<model::Polyhedron<3,3-1>>::force_count(0);
93
94 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
95 const double length=200.0/std::sqrt(3.0);
96 for(int scenario=0;scenario<5;++scenario)
97 {
98 {
99 std::ofstream f(input/"DefectiveCrystal.txt");
100 f<<"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0;\nEwaldLengthFactor=0;\n";
101 std::ofstream p(input/"polycrystal.txt");
102 p<<std::setprecision(17)<<"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\nF="<<length<<" 0 0 0 "<<length<<" 0 0 0 "<<length<<";\nX0=0 0 0;\nperiodicFaceIDs="<<seeds[scenario==2 ? 0 : 1]<<";\n";
103 }
104 model::DefectiveCrystalBase<3> base(folder.string());
105 model::MicrostructureContainer<3> container(base);
107 using V=Eigen::Vector3d;
109 Network network(container);
110 const auto grain=base.poly.grain(1);
111 const auto planeAt=[&](const V& point,const V& normal)
112 {
113 return base.dislocationDynamics().glidePlaneFactory.getFromKey(model::GlidePlaneKey<3>(point,grain->reciprocalLatticeDirection(normal)));
114 };
115 const auto plane=planeAt(V::Zero(),V::UnitZ());
116 const auto insert=[&](const V& burgers,const auto& gp,const auto& nodes)
117 {
118 const auto loop=network.loops().create(burgers,gp);
119 std::vector<std::shared_ptr<Network::LoopNodeType>> ring;
120 for(const auto& node : nodes)
121 {
122 ring.push_back(network.loopNodes().create(loop,node,node->get_P(),loop->periodicGlidePlane->getPatch(V::Zero()),
123 std::make_pair(std::shared_ptr<model::PeriodicPlaneEdge<3>>(),std::shared_ptr<model::PeriodicPlaneEdge<3>>())));
124 }
125 network.insertLoop(loop,ring);
126 return loop;
127 };
128 if(scenario==0 || scenario>=3)
129 {
130 const double delta=8.0/std::sqrt(3.0);
131 const V burgers=2.0/std::sqrt(3.0)*V::UnitY();
132 const auto a=network.networkNodes().create(V(0,-20,0));
133 const auto b=network.networkNodes().create(V(0,20,0));
134 const auto c=network.networkNodes().create(V(-20,0,0));
135 const auto d=network.networkNodes().create(V(delta,-20,0));
136 const auto e=network.networkNodes().create(V(delta,20,0));
137 const auto f=network.networkNodes().create(V(delta+10,0,-10));
138 const auto first=insert(burgers,plane,std::vector{a,b,c});
139 const auto second=insert(-burgers,planeAt(V(delta,0,0),V(1,0,1)),std::vector{d,e,f});
140 network.updateGeometry();
141 const auto keyA=std::make_pair(a->sID,b->sID);
142 const auto keyB=std::make_pair(d->sID,e->sID);
143 std::set<std::pair<size_t,size_t>> independentKeys;
144 if(scenario>=3)
145 {
146 // A second, independent collision must remain eligible in the
147 // same batch. A shared loop at the first collision must survive.
148 insert(2*burgers,plane,std::vector{a,b,network.networkNodes().create(V(-30,0,0))});
149 const V shift=scenario==3 ? V(0,0,40.0/std::sqrt(3.0)) : V(0,40.0/std::sqrt(3.0),0);
150 const auto aa=network.networkNodes().create(a->get_P()+shift);
151 const auto bb=network.networkNodes().create(b->get_P()+shift);
152 const auto cc=network.networkNodes().create(c->get_P()+shift);
153 const auto dd=network.networkNodes().create(d->get_P()+shift);
154 const auto ee=network.networkNodes().create(e->get_P()+shift);
155 const auto ff=network.networkNodes().create(f->get_P()+shift);
156 if(scenario==3)
157 {
158 insert(burgers,planeAt(shift,V::UnitZ()),std::vector{aa,bb,cc});
159 }
160 else
161 {
162 // Sharing even a distant node couples the two collisions.
163 insert(burgers,plane,std::vector{aa,bb,cc,c});
164 }
165 insert(-burgers,planeAt(V(delta,0,0)+shift,V(1,0,1)),std::vector{dd,ee,ff});
166 independentKeys.insert(std::minmax(aa->sID,bb->sID));
167 independentKeys.insert(std::minmax(dd->sID,ee->sID));
168 network.updateGeometry();
169 }
170 auto queue=Access::find(network);
171 Access::Queue selected(1);
172 for(const auto& item : queue.front())
173 {
174 if((std::get<0>(item)==keyA && std::get<1>(item)==keyB) ||
175 (std::get<0>(item)==keyB && std::get<1>(item)==keyA) ||
176 (independentKeys.count(std::get<0>(item)) && independentKeys.count(std::get<1>(item))))
177 {
178 selected.front().push_back(item);
179 }
180 }
181 require(!selected.front().empty(),"X-separated attractive segments were missed by sweep");
182 if(scenario>=3)
183 {
184 require(selected.front().size()>=2,"Expected an event for each independent collision");
185 // Queue the remaining collisions on the same contours after
186 // the two independent pairs. These must wait for reconciliation.
187 for(const auto& event : queue.front())
188 {
189 selected.front().push_back(event);
190 }
191 require(Access::contract(network,selected)==(scenario==3 ? 2 : 1),
192 "Batch must contract independent pairs and defer their dependent events");
193 for(const auto& entry : network.loops())
194 {
195 require(entry.second.lock()->isLoop(),"Batch broke shared loop closure");
196 }
197 bool shared=false;
198 for(const auto& entry : network.networkLinks())
199 {
200 shared=shared || entry.second.lock()->loopLinks().size()>1;
201 }
202 require(shared,"Batch lost junction sharing");
203 require(Access::contract(network,Access::find(network))>0,
204 "Deferred junction was not available in the next iteration");
205 }
206 else
207 {
208 // Keep the link IDs alive but invalidate their stored collision.
209 network.trialNodeMotion=true;
210 for(const auto& node : {d,e,f})
211 {
212 node->trySet_P(node->get_P()+V(20,0,-20));
213 }
214 network.updateBoundaryNodes();
215 network.trialNodeMotion=false;
216 network.updateGeometry();
217 const auto nodeCount=network.networkNodes().size();
218 const auto linkCount=network.networkLinks().size();
219 require(Access::contract(network,selected)==0,"Stale collision was contracted");
220 require(network.networkNodes().size()==nodeCount && network.networkLinks().size()==linkCount,
221 "Stale rejection changed topology");
222 }
223 }
224 else
225 {
226 // Two sessile loops share a segment whose sum is a slip Burgers vector.
227 V burgers=V::Zero();
228 for(const auto& slip : grain->slipSystems())
229 {
230 if(std::abs(slip.second->n.cartesian().normalized().x())<1.e-8)
231 {
232 burgers=slip.second->s.cartesian();
233 break;
234 }
235 }
236 require(burgers.norm()>0,"Missing X-directed glide plane");
237 const auto a=network.networkNodes().create(V(0,0,0));
238 const auto b=network.networkNodes().create(V(0.4*length,0,0));
239 const auto c=network.networkNodes().create(V(0.2*length,0.2*length,0));
240 const auto d=network.networkNodes().create(V(0.2*length,-0.2*length,0));
241 const auto first=insert(2*burgers,plane,std::vector{a,b,c});
242 const auto second=insert(-burgers,plane,std::vector{a,b,d});
243 network.updateBoundaryNodes();
244 network.updateGeometry();
245 network.trialNodeMotion=true;
246 b->trySet_P(V(0.7*length,0,0));
247 network.updateBoundaryNodes();
248 network.trialNodeMotion=false;
249 network.updateGeometry();
250 std::shared_ptr<Network::NetworkNodeType> endpoint;
251 for(const auto& entry : network.networkLinks())
252 {
253 const auto link=entry.second.lock();
254 if(link->loopLinks().size()==2 && (link->source==a || link->sink==a))
255 {
256 endpoint=link->source==a ? link->sink : link->source;
257 }
258 }
259 require(bool(endpoint) && endpoint->isBoundaryNode(),"Missing boundary junction endpoint");
260 // A third loop visits the endpoint but does not use the shared segment.
261 const auto e=network.networkNodes().create(endpoint->get_P()+V(-10,5,0));
262 const auto f=network.networkNodes().create(endpoint->get_P()+V(-10,-5,0));
263 const auto unrelated=insert(burgers,plane,std::vector{endpoint,e,f});
264 network.updateBoundaryNodes();
265 network.updateGeometry();
266 const auto area=[&]()
267 {
268 V result=V::Zero();
269 for(const auto* ll : unrelated->linkSequence())
270 {
271 result+=0.5*ll->source->get_P().cross(ll->sink->get_P());
272 }
273 return result;
274 };
275 const V before=area();
276 const auto count=network.loops().size();
277 const V probe(7,11,19);
278 const Eigen::Matrix3d stressBefore=network.stress(probe,nullptr,nullptr,nullptr);
279 Access::glissile(network);
280 network.updateGeometry();
281 const Eigen::Matrix3d stressAfter=network.stress(probe,nullptr,nullptr,nullptr);
282 require((stressAfter-stressBefore).norm()<1.e-9*std::max(1.e-12,stressBefore.norm()),
283 "Glissile seed changed the original stress field");
284 const auto shared=network.networkLinks().get(std::minmax(a->sID,endpoint->sID));
285 require(shared && shared->loopLinks().size()==3 && shared->hasZeroBurgers(),
286 "Glissile seed lost the original shared segment or its Burgers cancellation");
287 require(network.loops().size()>count,"Expected glissile junction was not created");
288 require((area()-before).norm()<1.e-8,"Unrelated loop at shared endpoint was altered");
289 require(first->isLoop() && second->isLoop() && unrelated->isLoop(),"Junction sharing broke loop closure");
290 }
291 for(const auto& entry : network.loopNodes())
292 {
293 const auto ln=entry.second.lock();
294 require((ln->get_P()+ln->periodicPlanePatch()->shift-ln->networkNode->get_P()).norm()<1.e-7,
295 "Junction formation broke periodic mapping");
296 }
297 }
298 if(argc>2 && std::string(argv[2])=="benchmark")
299 {
300 std::filesystem::copy_file(root/"Library/Meshes/bicrystal_48.msh",input/"mesh.msh",std::filesystem::copy_options::overwrite_existing);
301 {
302 std::ofstream f(input/"DefectiveCrystal.txt");
303 f<<"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0 0 0;\nEwaldLengthFactor=0;\n";
304 std::ofstream p(input/"polycrystal.txt");
305 const double scale=3671.9477120460206;
306 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="<<scale<<" 0 0 0 "<<scale<<" 0 0 0 "<<scale<<";\nX0=0 0 0;\nperiodicFaceIDs=-1;\n";
307 }
308 model::DefectiveCrystalBase<3> base(folder.string());
309 model::MicrostructureContainer<3> container(base);
310 model::DislocationNetwork<3> network(container);
311 model::DefectiveCrystalConfigIO<3> saved(folder.string());
312 std::ifstream stream(root/"tests/data/bicrystalBoundary433.txt");
313 require(bool(stream),"Missing collision benchmark fixture");
314 saved.readTxtStream(stream);
315 network.initializeConfiguration(saved,std::ofstream(),std::ofstream());
317 auto queue=Access::find(network);
318 const auto searchStart=std::chrono::steady_clock::now();
319 for(int i=0;i<20;++i)
320 {
321 queue=Access::find(network);
322 }
323 const double searchSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-searchStart).count()/20;
324 const auto refreshStart=std::chrono::steady_clock::now();
325 size_t refreshed=0;
326 for(int i=0;i<200;++i)
327 {
328 refreshed+=Access::refresh(network,queue);
329 }
330 const double refreshSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-refreshStart).count()/200;
331 require(!queue.front().empty() && refreshed>0,"Benchmark has no queued collisions");
332 std::cout<<"BENCHMARK: "<<queue.front().size()<<" queued events; search="<<searchSeconds
333 <<" s; refresh="<<refreshSeconds<<" s; ratio="<<refreshSeconds/searchSeconds<<'\n';
334 }
335 std::filesystem::remove_all(folder);
336 std::cout<<"PASS: X-separated candidates, stale collisions, physical endpoints and shared periodic junctions\n";
337 return 0;
338 }
339 catch(const std::exception& error)
340 {
341 std::cerr<<error.what()<<'\n';
342 return 1;
343 }
344}
Eigen::Vector3d V
void updateBoundaryNodes(const std::function< void()> &checkpoint={})
DislocationJunctionFormation< dim > junctionsMaker
MatrixDim stress(const VectorDim &, const NodeType *const, const ElementType *const ele, const SimplexDim *const guess) const override
void initializeConfiguration(const DefectiveCrystalConfigIO< dim > &configIO, const std::ofstream &f_file, const std::ofstream &F_labels) override
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
c
Definition int_R.m:15
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
size_t InstanceCount< Derived >::count
LatticePlaneKey< dim > GlidePlaneKey
int regions
Definition triangle.cpp:696
std::deque< Formation::IntersectionTypeContainerType > Queue
static size_t refresh(Network &network, const Queue &queue)
static size_t contract(Network &network, const Queue &queue)
DislocationJunctionFormation< model::TypeTraits< Network >::dim > Formation
SharedPtrType create(const Args &...args)
SharedPtrType get(const KeyType &key) const
model::DislocationNetwork< 3 > Network
void require(bool condition, const char *message)
REAL cc[4]
REAL aa[4]
REAL bb[4]
void argv
int axis
void b int argc
vertex p[3]
int i
int e
REAL area
result
vertex first