Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testBicrystalNodeClassification.cpp
Go to the documentation of this file.
1#include <NetworkNodeActor.h>
2#include <NetworkLinkActor.h>
3#include <vtkActorCollection.h>
4#include <vtkTubeFilter.h>
8#include <QApplication>
9#include <vtkRenderer.h>
10#include <vtkPointData.h>
11#include <filesystem>
12#include <fstream>
13#include <iomanip>
14#include <chrono>
15#include <iostream>
16#include <iterator>
17
18void require(bool condition,const char* message)
19{
20 if(!condition)
21 {
22 throw std::runtime_error(message);
23 }
24}
25int main(int argc,char** argv)
26{
27 QApplication app(argc,argv);
28 try
29 {
30 require(argc == 2, "Expected repository root");
31 const std::filesystem::path root(argv[1]);
32 const auto folder = std::filesystem::temp_directory_path() /
33 ("bicrystal-node-classification-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
34 const auto input = folder / "inputFiles";
35 std::filesystem::create_directories(input);
36 std::filesystem::create_directories(folder / "F");
37 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
38
39 std::filesystem::copy_file(root / "Library/Meshes/bicrystal_48.msh", input / "mesh.msh");
40 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
41 std::filesystem::create_directories(folder / "config");
42 const double length=3671.9477120460206;
43 {
44 std::ofstream f(input / "DefectiveCrystal.txt");
45 f << "physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0 0 0;\nEwaldLengthFactor=0;\n";
46 std::ofstream p(input / "polycrystal.txt");
47 p << std::setprecision(17) << "materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\n"
48 << "C2G2=0.8 0 0.6 0 1 0 -0.6 0 0.8;\nF=" << length << " 0 0 0 " << length << " 0 0 0 " << length
49 << ";\nX0=0 0 0;\nperiodicFaceIDs=-1;\n";
50 }
51 // Match the tutorial remeshing length, without depending on its inputs.
52 {
53 const auto dd=input/"DD.txt";
54 std::ifstream source(dd);
55 std::string contents((std::istreambuf_iterator<char>(source)),std::istreambuf_iterator<char>());
56 const auto location=contents.find("Lmax=100;");
57 require(location!=std::string::npos,"Expected library remeshing default");
58 contents.replace(location,std::string("Lmax=100;").size(),"Lmax=150;");
59 std::ofstream output(dd);
60 output<<contents;
61 }
62
63 auto window=vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
64 window->SetReadyForRendering(false);
65 auto renderer=vtkSmartPointer<vtkRenderer>::New();
66 window->AddRenderer(renderer);
67 model::NetworkNodeActor actor(window,renderer);
68 for(int frame : {430,500})
69 {
70 model::DefectiveCrystalBase<3> base(folder.string());
71 model::DefectiveCrystal<3> crystal(base);
73 model::DefectiveCrystalConfigIO<3> saved(folder.string());
74 std::ifstream stream(root/"tests/data"/("bicrystalBoundary"+std::to_string(frame)+".txt"));
75 require(bool(stream),"Missing bicrystal fixture");
76 saved.readTxtStream(stream);
77 network.initializeConfiguration(saved,std::ofstream(),std::ofstream());
78 size_t internal=0,periodicGB=0,sameGrain=0,bulk=0,corners=0,stale=0;
79 for(const auto& entry : network.networkNodes())
80 {
81 const auto node=entry.second.lock();
82 bool internalFace=false,crossGrainFace=false,sameGrainFace=false,external=false;
83 for(const auto* face : node->meshFaces())
84 {
85 if(face->regionIDs.first!=face->regionIDs.second)
86 {
87 internalFace=true;
88 }
89 else
90 {
91 external=true;
92 const auto* twin=face->periodicFacePair.second;
93 if(twin)
94 {
95 crossGrainFace|=face->regionIDs.first!=twin->regionIDs.first;
96 sameGrainFace|=face->regionIDs.first==twin->regionIDs.first;
97 }
98 }
99 }
100 const bool gb=internalFace || crossGrainFace;
101 const int expected=gb?2:(external?1:0);
102 require(node->isGrainBoundaryNode()==gb,"Wrong physical GB classification");
103 require(node->isOnInternalBoundary()==internalFace,"Geometric internal-face classification changed");
104 require(node->isBoundaryNode()==external,"Geometric external-face classification changed");
105 require(node->meshLocation()==expected,"GB does not take priority at periodic boundary");
106 const model::DislocationNodeIO<3> output(*node);
107 require(output.meshLocation==expected,"New output does not serialize updated classification");
108 internal+=internalFace;
109 periodicGB+=crossGrainFace;
110 sameGrain+=sameGrainFace && !gb;
111 bulk+=!gb && !external;
112 corners+=gb && sameGrainFace;
113 }
114 require(internal && periodicGB && sameGrain && bulk && corners,
115 "Bicrystal fixture lacks required node categories");
116 {
117 size_t internalSegments=0,periodicSegments=0,boundarySegments=0;
118 for(const auto& entry : network.networkLinks())
119 {
120 const auto link=entry.second.lock();
121 bool gb=false;
122 for(const auto* face : link->meshFaces())
123 {
124 gb|=face->regionIDs.first!=face->regionIDs.second ||
125 (face->periodicFacePair.second && face->regionIDs!=face->periodicFacePair.second->regionIDs);
126 }
127 require(link->isGrainBoundarySegment()==gb,"Wrong GB segment classification");
128 if(gb)
129 {
130 require(link->meshLocation()==2,"GB segment has boundary-only output label");
131 }
132 if(!link->hasZeroBurgers())
133 {
134 internalSegments+=link->isOnInternalBoundary();
135 periodicSegments+=gb && !link->isOnInternalBoundary();
136 boundarySegments+=gb || link->isBoundarySegment();
137 }
138 }
139 require(internalSegments && periodicSegments,"Fixture lacks both GB segment types");
140 actor.updateConfiguration(saved,&network);
141 const int actorCount=renderer->GetActors()->GetNumberOfItems();
142 model::NetworkLinkActor links(window,renderer,crystal);
143 links.updateConfiguration(actor.nodePolyData,{});
144 auto* boundaryActor=vtkActor::SafeDownCast(renderer->GetActors()->GetItemAsObject(actorCount+1));
145 require(boundaryActor && boundaryActor->GetMapper(),"Missing boundary segment actor");
146 auto* tube=vtkTubeFilter::SafeDownCast(boundaryActor->GetMapper()->GetInputAlgorithm());
147 auto* data=vtkPolyData::SafeDownCast(tube->GetInputDataObject(0,0));
148 require(data && data->GetNumberOfLines()==static_cast<vtkIdType>(boundarySegments),"DDqt omitted GB segments");
149 auto* show=links.findChild<QCheckBox*>("showSegments");
150 require(bool(show),"Missing segment visibility control");
151 auto* opacity=links.findChild<QSlider*>("boundarySegmentOpacity");
152 require(opacity && opacity->value()==0 && !boundaryActor->GetVisibility(),
153 "Boundary segments should default to transparent");
154 opacity->setValue(50);
155 require(boundaryActor->GetVisibility() && std::abs(boundaryActor->GetProperty()->GetOpacity()-0.5)<1.e-12,
156 "Boundary opacity control did not update the actor");
157 show->setChecked(false);
158 require(!boundaryActor->GetVisibility(),"Segments control did not hide GB segments");
159 show->setChecked(true);
160 require(boundaryActor->GetVisibility(),"Segments control did not show GB segments");
161 }
162 actor.updateConfiguration(saved,&network);
163 auto* colors=actor.nodePolyData->GetPointData()->GetScalars();
164 require(colors && colors->GetNumberOfTuples()==static_cast<vtkIdType>(saved.nodes().size()),
165 "Wrong color tuple count");
166 size_t i=0;
167 for(const auto& savedNode : saved.nodes())
168 {
169 const auto node=network.networkNodes().at(savedNode.sID).lock();
170 const int location=node->meshLocation();
171 for(int component=0;component<3;++component)
172 {
173 require(colors->GetComponent(i,component)==actor.nodeClr[location][component],
174 "DDqt uses stale serialized classification");
175 }
176 stale+=savedNode.meshLocation!=location;
177 ++i;
178 }
179 require(stale>0,"Fixture does not exercise legacy boundary labels");
180 // With no DD network available, retain support for serialized-only data.
181 actor.updateConfiguration(saved);
182 i=0;
183 for(const auto& node : saved.nodes())
184 {
185 const int index=node.meshLocation>=0 && node.meshLocation<=2?node.meshLocation:3;
186 for(int component=0;component<3;++component)
187 {
188 require(actor.nodePolyData->GetPointData()->GetScalars()->GetComponent(i,component)==actor.nodeClr[index][component],
189 "Serialized-only node coloring changed");
190 }
191 ++i;
192 }
193 std::cout<<"Frame "<<frame<<": internal GB "<<internal<<", periodic GB "<<periodicGB
194 <<", same-grain seam "<<sameGrain<<", corners "<<corners<<", corrected labels "<<stale<<std::endl;
195 }
196 std::filesystem::remove_all(folder);
197 return 0;
198 }
199 catch(const std::exception& error)
200 {
201 std::cerr<<error.what()<<std::endl;
202 return 1;
203 }
204}
void initializeConfiguration(const DefectiveCrystalConfigIO< dim > &configIO, const std::ofstream &f_file, const std::ofstream &F_labels) override
function int_R syms a b c u real f
Definition int_R.m:4
std::shared_ptr< MicrostructureType > getUniqueTypedMicrostructure() const
vtkSmartPointer< vtkPolyData > nodePolyData
void updateConfiguration(const DefectiveCrystalConfigIO< 3 > &configIO, const DislocationNetwork< 3 > *network=nullptr)
unsigned char nodeClr[4][3]
void require(bool condition, const char *message)
void argv
int index
int corners
void b int argc
vertex p[3]
int i