Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testBoundaryNodeUpdate.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
20int main(int argc, char** argv)
21{
22 try
23 {
24 require(argc == 2, "Expected repository root");
25 const std::filesystem::path root(argv[1]);
26 const auto folder = std::filesystem::temp_directory_path() /
27 ("periodic-fields-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
28 const auto input = folder / "inputFiles";
29 std::filesystem::create_directories(input);
30 std::filesystem::create_directories(folder / "F");
31 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
32 const auto meshFile = root / "Library/Meshes/unitCube24.msh";
33 std::filesystem::copy_file(meshFile, input / "mesh.msh");
34 model::SimplicialMesh<3> mesh(meshFile.string(), Eigen::Matrix3d::Identity(), Eigen::Vector3d::Zero(), {-1});
35 std::vector<int> seeds;
36 for(int axis = 0; axis < 3; ++axis)
37 {
38 for(const auto& region : mesh.regions())
39 {
40 for(const auto& face : region.second->faces())
41 {
42 const auto& pair = face.second->periodicFacePair;
43 if(pair.second && pair.first[axis] > 0.9 &&
44 pair.first.normalized().cross(Eigen::Vector3d::Unit(axis)).norm() < 1.e-8)
45 {
46 seeds.push_back(face.first);
47 }
48 }
49 }
50 }
51 require(seeds.size() == 3, "Expected three cube periodic directions");
52
53 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
54 const double length=200.0/std::sqrt(3.0);
55 for(int activeAxis : {-1,0,1})
56 {
57 const bool periodic=activeAxis==0;
58 {
59 std::ofstream f(input / "DefectiveCrystal.txt");
60 f << "physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\n"
61 << "outputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0;\nEwaldLengthFactor=0;\n";
62 std::ofstream p(input / "polycrystal.txt");
63 p << std::setprecision(17) << "materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\n"
64 << "C2G1=1 0 0 0 1 0 0 0 1;\nF=" << length << " 0 0 0 " << length << " 0 0 0 " << length
65 << ";\nX0=0 0 0;\nperiodicFaceIDs=";
66 if(activeAxis>=0)
67 {
68 p << seeds[activeAxis];
69 }
70 p << ";\n";
71 }
72 model::DefectiveCrystalBase<3> base(folder.string());
75 using V=Eigen::Vector3d;
76 Network network(container);
77 const auto grain=base.poly.grain(1);
78 const auto plane=base.dislocationDynamics().glidePlaneFactory.getFromKey(model::GlidePlaneKey<3>(V::Zero(),grain->reciprocalLatticeDirection(V::UnitZ())));
79 const V burgers=2.0/std::sqrt(3.0)*V::UnitX();
80 const auto first=network.loops().create(burgers,plane);
81 const auto second=network.loops().create(2.0*burgers,plane);
82 // Two oppositely oriented loops share AB. B is beyond the face in
83 // loop space, but its network representative is inside the RVE.
84 const V a=length*V(0.2,0,0);
85 const V b=length*V(0.7,0,0);
86 const auto na=network.networkNodes().create(a);
87 const auto nb=network.networkNodes().create(length*V(periodic ? -0.3 : 0.49,0,0));
88 const auto makeNode=[&](const auto& loop,const auto& net,const V& position)
89 {
90 return network.loopNodes().create(loop,net,position,loop->periodicGlidePlane->getPatch(V::Zero()),
91 std::make_pair(std::shared_ptr<model::PeriodicPlaneEdge<3>>(),std::shared_ptr<model::PeriodicPlaneEdge<3>>()));
92 };
93 auto a1=makeNode(first,na,a);
94 auto b1=makeNode(first,nb,b);
95 auto a2=makeNode(second,na,a);
96 auto b2=makeNode(second,nb,b);
97 const auto c1=makeNode(first,network.networkNodes().create(length*V(0.1,0.2,0)),length*V(0.1,0.2,0));
98 const auto c2=makeNode(second,network.networkNodes().create(length*V(0.1,-0.2,0)),length*V(0.1,-0.2,0));
99 network.insertLoop(first,std::vector<std::shared_ptr<Network::LoopNodeType>>{a1,b1,c1});
100 network.insertLoop(second,std::vector<std::shared_ptr<Network::LoopNodeType>>{b2,a2,c2});
101 // An unrelated coincident polygon must not acquire junction sharing.
102 const auto unrelated=network.loops().create(burgers,plane);
103 network.insertLoop(unrelated,std::vector<std::shared_ptr<Network::LoopNodeType>>{
104 makeNode(unrelated,network.networkNodes().create(a),a),
105 makeNode(unrelated,network.networkNodes().create(nb->get_P()),b),
106 makeNode(unrelated,network.networkNodes().create(c1->get_P()),c1->get_P())});
107 // A trimmed network node may still belong to an unaffected loop.
108 const auto survivor=network.loops().create(burgers,plane);
109 network.insertLoop(survivor,std::vector<std::shared_ptr<Network::LoopNodeType>>{
110 makeNode(survivor,nb,nb->get_P()),
111 makeNode(survivor,network.networkNodes().create(length*V(0,0.1,0)),length*V(0,0.1,0)),
112 makeNode(survivor,network.networkNodes().create(length*V(0,0.3,0)),length*V(0,0.3,0))});
113 // A real mesh-search failure must not change a network position
114 // before throwing (set_P is also used by the commit transaction).
115 const V originalPosition=na->get_P();
116 const auto originalSimplex=na->includingSimplex();
117 bool rejected=false;
118 try
119 {
120 na->set_P(length*V(10,10,10));
121 }
122 catch(const std::runtime_error&)
123 {
124 rejected=true;
125 }
126 require(rejected && (na->get_P()-originalPosition).norm()==0 && na->includingSimplex()==originalSimplex,
127 "Failed position validation changed a committed network node");
128 std::map<size_t,std::pair<const Network::LoopNodeType*,V>> oldLoopNodes;
129 std::map<size_t,std::pair<const Network::NetworkNodeType*,V>> oldNetworkNodes;
130 std::map<Network::LoopLinkType::KeyType,const Network::LoopLinkType*> oldLinks;
131 std::map<const Network::NetworkLinkType*,V> oldBurgers;
132 std::map<size_t,V> oldDisplacements;
133 for(const auto& entry : network.loopNodes())
134 {
135 const auto node=entry.second.lock();
136 oldLoopNodes.emplace(entry.first,std::make_pair(node.get(),node->get_P()));
137 }
138 for(const auto& entry : network.networkNodes())
139 {
140 const auto node=entry.second.lock();
141 oldNetworkNodes.emplace(entry.first,std::make_pair(node.get(),node->get_P()));
142 oldDisplacements.emplace(entry.first,node->get_deltaP());
143 }
144 for(const auto& entry : network.loopLinks())
145 {
146 oldLinks.emplace(entry.first,&entry.second);
147 }
148 for(const auto& entry : network.networkLinks())
149 {
150 const auto link=entry.second.lock();
151 oldBurgers.emplace(link.get(),link->burgers());
152 }
153 size_t failAt=1;
154 for(;failAt<500;++failAt)
155 {
156 size_t operations=0;
157 try
158 {
159 network.updateBoundaryNodes([&]()
160 {
161 if(++operations==failAt)
162 {
163 throw std::runtime_error("Injected boundary commit failure");
164 }
165 });
166 break;
167 }
168 catch(const std::runtime_error& error)
169 {
170 require(std::string(error.what())=="Injected boundary commit failure","Unexpected update failure");
171 }
172 require(network.loopNodes().size()==oldLoopNodes.size(),"Failed update leaked loop nodes");
173 require(network.networkNodes().size()==oldNetworkNodes.size(),"Failed update leaked network nodes");
174 require(network.loopLinks().size()==oldLinks.size(),"Failed update changed connectivity");
175 require(network.networkLinks().size()==oldBurgers.size(),"Failed update changed junction count");
176 for(const auto& entry : oldLoopNodes)
177 {
178 const auto node=network.loopNodes().get(entry.first);
179 require(node.get()==entry.second.first && (node->get_P()-entry.second.second).norm()==0,
180 "Failed update changed loop-node identity or position");
181 require(!node->periodicContourVertex,"Failed update changed contour flags");
182 }
183 for(const auto& entry : oldNetworkNodes)
184 {
185 const auto node=network.networkNodes().get(entry.first);
186 require(node.get()==entry.second.first && (node->get_P()-entry.second.second).norm()==0,
187 "Failed update changed network-node identity or position");
188 require((node->get_deltaP()-oldDisplacements.at(entry.first)).norm()==0,
189 "Failed update changed the stored motion increment");
190 }
191 for(const auto& entry : oldLinks)
192 {
193 require(&network.loopLinks().at(entry.first)==entry.second,"Rollback replaced an original link");
194 require(entry.second->source->next.second==entry.second && entry.second->sink->prev.second==entry.second,
195 "Rollback broke directed incidence");
196 }
197 for(const auto& entry : oldBurgers)
198 {
199 require((entry.first->burgers()-entry.second).norm()==0,"Rollback changed junction Burgers vector");
200 }
201 }
202 require(failAt>5 && failAt<500,"Commit fault injection did not complete");
203
204 require(first->isLoop() && second->isLoop(),"Clipped topology is not closed");
205 size_t shared=0;
206 size_t junctionSegments=0;
207 std::set<size_t> ids;
208 for(const auto* link : first->linkSequence())
209 {
210 ids.insert(link->source->sID);
211 const auto n=link->source->networkNode;
212 if(std::abs(n->get_P().x()-0.5*length)<1.e-6 && std::abs(n->get_P().y())<1.e-6)
213 {
214 require(n->loopNodes().size()==2,"Junction intersection is not shared");
215 ++shared;
216 }
217 if(!periodic)
218 {
219 require(link->source!=b1,"Trimmed original node remains in loop");
220 require(link->source->get_P().x()<=0.5*length+1.e-6,"Contour outside physical face");
221 }
222 if(const auto segment=link->networkLink())
223 {
224 if(segment->loopLinks().size()==2)
225 {
226 ++junctionSegments;
227 V expected=V::Zero();
228 for(const auto* participant : segment->loopLinks())
229 {
230 const V flow=participant->loop==first ? burgers : 2.0*burgers;
231 expected+=(participant->source->networkNode==segment->source ? flow : -flow);
232 }
233 require((segment->burgers()-expected).norm()<1.e-8,"Junction Burgers orientation changed");
234 }
235 }
236 }
237 require(junctionSegments>0,"Shared junction segments disappeared");
238 require(shared==1,"Expected one shared intersection on positive face");
239 if(!periodic)
240 {
241 require(b1->prev.second==nullptr && b1->next.second==nullptr,"Removed node retains links");
242 require(b2->prev.second==nullptr && b2->next.second==nullptr,"Removed partner retains links");
243 }
244 b1.reset();
245 b2.reset();
246 if(!periodic)
247 {
248 require(nb->loopNodes().size()==1,"Trimming removed unrelated membership or retained obsolete nodes");
249 }
250 network.updateBoundaryNodes();
251 require(survivor->isLoop(),"Trimming damaged unaffected loop");
252 std::set<size_t> after;
253 for(const auto* link : first->linkSequence())
254 {
255 after.insert(link->source->sID);
256 }
257 require(ids==after,"Repeated update replaced stable contour nodes");
258 if(periodic)
259 {
260 // A stale loop subdivision is replaced without breaking the
261 // other loop's ownership of its shared network representative.
262 std::shared_ptr<Network::LoopNodeType> stale;
263 for(const auto* link : first->linkSequence())
264 {
265 if(link->source->periodicPlaneEdge.first &&
266 std::abs(link->source->networkNode->get_P().x()-0.5*length)<1.e-6 &&
267 std::abs(link->source->get_P().y())<1.e-6)
268 {
269 stale=link->source;
270 break;
271 }
272 }
273 require(bool(stale),"Missing periodic subdivision");
274 const auto sharedNetwork=stale->networkNode;
275 network.danglingBoundaryLoopNodes.insert(stale.get());
276 network.updateBoundaryNodes();
277 require(stale->prev.second==nullptr && stale->next.second==nullptr,"Stale subdivision remains connected");
278 stale.reset();
279 require(sharedNetwork->loopNodes().size()==2,"Replacing stale subdivision broke junction sharing");
280 require(network.danglingBoundaryLoopNodes.empty(),"Dangling node registry not cleared");
281 }
282 }
283 std::filesystem::remove_all(folder);
284 std::cout << "PASS: shared junctions across periodic and physical faces, trimming, repeated updates\n";
285 return 0;
286 }
287 catch(const std::exception& error)
288 {
289 std::cerr << error.what() << '\n';
290 return 1;
291 }
292}
Eigen::Vector3d V
void updateBoundaryNodes(const std::function< void()> &checkpoint={})
std::set< const LoopNodeType * > danglingBoundaryLoopNodes
LoopLinkContainerType & loopLinks()
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)
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
SharedPtrType create(const Args &...args)
SharedPtrType get(const KeyType &key) const
void require(bool condition, const char *message)
void periodic(const Trial &field, double relative=1.e-10)
model::DislocationNetwork< 3 > Network
void argv
int axis
void b int argc
vertex p[3]
vertex first