Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testLoopAreaTriangulation.cpp
Go to the documentation of this file.
2#include <Polygon2D.h>
6#include <filesystem>
7#include <fstream>
8#include <iomanip>
9#include <chrono>
10#include <iostream>
11#include <iterator>
12
13void require(bool condition,const char* message)
14{
15 if(!condition)
16 {
17 throw std::runtime_error(message);
18 }
19}
20using V=Eigen::Vector2d;
21double area(const std::vector<V>& polygon)
22{
23 double result=0.0;
24 for(const auto& t : model::triangulateLoopArea(polygon))
25 {
26 const V a=t[1]-t[0];
27 const V b=t[2]-t[0];
28 result+=0.5*(a.x()*b.y()-a.y()*b.x());
29 require(model::Polygon2D::windingNumber(V((t[0]+t[1]+t[2])/3.0),polygon)!=0,
30 "Triangle filled a zero-winding region");
31 }
32 return result;
33}
34void example(std::vector<V> polygon,double expected)
35{
36 for(int direction=0;direction<2;++direction)
37 {
38 for(size_t i=0;i<polygon.size();++i)
39 {
40 require(std::abs(area(polygon)-expected)<1.e-10,"Incorrect filled area or start-dependent tessellation");
41 std::rotate(polygon.begin(),polygon.begin()+1,polygon.end());
42 }
43 std::reverse(polygon.begin(),polygon.end());
44 }
45}
46int main(int argc,char** argv)
47{
48 try
49 {
50 example({{0,0},{2,0},{2,2},{0,2}},4);
51 example({{0,0},{2,2},{0,2},{2,0}},2); // bow tie: zero signed area, two visible lobes
52 example({{0,0},{2,0},{2,1},{1,1},{1,2},{0,2}},3);
53 example({{0,0},{1,0},{1,1},{0,1},{0,0},{2,0},{3,0},{3,1},{2,1},{2,0}},2);
54 example({{0,0},{3,0},{3,3},{0,3},{0,0},{1,1},{1,2},{2,2},{2,1},{1,1}},8);
55 example({{0,0},{1,0},{1,1},{0,1},{0,0},{1,0},{1,1},{0,1}},1); // winding two, fill once
56 example({{0,0},{1,0},{1,1},{0,1},{0,0},{0,1},{1,1},{1,0}},0);
57 example({{0,0},{0,0},{1,0},{1,1},{0,1},{0,0}},1);
58 example({{0,0},{1,0},{2,0}},0);
59 require(argc == 2, "Expected repository root");
60 const std::filesystem::path root(argv[1]);
61 const auto folder = std::filesystem::temp_directory_path() /
62 ("bicrystal-boundary-topology-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
63 const auto input = folder / "inputFiles";
64 std::filesystem::create_directories(input);
65 std::filesystem::create_directories(folder / "F");
66 std::filesystem::copy_file(root / "Library/Materials/W.txt", input / "W.txt");
67
68 std::filesystem::copy_file(root / "Library/Meshes/bicrystal_48.msh", input / "mesh.msh");
69 std::filesystem::copy_file(root / "Library/DislocationDynamics/DD.txt", input / "DD.txt");
70 std::filesystem::create_directories(folder / "config");
71 const double length=3671.9477120460206;
72 {
73 std::ofstream f(input / "DefectiveCrystal.txt");
74 f << "physics=DislocationDynamics;\nuseFEM=0;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=0 0 0;\nEwaldLengthFactor=0;\n";
75 std::ofstream p(input / "polycrystal.txt");
76 p << std::setprecision(17) << "materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1=1 0 0 0 1 0 0 0 1;\n"
77 << "C2G2=0.8 0 0.6 0 1 0 -0.6 0 0.8;\nF=" << length << " 0 0 0 " << length << " 0 0 0 " << length
78 << ";\nX0=0 0 0;\nperiodicFaceIDs=-1;\n";
79 }
80 // Match the tutorial remeshing length, without depending on its inputs.
81 {
82 const auto dd=input/"DD.txt";
83 std::ifstream source(dd);
84 std::string contents((std::istreambuf_iterator<char>(source)),std::istreambuf_iterator<char>());
85 const auto location=contents.find("Lmax=100;");
86 require(location!=std::string::npos,"Expected library remeshing default");
87 contents.replace(location,std::string("Lmax=100;").size(),"Lmax=150;");
88 std::ofstream output(dd);
89 output<<contents;
90 }
91
92 for(int frame : {430,433,500})
93 {
94 model::DefectiveCrystalBase<3> base(folder.string());
96 model::DislocationNetwork<3> network(container);
97 model::DefectiveCrystalConfigIO<3> saved(folder.string());
98 std::ifstream stream(root/"tests/data"/("bicrystalBoundary"+std::to_string(frame)+".txt"));
99 require(bool(stream),"Missing bicrystal fixture");
100 saved.readTxtStream(stream);
101 network.initializeConfiguration(saved,std::ofstream(),std::ofstream());
102 size_t patches=0;
103 for(const auto& item : network.loops())
104 {
105 const auto loop=item.second.lock();
106 Eigen::Vector3d fullArea=Eigen::Vector3d::Zero();
107 for(const auto* link : loop->linkSequence())
108 {
109 fullArea+=0.5*link->source->get_P().cross(link->sink->get_P());
110 }
111 // Compare against the actual loop, not merely against the clipped
112 // input to the renderer: omitted physical corners corrupt that input.
113 require((loop->patches().orientedArea()-fullArea).norm()<1.e-8*std::max(1.0,fullArea.norm()),
114 "Patch assembly discarded part of the original loop area");
115 for(const auto& patch : loop->patches().localPatches())
116 {
117 const auto& polygon=patch.second;
118 const double original=area(polygon);
119 auto reversed=polygon;
120 std::reverse(reversed.begin(),reversed.end());
121 require(std::abs(area(reversed)-original)<1.e-8*std::max(1.0,original),
122 "Bicrystal fill depends on contour orientation");
123 const auto triangles=model::triangulateLoopArea(polygon);
124 if(polygon.empty())
125 {
126 continue;
127 }
128 V low=polygon.front(),high=low;
129 for(const auto& p : polygon)
130 {
131 low=low.cwiseMin(p);
132 high=high.cwiseMax(p);
133 }
134 // Independently compare raster coverage against the contour winding.
135 for(int i=0;i<19;++i)
136 {
137 for(int j=0;j<19;++j)
138 {
139 const V p=low+(high-low).cwiseProduct(V((i+0.371)/19,(j+0.613)/19));
140 int hits=0;
141 for(const auto& t : triangles)
142 {
143 hits+=model::Polygon2D::windingNumber(p,std::vector<V>{t[0],t[1],t[2]})!=0;
144 }
145 require(hits==(model::Polygon2D::windingNumber(p,polygon)!=0),
146 "Bicrystal tessellation has missing or overlapping fill");
147 }
148 }
149 ++patches;
150 }
151 }
152 require(patches>0,"No bicrystal patches tested");
153 std::cout<<"Checked frame "<<frame<<": "<<patches<<" patches\n";
154 }
155 std::filesystem::remove_all(folder);
156 return 0;
157 }
158 catch(const std::exception& e)
159 {
160 std::cerr<<e.what()<<std::endl;
161 return 1;
162 }
163}
Eigen::Vector3d V
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
final point of the source segment t
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
std::vector< std::array< Eigen::Vector2d, 3 > > triangulateLoopArea(const std::vector< Eigen::Vector2d > &contour)
static int windingNumber(const PointType &P, const std::vector< PointType > &V)
Definition Polygon2D.h:56
void require(bool condition, const char *message)
void example(std::vector< V > polygon, double expected)
int j
void argv
void b int argc
vertex p[3]
int i
int e
REAL area
result