Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testGlideIndexCaches.cpp
Go to the documentation of this file.
4#ifdef _OPENMP
5#include <omp.h>
6#endif
9#include <filesystem>
10#include <fstream>
11#include <iomanip>
12#include <chrono>
13#include <iostream>
14#include <map>
15#include <iterator>
16
17void require(bool condition, const char* message)
18{
19 if(!condition)
20 {
21 throw std::runtime_error(message);
22 }
23}
24
25
27using Triplets=std::deque<Eigen::Triplet<double>>;
28using Clock=std::chrono::steady_clock;
29
30Eigen::VectorXd referenceVelocity(Network& network,const Triplets& triplets,const Eigen::VectorXd& F)
31{
32 Triplets z;
33 const auto freeCount=model::periodicVelocityConstraints<3>(network,z);
34 Eigen::SparseMatrix<double> K(F.size(),F.size()),Z(F.size(),3*freeCount);
35 K.setFromTriplets(triplets.begin(),triplets.end());
36 Z.setFromTriplets(z.begin(),z.end());
37 const Eigen::SparseMatrix<double> reduced(Z.transpose()*K*Z);
38 const Eigen::VectorXd force(Z.transpose()*F);
39 Eigen::VectorXd diagonal=Eigen::VectorXd::Zero(Z.cols());
40 for(int k=0;k<reduced.outerSize();++k)
41 {
42 for(Eigen::SparseMatrix<double>::InnerIterator it(reduced,k);it;++it)
43 {
44 if(it.row()==it.col())
45 {
46 diagonal(it.row())+=it.value();
47 }
48 else
49 {
50 diagonal(it.row())+=0.5*it.value();
51 diagonal(it.col())+=0.5*it.value();
52 }
53 }
54 }
55 Eigen::VectorXd velocity=Eigen::VectorXd::Zero(Z.cols());
56 for(Eigen::Index k=0;k<diagonal.size();++k)
57 {
58 if(std::abs(diagonal(k))>FLT_EPSILON)
59 {
60 velocity(k)=force(k)/diagonal(k);
61 }
62 else
63 {
64 require(std::abs(force(k))<=FLT_EPSILON,"Force on a zero-mass degree of freedom");
65 }
66 }
67 return Z*velocity;
68}
69
70void verify(Network& network)
71{
72 network.updateGeometry();
73 const auto nodeCount=network.networkNodes().size();
74 const auto loopCount=network.loops().size();
75 std::map<size_t,std::pair<Eigen::Vector3d,Eigen::Vector3d>> positions;
76 for(const auto& entry : network.loopNodes())
77 {
78 const auto ln=entry.second.lock();
79 positions.emplace(ln->sID,std::make_pair(ln->get_P(),ln->networkNode->get_P()));
80 }
81 std::map<std::pair<size_t,size_t>,std::pair<Eigen::Vector3d,std::set<size_t>>> topology;
82 for(const auto& entry : network.networkLinks())
83 {
84 const auto link=entry.second.lock();
85 std::set<size_t> loops;
86 for(const auto* ll : link->loopLinks())
87 {
88 loops.insert(ll->loop->sID);
89 }
90 topology.emplace(entry.first,std::make_pair(link->burgers(),loops));
91 link->createQuadraturePoints(false);
92 }
93 const auto capture=[&]()
94 {
95 std::vector<Eigen::Matrix<double,18,1>> values;
96 for(const auto& entry : network.networkLinks())
97 {
98 for(const auto& qp : entry.second.lock()->quadraturePoints())
99 {
100 Eigen::Matrix<double,18,1> value;
101 value.head<9>()=Eigen::Map<const Eigen::Matrix<double,9,1>>(qp.stress.data());
102 value.segment<3>(9)=qp.pkForce;
103 value.segment<3>(12)=qp.lineTensionForce;
104 value.tail<3>()=qp.velocity;
105 values.push_back(value);
106 }
107 }
108 return values;
109 };
110 const auto t0=Clock::now();
111#ifdef _OPENMP
112#pragma omp parallel for
113#endif
114 for(size_t k=0;k<network.networkLinks().size();++k)
115 {
116 auto it=network.networkLinks().begin();
117 std::advance(it,k);
118 it->second.lock()->updateQuadraturePoints(false);
119 }
120 const double oldFields=std::chrono::duration<double>(Clock::now()-t0).count();
121 const auto oldValues=capture();
122 require(!oldValues.empty(),"Fixture has no quadrature points");
123 Triplets oldK;
124 Eigen::VectorXd oldF=Eigen::VectorXd::Zero(3*nodeCount);
125 const auto t1=Clock::now();
126 for(const auto& entry : network.networkLinks())
127 {
128 const auto link=entry.second.lock();
129 if(!link->isBoundarySegment())
130 {
131 link->addToGlobalAssembly(oldK,oldF);
132 }
133 }
134 const double oldAssembly=std::chrono::duration<double>(Clock::now()-t1).count();
135 const Eigen::VectorXd oldVelocity=referenceVelocity(network,oldK,oldF);
136
137 // Start both traversal methods from identical, freshly created quadrature points.
138 for(const auto& entry : network.networkLinks())
139 {
140 entry.second.lock()->createQuadraturePoints(false);
141 }
142 const auto t2=Clock::now();
143 std::vector<std::shared_ptr<Network::NetworkLinkType>> links;
144 links.reserve(network.networkLinks().size());
145 for(const auto& entry : network.networkLinks())
146 {
147 links.push_back(entry.second.lock());
148 }
149#ifdef _OPENMP
150#pragma omp parallel for
151#endif
152 for(size_t k=0;k<links.size();++k)
153 {
154 links[k]->updateQuadraturePoints(false);
155 }
156 const double newFields=std::chrono::duration<double>(Clock::now()-t2).count();
157 const auto newValues=capture();
158 require(newValues.size()==oldValues.size(),"Snapshot changed the quadrature count");
159 double fieldError=0;
160 for(size_t i=0;i<oldValues.size();++i)
161 {
162 require(newValues[i].allFinite(),"Nonfinite quadrature result");
163 fieldError=std::max(fieldError,(newValues[i]-oldValues[i]).cwiseAbs().maxCoeff());
164 }
165 require(fieldError==0,"Snapshot changed a quadrature field, force or velocity");
166 const auto t3=Clock::now();
167 std::map<size_t,size_t> indices;
168 for(const auto& entry : network.networkNodes())
169 {
170 indices.emplace(entry.second.lock()->sID,indices.size());
171 }
172 Triplets newK;
173 Eigen::VectorXd newF=Eigen::VectorXd::Zero(3*nodeCount);
174 for(const auto& link : links)
175 {
176 if(!link->isBoundarySegment())
177 {
178 link->addToGlobalAssembly(newK,newF,indices.at(link->source->sID),indices.at(link->sink->sID));
179 }
180 }
181 const double newAssembly=std::chrono::duration<double>(Clock::now()-t3).count();
182 require(oldK.size()==newK.size(),"Indexed assembly changed matrix sparsity");
183 for(size_t k=0;k<oldK.size();++k)
184 {
185 require(oldK[k].row()==newK[k].row() && oldK[k].col()==newK[k].col() && oldK[k].value()==newK[k].value(),
186 "Indexed assembly changed a matrix entry");
187 }
188 require((oldF.array()==newF.array()).all(),"Indexed assembly changed the force vector");
189 const Eigen::VectorXd velocity=network.glideSolver->getNodeVelocities();
190 const double velocityError=(velocity-oldVelocity).cwiseAbs().maxCoeff();
191 require(velocityError==0,"Production solver differs from legacy assembly");
192 require(network.networkNodes().size()==nodeCount && network.loops().size()==loopCount &&
193 network.networkLinks().size()==topology.size(),"Field evaluation changed topology");
194 for(const auto& entry : network.networkLinks())
195 {
196 const auto link=entry.second.lock();
197 std::set<size_t> loops;
198 for(const auto* ll : link->loopLinks())
199 {
200 loops.insert(ll->loop->sID);
201 }
202 require(loops==topology.at(entry.first).second &&
203 (link->burgers().array()==topology.at(entry.first).first.array()).all(),"Junction sharing changed");
204 }
205 for(const auto& entry : network.loopNodes())
206 {
207 const auto ln=entry.second.lock();
208 require((ln->get_P().array()==positions.at(ln->sID).first.array()).all() &&
209 (ln->networkNode->get_P().array()==positions.at(ln->sID).second.array()).all(),
210 "Field evaluation changed the periodic loop/network positions");
211 }
212 std::cout<<"\nCACHE BENCHMARK nodes="<<nodeCount<<" links="<<links.size()<<" qpoints="<<oldValues.size()
213 <<" fields="<<oldFields<<" -> "<<newFields<<" assembly="<<oldAssembly<<" -> "<<newAssembly
214 <<" fieldError="<<fieldError<<" velocityError="<<velocityError<<'\n';
215}
216
217int main(int argc, char** argv)
218{
219 try
220 {
221 require(argc == 2 || argc == 3, "Expected repository root");
222 const std::filesystem::path root(argv[1]);
223 if(argc==3 && std::filesystem::is_directory(argv[2]))
224 {
225#ifdef _OPENMP
226 omp_set_num_threads(12);
227#endif
229 model::DefectiveCrystalConfigIO<3> saved(base.simulationParameters.traitsIO.configFolder);
230 saved.read(base.simulationParameters.runID);
231 model::DefectiveCrystal<3> crystal(base);
232 crystal.initializeConfiguration(saved);
233 const auto network=crystal.getUniqueTypedMicrostructure<Network>();
234 require(bool(network) && !network->computeDDinteractions,"Benchmark requires DD with interactions disabled");
235 verify(*network);
236 return 0;
237 }
238 const auto folder = std::filesystem::temp_directory_path() /
239 ("glide-index-caches-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
240 const auto input = folder / "inputFiles";
241 std::filesystem::create_directories(input);
242 std::filesystem::create_directories(folder / "F");
243 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
244
245 std::filesystem::copy_file(root / "Library/Meshes/bicrystal_48.msh", input / "mesh.msh");
246 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
247 std::filesystem::create_directories(folder / "config");
248 const double length=3671.9477120460206;
249 {
250 std::ofstream f(input / "DefectiveCrystal.txt");
251 f << "physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0 0 0;\nEwaldLengthFactor=0;\n";
252 std::ofstream p(input / "polycrystal.txt");
253 p << std::setprecision(17) << "materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\n"
254 << "C2G2=0.8 0 0.6 0 1 0 -0.6 0 0.8;\nF=" << length << " 0 0 0 " << length << " 0 0 0 " << length
255 << ";\nX0=0 0 0;\nperiodicFaceIDs=-1;\n";
256 }
257 model::DefectiveCrystalBase<3> base(folder.string());
258 model::MicrostructureContainer<3> container(base);
259 model::DislocationNetwork<3> network(container);
260 model::DefectiveCrystalConfigIO<3> saved(folder.string());
261 std::ifstream stream(argc==3 ? std::filesystem::path(argv[2]) : root/"tests/data/bicrystalSharedConstraints1915.txt");
262 require(bool(stream),"Missing bicrystal junction regression fixture");
263 saved.readTxtStream(stream);
264 network.initializeConfiguration(saved,std::ofstream(),std::ofstream());
265 network.computeDDinteractions=false;
266#ifdef _OPENMP
267 omp_set_num_threads(argc==3 ? 12 : 2);
268#endif
269 verify(network);
270 if(argc==2)
271 {
272 std::shared_ptr<Network::NetworkLinkType> shared;
273 for(const auto& entry : network.networkLinks())
274 {
275 const auto link=entry.second.lock();
276 if(link->loopLinks().size()>1 && link->chordLength()>1 && !link->isBoundarySegment())
277 {
278 shared=link;
279 break;
280 }
281 }
282 require(bool(shared),"Missing shared junction for topology-change test");
283 const auto memberships=shared->loopLinks().size();
284 auto inserted=network.networkNodes().create(shared->get_r(0.5));
285 network.expandNetworkLink(shared,inserted);
286 shared.reset();
287 require(inserted->loopNodes().size()==memberships,"Expansion lost junction sharing");
288 verify(network);
289 network.removeNetworkNode(inserted->sID);
290 inserted.reset();
291 verify(network);
292 }
293 std::filesystem::remove_all(folder);
294 std::cout<<"PASS: exact cached-index equivalence before and after shared junction topology changes\n";
295 return 0;
296 }
297 catch(const std::exception& error)
298 {
299 std::cerr<<error.what()<<'\n';
300 return 1;
301 }
302}
std::chrono::steady_clock Clock
void initializeConfiguration(const DefectiveCrystalConfigIO< dim > &configIO)
std::shared_ptr< DislocationGlideSolverBase< dim > > glideSolver
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
NetworkLinkContainerType & networkLinks()
SharedNetworkNodePtrType expandNetworkLink(const std::shared_ptr< NetworkLinkType > &Lij, const SharedNetworkNodePtrType &networkNode)
void removeNetworkNode(const size_t &nodeID)
id z()
function int_R syms a b c u real f
Definition int_R.m:4
size_t periodicVelocityConstraints(const Network &network, Triplets &triplets)
std::shared_ptr< MicrostructureType > getUniqueTypedMicrostructure() const
SharedPtrType create(const Args &...args)
model::DislocationNetwork< 3 > Network
std::deque< Eigen::Triplet< double > > Triplets
void verify(Network &network)
Eigen::VectorXd referenceVelocity(Network &network, const Triplets &triplets, const Eigen::VectorXd &F)
void require(bool condition, const char *message)
REAL t0
void argv
void b int argc
INEXACT REAL t1
vertex p[3]
int k
int i