37 const std::filesystem::path root(
argv[1]);
38 const auto folder=std::filesystem::temp_directory_path()/
39 (
"thermal-branches-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
40 const auto input=folder/
"inputFiles";
41 std::filesystem::create_directories(input);
42 std::filesystem::create_directories(folder/
"F");
43 std::filesystem::create_directories(folder/
"config");
44 std::filesystem::copy_file(root/
"Library/Materials/Zr.txt",input/
"Zr.txt");
45 std::filesystem::copy_file(root/
"Library/Meshes/bicrystal_48.msh",input/
"mesh.msh");
47 std::ofstream material(input/
"Zr.txt",std::ios::app);
49 material<<
"\ncsw_SI=1e15;\ncsLb_SI=9.699e-8;\ncsLp_SI=9.699e-8;\ncsb2pE_eV=0;\ncsb2pVeg_SI=0;\ncsb2pVsc_SI=0;\ncsb2pVec_SI=0;\ncsp2bE_eV=0;\ncsp2bVeg_SI=0;\ncsp2bVsc_SI=0;\ncsp2bVec_SI=0;\n";
50 std::ifstream dd(root/
"Library/DislocationDynamics/DD.txt");
51 std::string
text((std::istreambuf_iterator<char>(dd)),std::istreambuf_iterator<char>());
52 const auto pos=
text.find(
"crossSlipModel=0;");
53 require(pos!=std::string::npos,
"Missing cross-slip setting");
54 text.replace(pos,17,
"crossSlipModel=2;");
55 std::ofstream(input/
"DD.txt")<<
text;
56 std::ofstream(input/
"DefectiveCrystal.txt")<<
"physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=;\nEwaldLengthFactor=0;\n";
58 const double box=3671.9477120460206;
59 std::vector<double> referenceLocations;
60 for(
const int refinement : {1,2,12,200})
63 std::ofstream
p(input/
"polycrystal.txt");
64 p<<std::setprecision(17)<<
"materialFile=Zr.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1="<<(refinement==200?
"0 -1 0 1 0 0 0 0 1" :
"1 0 0 0 1 0 0 0 1")<<
";\nC2G2=1 0 0 0 1 0 0 0 1;\nF="<<box<<
" 0 0 0 "<<box<<
" 0 0 0 "<<box<<
";\nX0=0 0 0;\nperiodicFaceIDs=;\n";
67 using V=Eigen::Vector3d;
71 const auto grain=base.poly.grain(1);
72 const auto slip=grain->slipSystems().begin()->second;
73 const auto plane=base.dislocationDynamics().glidePlaneFactory.getFromKey(
model::GlidePlaneKey<3>(
V(-0.5*box,0,0),slip->n));
74 const V center=plane->snapToPlane(
V(-0.5*box,0,0));
75 const V along=slip->s.cartesian().normalized();
76 const V side=plane->unitNormal.cross(along);
77 const V a=center-0.15*box*along,
end=center+0.15*box*along;
78 std::vector<V> polygon;
79 for(
int k=0;
k<=refinement;++
k)
81 polygon.push_back(
a+(
end-
a)*(
double(
k)/refinement));
83 polygon.push_back(
end+100.0*side);
84 polygon.push_back(
a+100.0*side);
88 const auto parent=network.
loops().begin()->second.lock();
89 base.simulationParameters.dt=1.0;
91 require(thermal,
"Missing production HEX thermal model");
92 double totalRate=0.0,totalLength=0.0;
93 for(
const auto* link :
parent->linkSequence())
95 if(link->networkLink())
97 link->networkLink()->createQuadraturePoints(
false);
98 for(
auto&
q : link->networkLink()->quadraturePoints())
102 if(thermal->isCrossSlipLink(*link->networkLink()).first)
104 totalLength+=link->networkLink()->chordLength();
105 for(
const auto& [target,rate] : thermal->targetRates(*link->networkLink()))
112 require(std::abs(totalLength-0.6*box)<1.e-6,
"Thermal eligibility depends on remeshing length");
113 require(totalRate>0.0 && std::abs(totalRate-thermal->csw*totalLength/thermal->csLb)<1.e-8*totalRate,
"Integrated branch hazard depends on segmentation");
114 std::deque<model::CrossSlipBranch<model::TypeTraits<Network>::dim>> candidates;
115 parent->crossSlipBranches(candidates,0.0);
116 require(candidates.empty(),
"Thermal sampler ignored the supplied timestep");
117 thermal->gen.seed(77);
118 parent->crossSlipBranches(candidates,1.0);
119 require(candidates.size()==2,
"Branch discovery depends on segmentation");
120 std::vector<double> locations;
121 for(
const auto& candidate : candidates)
123 require(candidate.thermal && std::abs(candidate.thermal->length-300.0)<1.e-9,
"Wrong physical nucleus length");
124 const auto target=grain->slipSystems().at(candidate.second);
125 require(target->n.base()(2)==0,
"Wrong thermal target plane family");
126 locations.push_back(candidate.thermal->centerFraction);
128 std::sort(locations.begin(),locations.end());
129 if(referenceLocations.empty())
131 referenceLocations=locations;
135 for(
size_t k=0;
k<locations.size();++
k)
137 require(std::abs(locations[
k]-referenceLocations[
k])<1.e-9,
"Nucleation position depends on segmentation");
144 std::shared_ptr<Network::LoopNodeType> junction;
145 for(
const auto* link :
parent->linkSequence())
147 if((link->source->get_P()-center).norm()<1.e-6)
149 junction=link->source;
152 require(
bool(junction),
"Missing junction anchor");
153 const auto other=network.
loops().
create(slip->s.cartesian(),plane);
154 std::vector<std::shared_ptr<Network::LoopNodeType>> nodes;
155 for(
const auto& net : std::vector{junction->networkNode,
160 other->periodicGlidePlane->getPatch(V::Zero()),std::make_pair(
nullptr,
nullptr)));
165 parent->crossSlipBranches(candidates,1.0);
166 require(candidates.size()==3,
"Thermal branch did not stop at a shared junction");
167 for(
const auto& candidate : candidates)
169 for(
size_t k=1;
k+1<candidate.first.size();++
k)
171 require(candidate.first[
k]->networkNode!=junction->networkNode,
"Junction remained inside thermal branch");
179 require(network.
loops().size()==3,
"Production thermal seeds were not inserted");
180 for(
const auto& entry : network.
loops())
182 const auto loop=entry.second.lock();
185 double perimeter=0.0;
186 for(
const auto* link : loop->linkSequence())
188 perimeter+=(link->sink->get_P()-link->source->get_P()).norm();
190 require(std::abs(perimeter-600.0)<1.e-6,
"Production seed length depends on segmentation");
194 std::filesystem::remove_all(folder);
195 std::cout<<
"PASS: production HEX branch discovery, rates and nucleus geometry invariant under refinement\n";
197 catch(
const std::exception& error)
199 std::cerr<<error.what()<<
'\n';