Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testPeriodicPolygonWalk.cpp
Go to the documentation of this file.
3#include <filesystem>
4#include <fstream>
5#include <iomanip>
6#include <chrono>
7#include <iostream>
8
9void require(bool condition, const char* message)
10{
11 if(!condition)
12 {
13 throw std::runtime_error(message);
14 }
15}
16
17struct OriginalNode {};
18
20 const std::vector<Eigen::Vector2d>& vertices, double expectedArea, bool reverse=false)
21{
22 const double length=base.mesh.xMax()[0]-base.mesh.xMin()[0];
23 const auto& grain=base.poly.grains.begin()->second;
24 const auto normal=grain->reciprocalLatticeDirection(Eigen::Vector3d::UnitZ());
25 auto plane=base.dislocationDynamics().periodicGlidePlaneFactory.get(model::GlidePlaneKey<3>(Eigen::Vector3d::Zero(),normal));
26 std::vector<OriginalNode> nodes(vertices.size());
27 std::vector<std::pair<Eigen::Vector3d,const OriginalNode* const>> polygon;
28 for(size_t k=0; k<vertices.size(); ++k)
29 {
30 const size_t i=reverse ? vertices.size()-1-k : k;
31 polygon.emplace_back(length*Eigen::Vector3d(vertices[i][0],vertices[i][1],0),&nodes[i]);
32 }
33 const auto records=plane->polygonPatchIntersection(polygon,true);
34 require(records.size()>=3,"Walk returned no contour");
35 double area=0.0;
36 for(size_t i=0; i<records.size(); ++i)
37 {
38 const Eigen::Vector3d p=plane->referencePlane->globalPosition(std::get<0>(records[i]));
39 const Eigen::Vector3d q=plane->referencePlane->globalPosition(std::get<0>(records[(i+1)%records.size()]));
40 area+=p.cross(q).z()/2.0;
41 const auto& provenance=std::get<6>(records[i]);
44 const auto verifySegment=[&](const model::PolygonOriginalSegment<OriginalNode>& segment)
45 {
46 require(segment.sourceIndex<polygon.size() && segment.sinkIndex==(segment.sourceIndex+1)%polygon.size(),
47 "Invalid original directed segment indices");
48 require(segment.source==polygon[segment.sourceIndex].second && segment.sink==polygon[segment.sinkIndex].second,
49 "Segment provenance lost its original node pointers");
50 };
51 if(provenance.polygonLocation)
52 {
53 const auto& location=*provenance.polygonLocation;
54 verifySegment(location.segment);
55 require(location.parameter>=0 && location.parameter<=1,"Invalid original segment parameter");
56 const Eigen::Vector3d expected=(1-location.parameter)*polygon[location.segment.sourceIndex].first+
57 location.parameter*polygon[location.segment.sinkIndex].first;
58 require((expected-p).norm()<1.e-6,"Original segment provenance does not reconstruct the intersection");
59 }
60 const auto metadataPatch=plane->getPatch(std::get<1>(records[i]));
61 bool physical=false;
62 size_t incidentEdges=0;
63 for(const auto& edge : metadataPatch->edges())
64 {
65 const Eigen::Vector2d d=*edge->sink-*edge->source;
66 const Eigen::Vector2d delta=std::get<0>(records[i])-*edge->source;
67 const double t=delta.dot(d)/d.squaredNorm();
68 if(t>=-1.e-8 && t<=1+1.e-8 && (delta-t*d).norm()<1.e-6)
69 {
70 ++incidentEdges;
71 for(const auto* face : edge->meshIntersection->faces)
72 {
73 physical=physical || !face->periodicFacePair.second ||
74 face->regionIDs!=face->periodicFacePair.second->regionIDs;
75 }
76 }
77 }
78 require(provenance.onPhysicalBoundary==physical && provenance.atPatchCorner==(incidentEdges>1),
79 "Boundary metadata disagrees with actual mesh faces");
80 bool seam=false;
81 for(size_t neighbor : {(i+records.size()-1)%records.size(),(i+1)%records.size()})
82 {
83 seam=seam || ((std::get<0>(records[i])-std::get<0>(records[neighbor])).norm()<1.e-6 &&
84 (std::get<1>(records[i])-std::get<1>(records[neighbor])).norm()>1.e-6);
85 }
86 require(provenance.crossesPeriodicSeam==seam,"Seam classification disagrees with contour patch transitions");
87 if(provenance.originalVertex)
88 {
89 require(provenance.kind==Kind::OriginalVertex &&
90 std::get<5>(records[i])==polygon[*provenance.originalVertex].second,
91 "Original vertex identity changed");
92 }
93 else if(provenance.onPhysicalBoundary)
94 {
95 require(provenance.kind==(provenance.polygonLocation ? Kind::PhysicalBoundaryIntersection : Kind::PhysicalBoundaryCorner),
96 "Physical boundary node misclassified");
97 }
98 else
99 {
100 require(provenance.kind==Kind::PeriodicCrossing && provenance.crossesPeriodicSeam,
101 "Periodic crossing misclassified");
102 }
103 if(provenance.incomingArc==Arc::OriginalSegment)
104 {
105 require(provenance.incomingOriginalSegment.has_value(),"Polygon arc has no original segment");
106 verifySegment(*provenance.incomingOriginalSegment);
107 const auto& origin=*provenance.incomingOriginalSegment;
108 const Eigen::Vector3d a=polygon[origin.sourceIndex].first;
109 const Eigen::Vector3d d=polygon[origin.sinkIndex].first-a;
110 const Eigen::Vector3d previous=plane->referencePlane->globalPosition(std::get<0>(records[(i+records.size()-1)%records.size()]));
111 require((p-a).cross(d).norm()<1.e-6 && (previous-a).cross(d).norm()<1.e-6,
112 "Incoming polygon arc attributed to wrong segment");
113 }
114 else
115 {
116 require(!provenance.incomingOriginalSegment,"Boundary or seam arc falsely attributed to original segment");
117 if(provenance.incomingArc==Arc::PeriodicSeam)
118 {
119 require(provenance.crossesPeriodicSeam &&
120 (std::get<0>(records[i])-std::get<0>(records[(i+records.size()-1)%records.size()])).norm()<1.e-6,
121 "Seam arc is not a zero-length periodic transition");
122 }
123 }
124
125 const Eigen::Vector3d mapped=p+std::get<1>(records[i]);
126 require((mapped.array()>=base.mesh.xMin().array()-1.e-6).all() &&
127 (mapped.array()<=base.mesh.xMax().array()+1.e-6).all(),"Mapped node outside RVE");
128 if(!std::get<5>(records[i]))
129 {
130 require(std::get<2>(records[i]).first>=0,"Inserted boundary node lacks edge ID");
131 const auto patch=plane->getPatch(std::get<1>(records[i]));
132 for(short id : {std::get<2>(records[i]).first,std::get<2>(records[i]).second})
133 {
134 if(id>=0)
135 {
136 const auto edge=patch->edges().at(id);
137 const Eigen::Vector2d direction=*edge->sink-*edge->source;
138 const Eigen::Vector2d delta=std::get<0>(records[i])-*edge->source;
139 const double t=delta.dot(direction)/direction.squaredNorm();
140 require(t>=-1.e-8 && t<=1+1.e-8 && (delta-t*direction).norm()<1.e-6,
141 "Intersection record references the wrong patch edge");
142 }
143 }
144 }
145 }
146 require(std::abs(area/(length*length)-(reverse ? -expectedArea : expectedArea))<1.e-8,
147 "Wrong retained area or orientation");
148 const bool retainsOriginal=std::any_of(records.begin(),records.end(),[](const auto& record)
149 {
150 return std::get<5>(record)!=nullptr;
151 });
152 require(!retainsOriginal || std::get<5>(records.back())!=nullptr,"Contour must finish at an original node");
153 // Geometry must also be checked in the runtime mode; this dummy deliberately
154 // has no periodicPlanePatch() accessor, so null metadata is safe too.
155 const auto runtime=plane->polygonPatchIntersection(polygon,false);
156 require(runtime.size()==records.size(),"Runtime walk differs from generator walk");
157 std::vector<std::pair<Eigen::Vector2d,const OriginalNode* const>> localPolygon;
158 for(const auto& vertex : polygon)
159 {
160 localPolygon.emplace_back(plane->referencePlane->localPosition(vertex.first),vertex.second);
161 }
162 const auto localRecords=plane->polygonPatchIntersection(localPolygon,true);
163 for(size_t i=0; i<records.size(); ++i)
164 {
165 const auto& a=std::get<6>(records[i]);
166 const auto& b=std::get<6>(localRecords.at(i));
167 require(a.kind==b.kind && a.originalVertex==b.originalVertex && a.incomingArc==b.incomingArc,
168 "2D and 3D provenance disagree");
169 }
170}
171
172// No polygon segment remains inside a fully enclosed cross-section.
174{
175 using V=Eigen::Vector3d;
176 const double length=base.mesh.xMax().x()-base.mesh.xMin().x();
177 const auto grain=base.poly.grains.begin()->second;
178 const auto plane=base.dislocationDynamics().periodicGlidePlaneFactory.get(model::GlidePlaneKey<3>(V::Zero(),grain->reciprocalLatticeDirection(V::UnitZ())));
179 for(int winding : {1,2})
180 {
181 std::vector<V> vertices;
182 for(int layer=0; layer<winding; ++layer)
183 {
184 for(const V& v : {V(-.8,-.8,0),V(.8,-.8,0),V(.8,.8,0),V(-.8,.8,0)})
185 {
186 vertices.push_back(length*v);
187 }
188 }
189 if(reverse)
190 {
191 std::reverse(vertices.begin(),vertices.end());
192 }
193 std::vector<std::pair<V,const OriginalNode* const>> polygon;
194 for(const auto& v : vertices)
195 {
196 polygon.emplace_back(v,nullptr);
197 }
198 const auto records=plane->polygonPatchIntersection(polygon,false);
199 require(records.size()>=4,"Enclosed grain cross-section was discarded");
200 double area=0;
201 for(size_t i=0; i<records.size(); ++i)
202 {
203 const auto& record=records[i];
204 const V a=plane->referencePlane->globalPosition(std::get<0>(record));
205 const V b=plane->referencePlane->globalPosition(std::get<0>(records[(i+1)%records.size()]));
206 area+=0.5*a.cross(b).z();
207 const auto& info=std::get<6>(record);
208 require(info.onPhysicalBoundary && info.atPatchCorner && !info.originalVertex && !info.polygonLocation,
209 "Enclosed boundary has incorrect provenance");
211 "Enclosed boundary falsely attributed to a polygon segment");
212 }
213 require(std::abs(area/(length*length)-(reverse ? -winding : winding))<1.e-8,
214 "Enclosed boundary lost its orientation or winding multiplicity");
215 for(auto& v : polygon)
216 {
217 v.first.x()+=3*length;
218 }
219 require(plane->polygonPatchIntersection(polygon,false).empty(),"Disjoint polygon retained a slipped area");
220 }
221}
222
224{
225 using V=Eigen::Vector3d;
226 const double length=base.mesh.xMax().x()-base.mesh.xMin().x();
227 const auto grain=base.poly.grains.begin()->second;
228 const auto plane=base.dislocationDynamics().periodicGlidePlaneFactory.get(model::GlidePlaneKey<3>(V::Zero(),grain->reciprocalLatticeDirection(V(1,0,3))));
229 std::vector<V> vertices={V(-2,-.8,2.0/3),V(2,-.8,-2.0/3),V(2,.8,-2.0/3),V(-2,.8,2.0/3)};
230 if(reverse)
231 {
232 std::reverse(vertices.begin(),vertices.end());
233 }
234 std::vector<std::pair<V,const OriginalNode* const>> polygon;
235 for(const auto& v : vertices)
236 {
237 polygon.emplace_back(length*v,nullptr);
238 }
239 const auto records=plane->polygonPatchIntersection(polygon,false);
240 require(records.size()>=4,"Enclosed periodic component was discarded");
241 V area=V::Zero();
242 size_t seams=0;
243 for(size_t i=0; i<records.size(); ++i)
244 {
245 const auto& record=records[i];
246 const V a=plane->referencePlane->globalPosition(std::get<0>(record));
247 const V b=plane->referencePlane->globalPosition(std::get<0>(records[(i+1)%records.size()]));
248 area+=0.5*a.cross(b);
249 const auto& info=std::get<6>(record);
250 require(info.onPhysicalBoundary && !info.originalVertex && !info.polygonLocation,
251 "Enclosed periodic boundary has incorrect provenance");
252 seams+=info.crossesPeriodicSeam;
253 const V mapped=a+std::get<1>(record);
254 require((mapped.array()>=base.mesh.xMin().array()-1.e-6).all() &&
255 (mapped.array()<=base.mesh.xMax().array()+1.e-6).all(),"Enclosed boundary mapped outside the RVE");
256 }
257 require(seams>0,"Enclosed boundary did not cross same-grain periodic twins");
258 const V expected=(reverse ? -1.0 : 1.0)*length*length*V(1,0,3);
259 require((area-expected).norm()<1.e-8*expected.norm(),"Wrong enclosed periodic area or orientation");
260}
261
262// A convex hexagonal prism whose X-periodic copies have a zigzag surface.
263// Fan triangulation about each face center ensures matching opposite faces.
264void writeHexagonalPrism(const std::filesystem::path& file)
265{
266 const std::vector<Eigen::Vector2d> hex={{0,.5},{-.5,.25},{-.5,-.25},{0,-.5},{.5,-.25},{.5,.25}};
267 std::vector<Eigen::Vector3d> nodes;
268 for(double z : {-.5,.5})
269 {
270 for(const auto& v : hex)
271 {
272 nodes.emplace_back(v.x(),v.y(),z);
273 }
274 }
275 std::vector<std::vector<int>> faces={{0,1,2,3,4,5},{6,7,8,9,10,11}};
276 for(int i=0; i<6; ++i)
277 {
278 faces.push_back({i,(i+1)%6,(i+1)%6+6,i+6});
279 }
280 const int center=nodes.size();
281 nodes.emplace_back(0,0,0);
282 std::vector<std::array<int,4>> tetrahedra;
283 for(const auto& face : faces)
284 {
285 Eigen::Vector3d mean=Eigen::Vector3d::Zero();
286 for(int i : face)
287 {
288 mean+=nodes[i];
289 }
290 mean/=face.size();
291 const int faceCenter=nodes.size();
292 nodes.push_back(mean);
293 for(size_t i=0; i<face.size(); ++i)
294 {
295 tetrahedra.push_back({center,faceCenter,face[i],face[(i+1)%face.size()]});
296 }
297 }
298 std::ofstream out(file);
299 out<<"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$Nodes\n"<<nodes.size()<<'\n';
300 for(size_t i=0; i<nodes.size(); ++i)
301 {
302 out<<i+1<<' '<<nodes[i].transpose()<<'\n';
303 }
304 out<<"$EndNodes\n$Elements\n"<<tetrahedra.size()<<'\n';
305 for(size_t i=0; i<tetrahedra.size(); ++i)
306 {
307 out<<i+1<<" 4 2 1 1";
308 for(int j : tetrahedra[i])
309 {
310 out<<' '<<j+1;
311 }
312 out<<'\n';
313 }
314 out<<"$EndElements\n";
315}
316
317int main(int argc,char** argv)
318{
319 try
320 {
321 require(argc==2,"Expected repository root");
322 const std::filesystem::path root(argv[1]);
323 const auto folder=std::filesystem::temp_directory_path()/
324 ("periodic-walk-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
325 const auto input=folder/"inputFiles";
326 std::filesystem::create_directories(input);
327 std::filesystem::create_directories(folder/"F");
328 std::filesystem::copy_file(root/"Library/Materials/W.txt",input/"W.txt");
329 std::filesystem::copy_file(root/"Library/Meshes/unitCube24.msh",input/"mesh.msh");
330 int seed=-1;
331 {
332 model::SimplicialMesh<3> mesh((input/"mesh.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),{-1});
333 for(const auto& region:mesh.regions())
334 {
335 for(const auto& face:region.second->faces())
336 {
337 if(face.second->periodicFacePair.first.x()>0.9)
338 {
339 seed=face.first;
340 }
341 }
342 }
343 }
344 require(seed>=0,"No X face");
345 for(int rank : {0,1,3})
346 {
347 const bool periodic=rank>0;
348 {
349 std::ofstream f(input/"DefectiveCrystal.txt");
350 f<<"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nEwaldLengthFactor=0;\n";
351 f<<"periodicImageSize="<<(rank==3 ? "0 0 0" : "0")<<";\n";
352 std::ofstream p(input/"polycrystal.txt");
353 const double length=200/std::sqrt(3.0);
354 p<<std::setprecision(17)<<"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\nF="
355 <<length<<" 0 0 0 "<<length<<" 0 0 0 "<<length<<";\nX0=0 0 0;\nperiodicFaceIDs=";
356 if(periodic)
357 {
358 p<<(rank==3 ? -1 : seed);
359 }
360 p<<";\n";
361 }
362 model::DefectiveCrystalBase<3> base(folder.string());
363 for(bool reverse:{false,true})
364 {
365 exercise(base,{{-.2,-.2},{.2,-.2},{.2,.2},{-.2,.2}},.16,reverse);
366 std::vector<Eigen::Vector2d> balanced{{-.2,-.2},{.2,.2},{-.2,.2},{.2,-.2}};
367 for(size_t start=0;start<balanced.size();++start)
368 {
369 exercise(base,balanced,0.0,reverse);
370 auto clippedBalanced=balanced;
371 for(auto& vertex : clippedBalanced)
372 {
373 vertex*=4.0;
374 }
375 exercise(base,clippedBalanced,0.0,reverse);
376 std::rotate(balanced.begin(),balanced.begin()+1,balanced.end());
377 }
378 // Tangency at a corner must not start a spurious boundary walk.
379 exercise(base,{{.1,.3},{.5,.5},{.1,.4}},.02,reverse);
380 if(rank==3)
381 {
382 exercise(base,{{-.2,.2},{1.2,.2},{1.2,.8},{-.2,.8}},.84,reverse);
383 exercise(base,{{.1,.1},{.9,.9},{-.1,.3}},.16,reverse);
384 continue;
385 }
386 // Two overlapping continuations meet the physical face at
387 // the same point. Keep the next branch in contour order,
388 // rather than jumping to the later overlapping return branch.
389 for(double epsilon : {1.e-3,1.e-4,1.e-5})
390 {
391 std::vector<Eigen::Vector2d> overlap={{0,.5},{.1,.4},{0,.4},
392 {-.1*epsilon,.5+.1*epsilon},{.3,.2},{-.3,.2},{-.3,.5}};
393 // Exact area of the contour clipped by y<=0.5.
394 const double expected=-.14-.005*epsilon/(1+epsilon);
395 for(size_t start=0; start<overlap.size(); ++start)
396 {
397 exercise(base,overlap,expected,reverse);
398 std::rotate(overlap.begin(),overlap.begin()+1,overlap.end());
399 }
400 }
401 // A later boundary-following return passes an earlier inward
402 // branch. Follow the return itself instead of cycling forever.
403 std::vector<Eigen::Vector2d> surfaceReturn={{0,.4},{.1,.3},{.1,.5001},
404 {.08,.5},{.05,.4},{.3,.3},{.3,.5001},{.28,.5},{0,.5}};
405 for(size_t start=0; start<surfaceReturn.size(); ++start)
406 {
407 exercise(base,surfaceReturn,.051,reverse);
408 std::rotate(surfaceReturn.begin(),surfaceReturn.begin()+1,surfaceReturn.end());
409 }
410 // Physical face exit and re-entry, including an outside first vertex.
411 exercise(base,{{-.2,.8},{-.2,.2},{.2,.2},{.2,.8}},.12,reverse);
412 exercise(base,{{-.2,.2},{.2,.2},{.2,.5},{-.2,.5}},.12,reverse);
413 if(periodic)
414 {
415 exercisePeriodicEnclosure(base,reverse);
416 // A self-intersection gives the top lobe the opposite
417 // winding to the overall contour. Its physical-boundary
418 // connection must follow predecessors on the open chain.
419 // Translate it across a seam to exercise twin crossings
420 // in that reverse walk, and vary the starting vertex.
421 for(double offset : {0.0,0.4,0.5,0.6,1.5})
422 {
423 std::vector<Eigen::Vector2d> folded={{-.4,0},{.2,.6},{-.2,.6},{.4,0}};
424 for(auto& point : folded)
425 {
426 point.x()+=offset;
427 }
428 for(size_t start=0; start<folded.size(); ++start)
429 {
430 exercise(base,folded,-.15,reverse);
431 std::rotate(folded.begin(),folded.begin()+1,folded.end());
432 }
433 }
434 // Original polygon crosses same-grain seams without clipping.
435 exercise(base,{{-.2,-.2},{1.2,-.2},{1.2,.2},{-.2,.2}},.56,reverse);
436 // Boundary walk crosses a seam at a physical-face corner.
437 exercise(base,{{-.2,.2},{1.2,.2},{1.2,.8},{-.2,.8}},.42,reverse);
438 // Two surviving components: retain only the starting left one.
439 exercise(base,{{-.4,.1},{-.2,.1},{-.2,.7},{.2,.7},{.2,.1},{.4,.1},{.4,.9},{-.4,.9}},.08,reverse);
440 }
441 else
442 {
443 exerciseEnclosure(base,reverse);
444 // A negative local lobe at a closed grain boundary must
445 // not acquire an extra winding around the entire grain.
446 std::vector<Eigen::Vector2d> folded={{-.4,0},{.2,.6},{-.2,.6},{.4,0}};
447 for(size_t start=0;start<folded.size();++start)
448 {
449 exercise(base,folded,-.15,reverse);
450 std::rotate(folded.begin(),folded.begin()+1,folded.end());
451 }
452 // Same-face ports can connect around the whole boundary,
453 // rather than directly backwards along their shared edge.
454 exercise(base,{{0,.2},{.2,.8},{1,1},{1,-1},{-1,-1},{-1,1},{-.2,.8}},-.97,reverse);
455 // Follow two physical faces through their shared corner.
456 exercise(base,{{0,0},{.8,0},{.8,.8},{0,.8}},.25,reverse);
457 }
458 }
459 }
460 writeHexagonalPrism(input/"mesh.msh");
461 {
462 // Select the vertical X faces directly; the test isolates patch
463 // traversal from lattice-basis discovery and its disambiguation.
464 std::ofstream p(input/"polycrystal.txt");
465 const double length=200/std::sqrt(3.0);
466 p<<std::setprecision(17)<<"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\nF="
467 <<length<<" 0 0 0 "<<length<<" 0 0 0 "<<length<<";\nX0=0 0 0;\nperiodicFaceIDs=;\n";
468 }
469 {
470 model::DefectiveCrystalBase<3> base(folder.string());
471 model::Polyhedron<3,3-1>* left=nullptr;
472 model::Polyhedron<3,3-1>* right=nullptr;
473 for(const auto& region : base.mesh.regions())
474 {
475 for(const auto& face : region.second->faces())
476 {
477 if(face.second->outNormal().x() < -.99)
478 {
479 left=face.second.get();
480 }
481 if(face.second->outNormal().x() > .99)
482 {
483 right=face.second.get();
484 }
485 }
486 }
487 require(left && right,"Missing prism seam");
488 const Eigen::Vector3d shift((base.mesh.xMax()[0]-base.mesh.xMin()[0]),0,0);
489 left->periodicFacePair={shift,right};
490 right->periodicFacePair={-shift,left};
491 for(bool reverse : {false,true})
492 {
493 exercise(base,{{-.2,.1},{1.2,.1},{1.2,.7},{-.2,.7}},.415,reverse);
494 // All vertices lie in a distant copy; straight-line location
495 // from the reference center would leave the zigzag domain.
496 exercise(base,{{1.95,.4},{2.05,.4},{2.05,.45},{1.95,.45}},.005,reverse);
497 }
498 }
499 // Cross-grain periodic faces are walls for a loop belonging to grain 1.
500 std::filesystem::copy_file(root/"Library/Meshes/bicrystal_48.msh",input/"mesh.msh",
501 std::filesystem::copy_options::overwrite_existing);
502 {
503 std::ofstream p(input/"polycrystal.txt");
504 const double length=200/std::sqrt(3.0);
505 p<<std::setprecision(17)<<"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\nC2G2=1 0 0 0 1 0 0 0 1;\nF="
506 <<length<<" 0 0 0 "<<length<<" 0 0 0 "<<length<<";\nX0=0 0 0;\nperiodicFaceIDs=-1;\n";
507 }
508 {
509 std::ofstream f(input/"DefectiveCrystal.txt");
510 f<<"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0 0 0;\nEwaldLengthFactor=0;\n";
511 }
512 {
513 model::DefectiveCrystalBase<3> base(folder.string());
514 for(bool reverse : {false,true})
515 {
516 exercise(base,{{-.4,-.1},{-.2,-.1},{-.2,.1},{-.4,.1}},.04,reverse);
517 exercise(base,{{-.4,-.1},{-.6,-.1},{-.6,.1},{-.4,.1}},-.02,reverse);
518 }
519 }
520 std::filesystem::remove_all(folder);
521 std::cout<<"PASS: periodic polygon boundary walking\n";
522 return 0;
523 }
524 catch(const std::exception& error)
525 {
526 std::cerr<<error.what()<<'\n';
527 return 1;
528 }
529}
Eigen::Vector3d V
the magnitude of the R vector q
id z()
final point of the source segment t
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
void periodic(const Trial &field, double relative=1.e-10)
void exerciseEnclosure(model::DefectiveCrystalBase< 3 > &base, bool reverse)
void require(bool condition, const char *message)
void writeHexagonalPrism(const std::filesystem::path &file)
void exercise(model::DefectiveCrystalBase< 3 > &base, const std::vector< Eigen::Vector2d > &vertices, double expectedArea, bool reverse=false)
void exercisePeriodicEnclosure(model::DefectiveCrystalBase< 3 > &base, bool reverse)
right
int j
void out
struct otri neighbor
void argv
left
void b int argc
REAL epsilon
Definition triangle.cpp:647
vertex p[3]
int k
void info()
int i
REAL * vertex
Definition triangle.cpp:542
REAL area
vertex first
void vertices
REAL v[4]