Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testWeakProblem.cpp
Go to the documentation of this file.
1#include <FiniteElement.h>
2#include <WeakProblem.h>
3#include <Eigen/SparseLU>
4#include <Eigen/IterativeLinearSolvers>
5#include <filesystem>
6#include <numeric>
7#include <iostream>
8
9using namespace model;
10using Sparse=Eigen::SparseMatrix<double>;
11using Direct=Eigen::SparseLU<Sparse>;
12using Iterative=Eigen::BiCGSTAB<Sparse>;
14
15void require(bool value,const char* message)
16{
17 if(!value)
18 {
19 throw std::runtime_error(message);
20 }
21}
22template<class F>
23void rejects(F&& operation,const char* message)
24{
25 bool rejected=false;
26 try
27 {
28 operation();
29 }
30 catch(const std::exception&)
31 {
32 rejected=true;
33 }
34 require(rejected,message);
35}
36
37// Independent oracle: construct equivalence classes from the pair edges,
38// offsets from coordinates, and perform the explicit dense T^T*A*T product.
39template<class Field,class BWF,class LWF,class Problem>
40void compare(const std::shared_ptr<Field>& u,const std::shared_ptr<BWF>& bwf,
41 const std::shared_ptr<LWF>& lwf,const std::shared_ptr<Problem>& problem)
42{
43 const auto& fe=u->fe();
44 constexpr int components=Field::dofPerNode;
45 std::vector<size_t> roots(fe.nodeSize());
46 std::iota(roots.begin(),roots.end(),0);
47 for(const auto& pair : fe.periodicNodePairs())
48 {
49 const auto a=roots[pair.source];
50 const auto b=roots[pair.sink];
51 for(auto& root : roots)
52 {
53 if(root==a || root==b)
54 {
55 root=std::min(a,b);
56 }
57 }
58 }
59 const Eigen::Index n=u->gSize();
60 Eigen::VectorXd g(n);
61 std::map<size_t,double> fixed;
62 for(size_t node=0;node<fe.nodeSize();++node)
63 {
64 g.segment(node*components,components)=u->periodicGradient()*(fe.node(node).P0-fe.node(roots[node]).P0);
65 }
66 for(const auto& bc : u->dirichletConditions())
67 {
68 fixed[roots[bc.first/components]*components+bc.first%components]=bc.second-g(bc.first);
69 }
70 std::map<size_t,size_t> columns;
71 for(size_t i=0;i<size_t(n);++i)
72 {
73 const auto root=roots[i/components]*components+i%components;
74 if(!fixed.count(root))
75 {
76 columns.emplace(root,columns.size());
77 }
78 }
79 Eigen::MatrixXd T=Eigen::MatrixXd::Zero(n,columns.size());
80 for(size_t i=0;i<size_t(n);++i)
81 {
82 const auto root=roots[i/components]*components+i%components;
83 if(fixed.count(root))
84 {
85 g(i)+=fixed.at(root);
86 }
87 else
88 {
89 T(i,columns.at(root))=1.0;
90 }
91 }
92 Eigen::MatrixXd A=Eigen::MatrixXd::Zero(n,n);
93 for(const auto& t : bwf->globalTriplets())
94 {
95 A(t.row(),t.col())+=t.value();
96 }
97 const Eigen::MatrixXd reduced=T.transpose()*A*T;
98 const Eigen::VectorXd b=T.transpose()*(lwf->globalVector()-A*g);
99 const Eigen::VectorXd expected=T*reduced.fullPivLu().solve(b)+g;
100 require((Eigen::MatrixXd(problem->reducedMatrix())-reduced).norm()<1.e-10*std::max(1.0,reduced.norm()),"Constrained matrix differs from T^T*A*T");
101 require((problem->reducedForceVector()-b).norm()<1.e-10*std::max(1.0,b.norm()),"Constrained force differs from T^T*(b-A*g)");
102 require((u->dofVector()-expected).norm()<2.e-8*std::max(1.0,expected.norm()),"Reconstructed solution differs from reference");
103}
104
105template<int order,int components>
106void exercise(const std::filesystem::path& root,bool periodic,bool iterative)
107{
108 auto mesh=std::make_shared<Mesh>((root/"Library/Meshes/bicrystal_48.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),periodic ? std::set<int>{-1} : std::set<int>{});
110 auto fe=std::make_shared<FE>(mesh);
111 auto u=fe->template trial<components>();
112 auto other=fe->template trial<components>();
113 const auto dV=fe->template domain<EntireDomain,4,GaussLegendre>();
114 const auto mass=(test(u),u)*dV;
115 const auto bwf=mass+(test(grad(u)),grad(u))*dV;
116 const Eigen::Matrix<double,components,1> ones=Eigen::Matrix<double,components,1>::Ones();
117 auto lwf=(test(u),make_constant(ones))*dV;
118 WeakProblemOptions options;
119 options.useDirectSolver=!iterative;
120 options.tolerance=1.e-9;
121 options.maxIterations=4000;
122 auto problem=weakProblem<Direct,Iterative>(bwf,lwf,options);
123 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,"First solve claimed reuse");
124 compare(u,bwf,lwf,problem);
125 require((u->dofVector().array()-1.0).matrix().norm()<1.e-7,"Constant solution changed");
126 require(problem->factorizationCount()==1,"Initial setup count incorrect");
127
128 lwf=(test(u),make_constant(Eigen::Matrix<double,components,1>(3.0*ones)))*dV;
129 problem->setRightHandSide(lwf);
130 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"New RHS lost factorization");
131 compare(u,bwf,lwf,problem);
132 require(problem->factorizationCount()==1,"New RHS refactorized matrix");
133 const auto previous=u->dofVector().eval();
134 const auto foreign=(test(other),make_constant(ones))*dV;
135 rejects([&]() { problem->setRightHandSide(foreign); },"Foreign RHS accepted");
136 rejects([&]() { auto invalid=weakProblem<Direct,Iterative>(bwf,foreign); },"Mixed fields accepted at construction");
137 require((u->dofVector()-previous).norm()==0.0,"Failed RHS replacement modified the field");
138
139 u->dirichletConditions()[0]=2.0;
140 problem->solveWithMasterSlaveConstraints();
141 compare(u,bwf,lwf,problem);
142 require(problem->factorizationCount()==2,"New constraint mapping did not rebuild");
143 u->dirichletConditions()[0]=-0.4;
144 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Prescribed-value change refactorized matrix");
145 compare(u,bwf,lwf,problem);
146
147 Eigen::Matrix<double,components,3> gradient;
148 for(int c=0;c<components;++c)
149 {
150 gradient.row(c)<<0.2*(c+1),-0.3*(c+1),0.4*(c+1);
151 }
152 u->setPeriodicGradient(gradient);
153 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Affine offset change refactorized matrix");
154 compare(u,bwf,lwf,problem);
155
156 if(periodic)
157 {
158 const auto pair=fe->periodicNodePairs().front();
159 const size_t source=pair.source*components;
160 const size_t sink=pair.sink*components;
161 u->dirichletConditions().clear();
162 u->dirichletConditions()[source]=0.7;
163 u->dirichletConditions()[sink]=0.7+(gradient*pair.shift)(0);
164 problem->solveWithMasterSlaveConstraints();
165 compare(u,bwf,lwf,problem);
166 const auto saved=u->dofVector().eval();
167 u->dirichletConditions()[sink]+=1.0;
168 rejects([&]() { problem->solveWithMasterSlaveConstraints(); },"Conflicting periodic Dirichlet values accepted");
169 require((u->dofVector()-saved).norm()==0.0,"Failed constraint validation modified field");
170 u->dirichletConditions().clear();
171 // Select one active lattice direction, then remove periodicity entirely.
172 int seed=-1;
173 for(const auto& region : mesh->regions())
174 {
175 for(const auto& face : region.second->faces())
176 {
177 const auto& p=face.second->periodicFacePair;
178 if(p.second && p.first.cross(mesh->periodicBasis().col(0)).norm()<1.e-8)
179 {
180 seed=face.first;
181 }
182 }
183 }
184 require(seed>=0,"No partial-periodic seed face");
185 mesh->identifyParallelFaces({seed});
186 fe->updatePeriodicNodePairs();
187 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,"Partial-periodic mapping did not rebuild");
188 compare(u,bwf,lwf,problem);
189 mesh->identifyParallelFaces({});
190 fe->updatePeriodicNodePairs();
191 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,"Removed periodicity did not rebuild");
192 compare(u,bwf,lwf,problem);
193 }
194 problem->reassembleMatrix();
195 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,"Explicit reassembly reused stale setup");
196 compare(u,bwf,lwf,problem);
197}
198
199void failuresAndReuse(const std::filesystem::path& root)
200{
201 auto mesh=std::make_shared<Mesh>((root/"Library/Meshes/bicrystal_48.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),std::set<int>{});
203 auto fe=std::make_shared<FE>(mesh);
204 auto u=fe->trial<1>();
205 auto coefficient=fe->trial<1>();
206 *coefficient=1.0;
207 auto dV=fe->domain<EntireDomain,4,GaussLegendre>();
208 auto bwf=(test(u),eval(coefficient)*u)*dV;
209 auto lwf=(test(u),make_constant(1.0))*dV;
210 auto problem=weakProblem<Direct,Iterative>(bwf,lwf);
211 problem->solveWithMasterSlaveConstraints();
212 *coefficient=2.0;
213 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Coefficient snapshot unexpectedly rebuilt");
214 require((u->dofVector().array()-1.0).matrix().norm()<1.e-9,"Cached coefficient snapshot changed implicitly");
215 problem->reassembleMatrix();
216 problem->solveWithMasterSlaveConstraints();
217 require((u->dofVector().array()-0.5).matrix().norm()<1.e-9,"Changed coefficient not reassembled");
218 require(problem->factorizationCount()==2,"Coefficient change setup count incorrect");
219 compare(u,bwf,lwf,problem);
220 const auto saved=u->dofVector().eval();
221 *coefficient=0.0;
222 problem->reassembleMatrix();
223 rejects([&]() { problem->solveWithMasterSlaveConstraints(); },"Singular system accepted");
224 require((u->dofVector()-saved).norm()==0.0,"Failed solve modified field");
225 *coefficient=4.0;
226 problem->reassembleMatrix();
227 problem->solveWithMasterSlaveConstraints();
228 require((u->dofVector().array()-0.25).matrix().norm()<1.e-9,"Recovery after failed factorization failed");
229 auto rhsField=fe->trial<1>();
230 *rhsField=2.0;
231 auto liveRhs=(test(u),eval(rhsField))*dV;
232 problem->setRightHandSide(liveRhs);
233 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Live RHS lost setup");
234 require((u->dofVector().array()-0.5).matrix().norm()<1.e-9,"Live RHS not evaluated");
235 *rhsField=3.0;
236 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Changed live RHS lost setup");
237 require((u->dofVector().array()-0.75).matrix().norm()<1.e-9,"Changed live RHS was stale");
238 const auto beforeInvalid=u->dofVector().eval();
239 *rhsField=std::numeric_limits<double>::quiet_NaN();
240 rejects([&]() { problem->solveWithMasterSlaveConstraints(); },"Nonfinite RHS accepted");
241 require((u->dofVector()-beforeInvalid).norm()==0.0,"Invalid RHS changed trial");
242 problem->setRightHandSide(lwf);
243 for(size_t i=0;i<rhsField->gSize();++i)
244 {
245 rhsField->dofVector()(i)=std::sin(0.37*double(i));
246 }
247 WeakProblemOptions limited;
248 limited.useDirectSolver=false;
249 limited.maxIterations=1;
250 limited.tolerance=1.e-14;
251 auto nonconverging=weakProblem<Direct,Iterative>(bwf,liveRhs,limited);
252 rejects([&]() { nonconverging->solveWithMasterSlaveConstraints(); },"Insufficient iterative solve accepted");
253 require((u->dofVector()-beforeInvalid).norm()==0.0,"Nonconverged iteration changed trial");
254 auto spd=weakProblem<Eigen::SimplicialLDLT<Sparse>,Eigen::ConjugateGradient<Sparse>>(bwf,lwf);
255 spd->solveWithMasterSlaveConstraints();
256 require((u->dofVector().array()-0.25).matrix().norm()<1.e-9,"Alternative solver types failed");
257 for(size_t i=0;i<u->gSize();++i)
258 {
259 u->dirichletConditions()[i]=double(i)/10;
260 }
261 const size_t count=problem->factorizationCount();
262 problem->solveWithMasterSlaveConstraints();
263 require(problem->reducedMatrix().rows()==0,"Fully prescribed problem has unknowns");
264 require(problem->factorizationCount()==count,"Empty system called solver");
265 for(size_t i=0;i<u->gSize();++i)
266 {
267 require(u->dofVector()(i)==double(i)/10,"Prescribed-only reconstruction failed");
268 }
269 const auto allFixed=u->dofVector().eval();
270 u->dirichletConditions()[u->gSize()]=0.0;
271 rejects([&]() { problem->solveWithMasterSlaveConstraints(); },"Out-of-range constraint accepted");
272 require((u->dofVector()-allFixed).norm()==0.0,"Invalid constraint modified field");
273}
274void ownership(const std::filesystem::path& root)
275{
277 using Field=TrialFunction<'u',1,FE>;
278 std::weak_ptr<Field> weakField;
279 std::weak_ptr<FE> weakSpace;
280 auto problem=[&]()
281 {
282 auto mesh=std::make_shared<Mesh>((root/"Library/Meshes/bicrystal_48.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),std::set<int>{-1});
283 auto fe=std::make_shared<FE>(mesh);
284 auto u=fe->trial<1>();
285 weakField=u;
286 weakSpace=fe;
287 auto dV=fe->domain<EntireDomain,4,GaussLegendre>();
288 return weakProblem((test(u),u)*dV,(test(u),make_constant(2.0))*dV);
289 }();
290 problem->solveWithMasterSlaveConstraints();
291 require(!weakField.expired() && !weakSpace.expired(),"Problem did not retain trial and FEM space");
292 require((weakField.lock()->dofVector().array()-2.0).matrix().norm()<1.e-9,"Detached problem solution invalid");
293 problem.reset();
294 require(weakField.expired() && weakSpace.expired(),"WeakProblem leaked an ownership cycle");
295}
296
297int main(int argc,char** argv)
298{
299 try
300 {
301 require(argc==2,"Expected repository root");
302 exercise<1,1>(argv[1],false,false);
303 exercise<1,1>(argv[1],true,false);
304 exercise<2,1>(argv[1],true,false);
305 exercise<1,3>(argv[1],true,false);
306 exercise<1,1>(argv[1],true,true);
308 ownership(argv[1]);
309 std::cout<<"PASS WeakProblem constrained triplets, affine periodicity, repeated RHS, reuse and atomic failure"<<std::endl;
310 }
311 catch(const std::exception& e)
312 {
313 std::cerr<<e.what()<<std::endl;
314 return 1;
315 }
316}
function infiniteDisplacement syms Ax Ay Az Bx By Bz qx qy qz u L x y z real A
final point of the source segment t
c
Definition int_R.m:15
a
Definition int_R.m:14
b
Definition int_R.m:16
auto weakProblem(std::shared_ptr< BWF > bwf, std::shared_ptr< LWF > lwf, WeakProblemOptions options={})
size_t InstanceCount< Derived >::count
auto grad(const std::shared_ptr< T > &x)
Definition TrialGrad.h:66
auto eval(const std::shared_ptr< T > &t)
auto make_constant(const Eigen::Matrix< double, rows, cols > &c)
Definition Constant.h:44
int regions
Definition triangle.cpp:696
Class template defining the GaussLegendre rules for determination of quadrature abscissas and weights...
void periodic(const Trial &field, double relative=1.e-10)
TrialFunction< 'c', 1, Space > Field
SimplicialMesh< 3 > Mesh
void compare(const std::shared_ptr< Field > &u, const std::shared_ptr< BWF > &bwf, const std::shared_ptr< LWF > &lwf, const std::shared_ptr< Problem > &problem)
void exercise(const std::filesystem::path &root, bool periodic, bool iterative)
Eigen::SparseMatrix< double > Sparse
void rejects(F &&operation, const char *message)
void failuresAndReuse(const std::filesystem::path &root)
void require(bool value, const char *message)
Eigen::SparseLU< Sparse > Direct
void ownership(const std::filesystem::path &root)
Eigen::BiCGSTAB< Sparse > Iterative
void argv
REAL u[4]
void b int argc
vertex p[3]
int i
int e
REAL bc[4]