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");
35 std::vector<int> seeds;
40 for(
const auto& face : region.second->faces())
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)
46 seeds.push_back(face.first);
51 require(seeds.size() == 3,
"Expected three cube periodic directions");
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})
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=";
68 p << seeds[activeAxis];
75 using V=Eigen::Vector3d;
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();
81 const auto second=network.
loops().
create(2.0*burgers,plane);
84 const V a=length*
V(0.2,0,0);
85 const V b=length*
V(0.7,0,0);
88 const auto makeNode=[&](
const auto& loop,
const auto& net,
const V& position)
90 return network.
loopNodes().
create(loop,net,position,loop->periodicGlidePlane->getPatch(V::Zero()),
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);
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});
102 const auto unrelated=network.
loops().
create(burgers,plane);
103 network.
insertLoop(unrelated,std::vector<std::shared_ptr<Network::LoopNodeType>>{
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()),
115 const V originalPosition=na->get_P();
116 const auto originalSimplex=na->includingSimplex();
120 na->set_P(length*
V(10,10,10));
122 catch(
const std::runtime_error&)
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())
135 const auto node=entry.second.lock();
136 oldLoopNodes.emplace(entry.first,std::make_pair(node.get(),node->get_P()));
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());
144 for(
const auto& entry : network.
loopLinks())
146 oldLinks.emplace(entry.first,&entry.second);
150 const auto link=entry.second.lock();
151 oldBurgers.emplace(link.get(),link->burgers());
154 for(;failAt<500;++failAt)
161 if(++operations==failAt)
163 throw std::runtime_error(
"Injected boundary commit failure");
168 catch(
const std::runtime_error& error)
170 require(std::string(error.what())==
"Injected boundary commit failure",
"Unexpected update failure");
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)
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");
183 for(
const auto& entry : oldNetworkNodes)
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");
191 for(
const auto& entry : oldLinks)
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");
197 for(
const auto& entry : oldBurgers)
199 require((entry.first->burgers()-entry.second).norm()==0,
"Rollback changed junction Burgers vector");
202 require(failAt>5 && failAt<500,
"Commit fault injection did not complete");
204 require(
first->isLoop() && second->isLoop(),
"Clipped topology is not closed");
206 size_t junctionSegments=0;
207 std::set<size_t> ids;
208 for(
const auto* link :
first->linkSequence())
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)
214 require(n->loopNodes().size()==2,
"Junction intersection is not shared");
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");
222 if(
const auto segment=link->networkLink())
224 if(segment->loopLinks().size()==2)
227 V expected=V::Zero();
228 for(
const auto* participant : segment->loopLinks())
230 const V flow=participant->loop==
first ? burgers : 2.0*burgers;
231 expected+=(participant->source->networkNode==segment->source ? flow : -flow);
233 require((segment->burgers()-expected).norm()<1.e-8,
"Junction Burgers orientation changed");
237 require(junctionSegments>0,
"Shared junction segments disappeared");
238 require(shared==1,
"Expected one shared intersection on positive face");
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");
248 require(nb->loopNodes().size()==1,
"Trimming removed unrelated membership or retained obsolete nodes");
251 require(survivor->isLoop(),
"Trimming damaged unaffected loop");
252 std::set<size_t> after;
253 for(
const auto* link :
first->linkSequence())
255 after.insert(link->source->sID);
257 require(ids==after,
"Repeated update replaced stable contour nodes");
262 std::shared_ptr<Network::LoopNodeType> stale;
263 for(
const auto* link :
first->linkSequence())
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)
273 require(
bool(stale),
"Missing periodic subdivision");
274 const auto sharedNetwork=stale->networkNode;
277 require(stale->prev.second==
nullptr && stale->next.second==
nullptr,
"Stale subdivision remains connected");
279 require(sharedNetwork->loopNodes().size()==2,
"Replacing stale subdivision broke junction sharing");
283 std::filesystem::remove_all(folder);
284 std::cout <<
"PASS: shared junctions across periodic and physical faces, trimming, repeated updates\n";
287 catch(
const std::exception& error)
289 std::cerr << error.what() <<
'\n';