Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testPeriodicGlissileJunction.cpp
Go to the documentation of this file.
5#include <filesystem>
6#include <fstream>
7#include <iomanip>
8#include <chrono>
9#include <iostream>
10
11namespace model
12{
14 {
15 static void glissile(DislocationNetwork<3>& network,double minimumLength)
16 {
17 network.junctionsMaker.glissileJunctions(minimumLength);
18 }
19 };
20}
21
22void require(bool value,const char* message)
23{
24 if(!value)
25 {
26 throw std::runtime_error(message);
27 }
28}
29
30int main(int argc,char** argv)
31{
32 try
33 {
34 require(argc>=2,"Expected repository root");
35#ifdef NDEBUG
36 require(argc<3 || std::string(argv[2])!="--require-assertions","Assertions are disabled in this test build");
37#else
38 std::cout<<"Assertions enabled in periodic glissile-junction regression\n";
39#endif
40 const std::filesystem::path root(argv[1]);
41 const auto folder=std::filesystem::temp_directory_path()/
42 ("periodic-glissile-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
43 const auto input=folder/"inputFiles";
44 std::filesystem::create_directories(input);
45 std::filesystem::create_directories(folder/"F");
46 std::filesystem::create_directories(folder/"config");
47 std::filesystem::copy_file(root/"Library/Materials/W.txt",input/"W.txt");
48 std::filesystem::copy_file(root/"Library/DislocationDynamics/DD.txt",input/"DD.txt");
49 std::filesystem::copy_file(root/"Library/Meshes/unitCube24.msh",input/"mesh.msh");
51 using V=Eigen::Vector3d;
52 model::SimplicialMesh<3> mesh((input/"mesh.msh").string(),Eigen::Matrix3d::Identity(),V::Zero(),{-1});
53 std::array<int,3> seeds{-1,-1,-1};
54 for(const auto& region : mesh.regions())
55 {
56 for(const auto& face : region.second->faces())
57 {
58 const auto& pair=face.second->periodicFacePair;
59 if(pair.second)
60 {
61 for(int axis=0;axis<3;++axis)
62 {
63 if(pair.first[axis]>0.9 && pair.first.normalized().cross(V::Unit(axis)).norm()<1.e-8)
64 {
65 seeds[axis]=face.first;
66 }
67 }
68 }
69 }
70 }
71 require(seeds[0]>=0 && seeds[1]>=0,"Missing periodic face seeds");
72 model::StaticID<model::Polyhedron<3,3-1>>::force_count(0);
73 const double length=200.0/std::sqrt(3.0);
74 for(int scenario=0;scenario<4;++scenario)
75 {
76 const bool corner=scenario==1 || scenario==2;
77 const bool twoEnds=scenario!=1;
78 const int rank=corner? 3 : (scenario==3? 2 : 1);
79 {
80 std::ofstream f(input/"DefectiveCrystal.txt");
81 f<<"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=";
82 for(int k=0;k<rank;++k)
83 {
84 f<<"0 ";
85 }
86 f<<";\nEwaldLengthFactor=0;\n";
87 std::ofstream p(input/"polycrystal.txt");
88 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=";
89 for(int k=0;k<rank;++k)
90 {
91 p<<seeds[k]<<' ';
92 }
93 p<<";\n";
94 }
95 model::DefectiveCrystalBase<3> base(folder.string());
97 Network network(container);
98 const auto grain=base.poly.grain(1);
99 const V direction=corner? V(1,1,1) : (scenario==3? V(1,1,0) : V(1,0,0));
100 const V parentNormal=corner? V(1,-1,0).normalized().eval() : V::UnitZ().eval();
101 const V side=parentNormal.cross(direction).normalized();
102 const auto planeAt=[&](const V& point)
103 {
104 return base.dislocationDynamics().glidePlaneFactory.getFromKey(model::GlidePlaneKey<3>(point,grain->reciprocalLatticeDirection(parentNormal)));
105 };
106 const auto plane=planeAt(V::Zero());
107 V burgers=V::Zero();
108 for(const auto& [id,slip] : grain->slipSystems())
109 {
110 if(std::abs(slip->unitNormal.dot(direction))<1.e-8 &&
111 std::abs(slip->s.cartesian().dot(parentNormal))>1.e-8)
112 {
113 burgers=slip->s.cartesian();
114 break;
115 }
116 }
117 require(burgers.norm()>0.0,"No glissile Burgers vector for test direction");
118 const auto insert=[&](const V& b,const auto& gp,const auto& netNodes)
119 {
120 const auto loop=network.loops().create(b,gp);
121 std::vector<std::shared_ptr<Network::LoopNodeType>> nodes;
122 for(const auto& node : netNodes)
123 {
124 nodes.push_back(network.loopNodes().create(loop,node,node->get_P(),
125 loop->periodicGlidePlane->getPatch(V::Zero()),std::make_pair(nullptr,nullptr)));
126 }
127 network.insertLoop(loop,nodes);
128 return loop;
129 };
130 const auto a=network.networkNodes().create(V((twoEnds? -0.3 : 0.0)*length*direction));
131 const auto b=network.networkNodes().create(V(0.3*length*direction));
132 const auto c=network.networkNodes().create(V(0.12*length*side));
133 const auto d=network.networkNodes().create(V(-0.12*length*side));
134 const auto first=insert(2.0*burgers,plane,std::vector{a,b,c});
135 const auto second=insert(-burgers,plane,std::vector{a,b,d});
136 network.updateBoundaryNodes();
137 network.updateGeometry();
138 network.trialNodeMotion=true;
139 if(twoEnds)
140 {
141 a->trySet_P(-0.9*length*direction);
142 }
143 b->trySet_P(0.9*length*direction);
144 network.updateBoundaryNodes();
145 network.trialNodeMotion=false;
146 network.updateGeometry();
147 const double threshold=twoEnds? 0.8*length*direction.norm() : 0.0;
148 std::shared_ptr<Network::NetworkLinkType> selected;
149 std::vector<std::shared_ptr<Network::NetworkLinkType>> shared;
150 for(const auto& entry : network.networkLinks())
151 {
152 const auto link=entry.second.lock();
153 if(link->loopLinks().size()==2)
154 {
155 shared.push_back(link);
156 if(link->chordLength()>threshold && !selected)
157 {
158 selected=link;
159 }
160 }
161 }
162 require(selected && shared.size()>=2,"Missing shared periodic continuation");
163 if(twoEnds)
164 {
165 for(const auto* occurrence : selected->loopLinks())
166 {
167 require(occurrence->source->isPeriodicSubdivision() && occurrence->sink->isPeriodicSubdivision(),
168 "Test did not isolate a segment with two periodic endpoints");
169 }
170 }
171 // An unrelated loop visits a periodic endpoint. It must not be
172 // selected as a continuation, modified, or detached from that node.
173 auto endpoint=selected->source;
174 if(!(*selected->loopLinks().begin())->source->isPeriodicSubdivision())
175 {
176 endpoint=selected->sink;
177 }
178 const auto unrelated=network.loops().create(burgers,plane);
179 std::map<size_t,std::shared_ptr<Network::NetworkNodeType>> copies;
180 std::vector<std::shared_ptr<Network::LoopNodeType>> unrelatedNodes;
181 for(const auto* link : first->linkSequence())
182 {
183 const auto& node=link->source;
184 auto& copy=copies[node->networkNode->sID];
185 if(!copy)
186 {
187 copy=node->networkNode==endpoint? endpoint : network.networkNodes().clone(node->networkNode->sID);
188 }
189 unrelatedNodes.push_back(network.loopNodes().create(unrelated,copy,node->get_P(),
190 node->periodicPlanePatch(),node->periodicPlaneEdge));
191 }
192 network.insertLoop(unrelated,unrelatedNodes);
193 unrelatedNodes.clear();
194 copies.clear();
195 network.updateGeometry();
196 const V unrelatedArea=unrelated->currentAreaVector();
197 const auto memberships=endpoint->loopNodes();
198 const Eigen::Matrix3d stressBefore=network.stress(V(7,11,19),nullptr,nullptr,nullptr);
200 network.updateGeometry();
201 require(network.loops().size()==4,"Expected exactly one glissile seed for the whole continuation");
202 for(auto* occurrence : memberships)
203 {
204 require(endpoint->loopNodes().count(occurrence)==1,"Shared periodic endpoint was detached");
205 }
206 require(endpoint->loopNodes().size()>memberships.size(),"Seed did not share the periodic endpoint");
207 require((unrelated->currentAreaVector()-unrelatedArea).norm()<1.e-8,"Unrelated loop at periodic endpoint changed");
208 require((network.stress(V(7,11,19),nullptr,nullptr,nullptr)-stressBefore).norm()<1.e-9*std::max(1.e-12,stressBefore.norm()),
209 "Zero-area junction seed changed the stress field");
210 for(const auto& link : shared)
211 {
212 require(link->loopLinks().size()==3 && link->hasZeroBurgers(),"Seed lost junction sharing or Burgers cancellation");
213 }
214 std::shared_ptr<Network::LoopType> seed;
215 for(const auto& entry : network.loops())
216 {
217 const auto loop=entry.second.lock();
218 if(loop!=first && loop!=second && loop!=unrelated)
219 {
220 seed=loop;
221 }
222 }
223 require(seed && seed->isLoop() && seed->currentAreaVector().norm()<1.e-8,"Invalid glissile seed");
224 size_t corners=0;
225 for(const auto* link : seed->linkSequence())
226 {
227 const auto node=link->source;
228 corners+=bool(node->periodicPlaneEdge.second);
229 require((node->get_P()+node->periodicPlanePatch()->shift-node->networkNode->get_P()).norm()<1.e-7,"Incorrect seed patch translation");
230 for(const auto& edge : {node->periodicPlaneEdge.first,node->periodicPlaneEdge.second})
231 {
232 if(edge)
233 {
234 require(edge->patch==node->periodicPlanePatch().get() && edge->meshIntersection->contains(node->networkNode->get_P()),
235 "Invalid corner edge metadata");
236 }
237 }
238 }
239 require(!corner || corners>0,"Corner crossing lost its second edge");
240 // Move the free interior seed node and let the ordinary boundary
241 // walk rebuild both sides. This exercises the stored corner metadata.
242 for(const auto& entry : network.networkNodes())
243 {
244 entry.second.lock()->set_V(V::Zero(),false);
245 }
246 for(const auto* link : seed->linkSequence())
247 {
248 const auto node=link->source;
249 if(!node->isPeriodicSubdivision() && node->networkNode->loopNodes().size()==1)
250 {
251 node->networkNode->set_V(seed->glidePlane->unitNormal.cross(direction).normalized(),false);
252 }
253 }
254 double sharedLengthBefore=0.0;
255 for(const auto& link : shared)
256 {
257 sharedLengthBefore+=link->chordLength();
258 }
259 shared.clear();
260 selected.reset();
261 network.moveNodes(0.1);
262 require(seed->currentAreaVector().norm()>1.e-8,"Glissile seed did not expand");
263 double sharedLengthAfter=0.0;
264 for(const auto& entry : network.networkLinks())
265 {
266 const auto segment=entry.second.lock();
267 bool hasFirst=false,hasSecond=false,hasSeed=false;
268 for(const auto* occurrence : segment->loopLinks())
269 {
270 hasFirst=hasFirst || occurrence->loop==first;
271 hasSecond=hasSecond || occurrence->loop==second;
272 hasSeed=hasSeed || occurrence->loop==seed;
273 }
274 if(hasFirst && hasSecond && hasSeed)
275 {
276 require(segment->hasZeroBurgers(),"Motion lost Burgers cancellation on shared continuation");
277 sharedLengthAfter+=segment->chordLength();
278 }
279 }
280 require(std::abs(sharedLengthAfter-sharedLengthBefore)<1.e-7,"Motion lost sharing along the periodic continuation");
281 for(const auto& entry : network.loopNodes())
282 {
283 const auto node=entry.second.lock();
284 require((node->get_P()+node->periodicPlanePatch()->shift-node->networkNode->get_P()).norm()<1.e-7,
285 "Motion lost periodic mapping");
286 }
287 require((unrelated->currentAreaVector()-unrelatedArea).norm()<1.e-7,"Seed motion altered unrelated loop");
288 require(first->isLoop() && second->isLoop() && unrelated->isLoop() && seed->isLoop(),"Motion broke loop closure");
289 std::cout<<"PASS periodic glissile scenario "<<scenario<<'\n';
290 }
291 std::filesystem::remove_all(folder);
292 }
293 catch(const std::exception& error)
294 {
295 std::cerr<<error.what()<<'\n';
296 return 1;
297 }
298}
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 moveNodes(const double &dt_in)
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
LatticePlaneKey< dim > GlidePlaneKey
int regions
Definition triangle.cpp:696
static void glissile(DislocationNetwork< 3 > &network, double minimumLength)
SharedPtrType create(const Args &...args)
SharedPtrType clone(const KeyType &key, const Args &... args)
model::DislocationNetwork< 3 > Network
void require(bool value, const char *message)
int corner[3]
void argv
int corners
int axis
void b int argc
vertex p[3]
int k
vertex first