Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testThermalCrossSlipTopology.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>
15namespace model
16{
18{
19 template<class N> static void queue(N& network,const std::deque<std::shared_ptr<typename N::LoopNodeType>>& nodes,int slip)
20 {
21 network.crossSlipMaker.csNodes.clear();
22 network.crossSlipMaker.csNodes.emplace_back(nodes,slip);
23 network.crossSlipMaker.csNodes.back().thermal=model::ThermalCrossSlipEvent{slip,300.0,0.5};
24 }
25};
26}
27
28
29void require(bool condition, const char* message)
30{
31 if(!condition)
32 {
33 throw std::runtime_error(message);
34 }
35}
36
37int main(int argc, char** argv)
38{
39 try
40 {
41 require(argc == 2, "Expected repository root");
42 const std::filesystem::path root(argv[1]);
43 const auto folder = std::filesystem::temp_directory_path() /
44 ("thermal-cross-slip-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
45 const auto input = folder / "inputFiles";
46 std::filesystem::create_directories(input);
47 std::filesystem::create_directories(folder / "F");
48 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
49
50 std::filesystem::copy_file(root / "Library/Meshes/bicrystal_48.msh", input / "mesh.msh");
51 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
52 {
53 std::ifstream inputDD(input/"DD.txt");
54 std::string contents((std::istreambuf_iterator<char>(inputDD)),std::istreambuf_iterator<char>());
55 const auto pos=contents.find("crossSlipModel=0;");
56 require(pos!=std::string::npos,"Missing cross-slip setting");
57 contents.replace(pos,17,"crossSlipModel=1;");
58 std::ofstream outputDD(input/"DD.txt");
59 outputDD<<contents;
60 }
61 std::filesystem::create_directories(folder / "config");
62 model::SimplicialMesh<3> mesh((input/"mesh.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),{-1});
63 std::array<int,3> seeds{-1,-1,-1};
64 for(const auto& region : mesh.regions())
65 {
66 for(const auto& face : region.second->faces())
67 {
68 const auto& pair=face.second->periodicFacePair;
69 if(pair.second)
70 {
71 for(int axis=0;axis<3;++axis)
72 {
73 if(pair.first.normalized().cross(Eigen::Vector3d::Unit(axis)).norm()<1.e-8)
74 {
75 seeds[axis]=face.first;
76 }
77 }
78 }
79 }
80 }
81 require(seeds[0]>=0 && seeds[1]>=0,"Missing periodic seeds");
82 model::StaticID<model::Polyhedron<3,3-1>>::force_count(0);
83 const double length=3671.9477120460206;
84 for(int refinement : {1,2,12})
85 {
86 for(int scenario=0;scenario<6;++scenario)
87 {
88 const bool full=scenario==1;
89 const bool periodic=scenario!=2;
90 {
91 std::ofstream f(input/"DefectiveCrystal.txt");
92 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";
93 std::ofstream p(input/"polycrystal.txt");
94 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=";
95 if(periodic)
96 {
97 p<<(full ? -1 : seeds[1]);
98 }
99 p<<";\n";
100 }
101 model::DefectiveCrystalBase<3> base(folder.string());
102 model::MicrostructureContainer<3> container(base);
104 using V=Eigen::Vector3d;
105 Network network(container);
106 const auto grain=base.poly.grain(1);
107 const auto slip=grain->slipSystems().begin()->second;
108 int other=-1;
109 for(size_t k=0;k<grain->slipSystems().size();++k)
110 {
111 const auto candidate=grain->slipSystems().at(k);
112 if((candidate->s-slip->s).squaredNorm()==0 && candidate->unitNormal.cross(slip->unitNormal).norm()>0.1)
113 {
114 other=k;
115 break;
116 }
117 }
118 require(other>=0,"No cross-slip system");
119 const auto css=grain->slipSystems().at(other);
120 const V b=slip->s.cartesian();
121 const V center=scenario==4 ? V::Zero().eval() : V(length*V(-0.5,periodic ? 0.49 : 0.0,0));
122 const auto plane=base.dislocationDynamics().glidePlaneFactory.getFromKey(model::GlidePlaneKey<3>(center,slip->n));
123 const long spacing=std::max(1L,static_cast<long>(20.0/css->n.planeSpacing()));
124 const long height=std::lround(double(css->n.closestPlaneIndexOfPoint(center))/spacing)*spacing;
125 model::PlanePlaneIntersection<3> intersection(plane->P,plane->unitNormal,height*css->n.planeSpacing()*css->unitNormal,css->unitNormal);
126 V origin=intersection.P+(center-intersection.P).dot(intersection.d)*intersection.d;
127 V along=intersection.d;
128 if(scenario==4 && along.x()<0.0)
129 {
130 along=-along;
131 }
132 V side=plane->unitNormal.cross(along).normalized();
133 if(scenario==4)
134 {
135 origin=-0.15*length*along;
136 if(side.x()>0.0)
137 {
138 side=-side;
139 }
140 }
141 if(scenario==5)
142 {
143 origin+=(0.5*length+2.0-origin.y())/along.y()*along;
144 }
145 const V a=origin-0.15*length*along;
146 const V middle=origin;
147 const V end=origin+0.15*length*along;
148 std::vector<V> polygon{a,middle,end,end+100.0*side,a+100.0*side};
149 if(scenario==5)
150 {
151 polygon[1]-=4.0/side.y()*side;
152 }
153 if(scenario==3)
154 {
155 polygon[0]+=0.2*side;
156 polygon[1]+=0.2*side;
157 polygon[2]+=2.0*css->n.planeSpacing()/css->unitNormal.dot(side)*side;
158 }
159 if(scenario!=3 && scenario!=5)
160 {
161 polygon.clear();
162 for(int k=0;k<=refinement;++k)
163 {
164 polygon.push_back(a+(end-a)*(double(k)/refinement));
165 }
166 polygon.push_back(end+100.0*side);
167 polygon.push_back(a+100.0*side);
168 }
169 model::MicrostructureGenerator generator(base);
170 require(generator.insertJunctionLoop(polygon,base.dislocationDynamics().periodicGlidePlaneFactory.get(plane->key),b,plane->unitNormal,plane->P,1,model::DislocationLoopIO<3>::GLISSILELOOP),"Parent insertion failed");
171 network.initializeConfiguration(generator.config(),std::ofstream(),std::ofstream());
172
173 const auto parent=network.loops().begin()->second.lock();
174 std::deque<std::shared_ptr<Network::LoopNodeType>> branch;
175 for(const auto& point : std::vector<V>{polygon.front(),polygon[polygon.size()-3]})
176 {
177 for(const auto* link : parent->linkSequence())
178 {
179 if((link->source->get_P()-point).norm()<1.e-6 && !link->source->isPeriodicSubdivision())
180 {
181 branch.push_back(link->source);
182 break;
183 }
184 }
185 }
186 require(branch.size()==2,"Missing branch anchors");
187 if(scenario==3 || scenario==5)
188 {
189 for(const auto* link : parent->linkSequence())
190 {
191 if((link->source->get_P()-polygon[1]).norm()<1.e-6 && !link->source->isPeriodicSubdivision())
192 {
193 branch.insert(branch.begin()+1,link->source);
194 break;
195 }
196 }
197 }
198 // A second loop shares the final endpoint. Its plane either permits
199 // alignment, or locks the displaced endpoint against that motion.
200 const auto endpoint=branch.back()->networkNode;
201 const V normal=scenario==3 ? css->unitNormal : plane->unitNormal;
202 const auto extraPlane=base.dislocationDynamics().glidePlaneFactory.getFromKey(model::GlidePlaneKey<3>(endpoint->get_P(),grain->reciprocalLatticeDirection(normal)));
203 const auto extra=network.loops().create(b,extraPlane);
204 V u=normal.cross(along).normalized();
205 if(scenario==4 && u.x()>0.0)
206 {
207 u=-u;
208 }
209 const auto e1=network.networkNodes().create(V(endpoint->get_P()-30.0*along));
210 const auto e2=network.networkNodes().create(V(endpoint->get_P()-30.0*along+20.0*u));
211 std::vector<std::shared_ptr<Network::LoopNodeType>> ring;
212 for(const auto& node : std::vector{endpoint,e1,e2})
213 {
214 ring.push_back(network.loopNodes().create(extra,node,node->get_P(),extra->periodicGlidePlane->getPatch(V::Zero()),std::make_pair(nullptr,nullptr)));
215 }
216 network.insertLoop(extra,ring);
217 network.updateGeometry();
218 const auto snapshot=[&]()
219 {
220 std::ostringstream out;
221 out<<std::setprecision(17);
222 for(const auto& entry : network.networkNodes())
223 {
224 const auto node=entry.second.lock();
225 out<<node->sID<<' '<<node->get_P().transpose()<<' '<<node->get_V().transpose()<<'\n';
226 }
227 for(const auto& entry : network.loops())
228 {
229 out<<entry.first<<'\n';
230 for(const auto* link : entry.second.lock()->linkSequence())
231 {
232 out<<link->source->sID<<' '<<link->source->networkNode->sID<<' '<<link->source->get_P().transpose()<<' '<<link->sink->sID<<'\n';
233 }
234 }
235 return out.str();
236 };
237 const auto before=snapshot();
238 const auto endpointID=endpoint->sID;
239 const auto middleNode=branch[1]->networkNode;
240 const V oldMiddle=middleNode->get_P();
241 model::CrossSlipTestAccess::queue(network,branch,other);
242 network.crossSlipMaker.execute();
243 if(scenario==3)
244 {
245 require(snapshot()==before,"Rejected alignment changed network");
246 continue;
247 }
248 if(scenario==5)
249 {
250 require((middleNode->get_P()-oldMiddle).norm()>0.5*length,"Alignment did not cross the periodic face");
251 require(network.networkNodes().get(middleNode->sID)==middleNode,"Alignment lost network identity at seam");
252 }
253 branch.clear();
254 ring.clear();
255 require(network.loops().size()==3,"Cross-slip seed was discarded");
256 require(network.networkNodes().get(endpointID)==endpoint && endpoint->loopNodes().size()==2,"Shared endpoint was detached");
257 std::shared_ptr<Network::LoopType> seed;
258 for(const auto& entry : network.loops())
259 {
260 const auto loop=entry.second.lock();
261 if(loop!=parent && loop!=extra)
262 {
263 seed=loop;
264 }
265 }
266 require(seed && seed->loopLinks().size()>=4 && seed->slipSystem(),"Invalid zero-area seed");
267 double perimeter=0.0;
268 for(const auto* link : seed->linkSequence())
269 {
270 perimeter+=(link->sink->get_P()-link->source->get_P()).norm();
271 }
272 require(std::abs(perimeter-600.0)<1.e-6,"Nucleus length depends on segmentation");
273 require(seed->currentAreaVector().norm()<1.e-6,"Seed added slipped area before motion");
274 size_t seams=0,cancelled=0;
275 for(const auto* link : seed->linkSequence())
276 {
277 seams+=link->source->isPeriodicSubdivision();
278 if(link->networkLink() && link->networkLink()->loopLinks().size()==2)
279 {
280 require(link->networkLink()->hasZeroBurgers(),"Parent branch Burgers vector not cancelled");
281 ++cancelled;
282 }
283 require((link->source->get_P()+link->source->periodicPlanePatch()->shift-link->source->networkNode->get_P()).norm()<1.e-6,"Incorrect periodic mapping");
284 }
285 require(cancelled>=2,"No cancellation on original branch");
286 require((seams>0)==(periodic && scenario!=4),"Periodic crossing was not preserved");
287 const auto expected=network.glideSolver->getNodeVelocities();
288 size_t k=0;
289 for(const auto& entry : network.networkNodes())
290 {
291 const auto node=entry.second.lock();
292 const V stored=node->get_V();
293 node->set_V(expected.segment<3>(3*k),false);
294 require((stored-node->get_V()).norm()<1.e-7,"Stale nodal velocity after cross-slip");
295 ++k;
296 }
297 // Drive only the free side of the seed on its new plane. This
298 // checks the ordinary motion/boundary walk after seed creation.
299 for(const auto& entry : network.networkNodes())
300 {
301 entry.second.lock()->set_V(V::Zero(),false);
302 }
303 for(const auto* link : seed->linkSequence())
304 {
305 const auto node=link->source->networkNode;
306 if(node->loopNodes().size()==1)
307 {
308 node->set_V(css->unitNormal.cross(along).normalized(),false);
309 }
310 }
311 network.moveNodes(0.1);
312 require(seed->currentAreaVector().norm()>1.e-6,"Seed failed to expand under motion");
313 require(endpoint->loopNodes().size()==2,"Motion lost shared endpoint");
314 }
315 }
316 std::filesystem::remove_all(folder);
317 std::cout<<"PASS: thermal nucleus length, periodic mapping, shared endpoint, fresh velocities and atomic rejection\n";
318 return 0;
319 }
320 catch(const std::exception& error)
321 {
322 std::cerr<<error.what()<<'\n';
323 return 1;
324 }
325}
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 LoopContainerType & loops() const
Definition LoopNetwork.h:98
const NetworkNodeContainerType & networkNodes() const
const LoopNodeContainerType & loopNodes() const
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 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