Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
testElasticGlobalPenalty.cpp
Go to the documentation of this file.
1#include <FiniteElement.h>
2#include <WeakProblem.h>
6#include <filesystem>
7#include <fstream>
8#include <chrono>
9#include <iomanip>
10#include <sstream>
11#include <iostream>
12using namespace model;
13void require(bool v,const char* text)
14{
15 if(!v)
16 {
17 throw std::runtime_error(text);
18 }
19}
20template<class Trial>
21struct Load
22{
23 std::shared_ptr<Trial> trial;
24 Eigen::VectorXd value;
25 const auto& field() const
26 {
27 return trial->field();
28 }
29 Eigen::VectorXd globalVector() const
30 {
31 return value;
32 }
33};
34void exercise(const std::filesystem::path& root,int rank)
35{
36 auto mesh=std::make_shared<SimplicialMesh<3>>((root/"Library/Meshes/bicrystal_48.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d(2,3,4),std::set<int>{-1});
37 std::set<int> seeds;
38 for(int d=0;d<rank;++d)
39 {
40 for(const auto& region : mesh->regions())
41 {
42 for(const auto& face : region.second->faces())
43 {
44 const auto& pair=face.second->periodicFacePair;
45 if(pair.second && pair.first.cross(mesh->periodicBasis().col(d)).norm()<1.e-8)
46 {
47 seeds.insert(face.first);
48 break;
49 }
50 }
51 }
52 }
53 mesh->identifyParallelFaces(seeds);
55 auto fe=std::make_shared<FE>(mesh);
56 auto u=fe->trial<3>();
57 using Trial=typename decltype(u)::element_type;
58 auto dV=fe->domain<EntireDomain,4,GaussLegendre>();
59 Eigen::Matrix<double,6,6> C=Eigen::Matrix<double,6,6>::Identity();
60 C.topLeftCorner<3,3>().array()+=1.0;
61 auto strain=def(u);
62 auto stiffness=(test(strain),C*strain)*dV;
63 auto penalty=std::make_shared<GlobalRigidMotionPenalty<Trial>>(u,2.0);
64 auto form=stiffness+penalty;
65 const Eigen::MatrixXd F=penalty->lowRankPenaltyRows();
66 const Eigen::MatrixXd R=penalty->rigidModes();
67 const int modes=rank==0 ? 6 : (rank==1 ? 4 : 3);
68 require(F.rows()==modes,"Wrong admissible rigid-mode count");
69 Eigen::SparseMatrix<double> sparse(u->gSize(),u->gSize());
70 auto triplets=stiffness->globalTriplets();
71 sparse.setFromTriplets(triplets.begin(),triplets.end());
72 const Eigen::MatrixXd K=sparse;
73 require((K*R).norm()<1.e-10*K.norm()*R.norm(),"Rigid mode has elastic strain energy");
74 require((F*R).fullPivLu().rank()==modes,"Penalty leaves a rigid mode free");
75 // Independent explicit T, g reference, including a nonzero affine jump.
76 std::map<size_t,size_t> columns;
77 for(size_t n=0;n<fe->nodeSize();++n)
78 {
79 columns.emplace(fe->periodicRepresentative(n),columns.size());
80 }
81 Eigen::MatrixXd T=Eigen::MatrixXd::Zero(u->gSize(),3*columns.size());
82 Eigen::VectorXd g=Eigen::VectorXd::Zero(u->gSize());
83 Eigen::Matrix3d H=Eigen::Matrix3d::Zero();
84 H(2,2)=0.02;
85 u->setPeriodicGradient(H);
86 for(size_t n=0;n<fe->nodeSize();++n)
87 {
88 T.block<3,3>(3*n,3*columns.at(fe->periodicRepresentative(n))).setIdentity();
89 g.segment<3>(3*n)=H*fe->periodicShift(n);
90 }
91 Eigen::VectorXd q(T.cols());
92 for(Eigen::Index i=0;i<q.size();++i)
93 {
94 q(i)=std::sin(0.31*i);
95 }
96 Eigen::VectorXd target=T*q+g;
97 target-=R*(F*R).fullPivLu().solve(F*target);
98 require((F*target).norm()<1.e-10,"Deformation has nonzero global rigid moment");
99 require((K*target).norm()>1.e-4,"Test deformation is trivial");
100 auto rhs=std::make_shared<Load<Trial>>(Load<Trial>{u,K*target});
101 auto problem=weakProblem(form,rhs);
102 problem->solveWithMasterSlaveConstraints();
103 require((u->dofVector()-target).norm()<1.e-7*target.norm(),"Global penalty altered deformation");
104 const Eigen::MatrixXd A=K+F.transpose()*F;
105 const Eigen::MatrixXd reduced=T.transpose()*A*T;
106 const Eigen::VectorXd b=T.transpose()*(rhs->value-A*g);
107 const Eigen::VectorXd expected=T*reduced.ldlt().solve(b)+g;
108 require((u->dofVector()-expected).norm()<1.e-7*expected.norm(),"Bordered solve differs from explicit global penalty");
109 require((problem->reducedForceVector()-b).norm()<1.e-9*std::max(1.0,b.norm()),"Affine penalty lifting is wrong");
110 require(problem->solverMatrix().rows()==T.cols()+modes,"Penalty was not kept low rank");
111 rhs->value*=2.0;
112 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,"Changing RHS refactorized stiffness");
113 auto strong=std::make_shared<GlobalRigidMotionPenalty<Trial>>(u,200.0);
114 auto other=weakProblem(stiffness+strong,rhs);
115 const Eigen::VectorXd solution=u->dofVector();
116 other->solveWithMasterSlaveConstraints();
117 require((u->dofVector()-solution).norm()<1.e-7*solution.norm(),"Penalty strength changed deformation");
118 const auto external=fe->boundary<ExternalBoundary,3,GaussLegendre>();
119 const auto physical=fe->boundary<NonPeriodicExternalBoundary,3,GaussLegendre>();
120 for(const auto& face : physical)
121 {
122 require(!mesh->periodicSimplexPairs().count(&face.first->simplex.child(face.second)),"Periodic face received free-surface traction");
123 }
124 require((rank==0 && physical.size()==external.size()) || (rank>0 && physical.size()<external.size()),"Physical traction selection is wrong");
125 // Fixing one point removes translations but retains compatible rotations about it.
126 for(size_t i=0;i<3;++i)
127 {
128 u->dirichletConditions()[i]=solution(i);
129 }
130 require(penalty->rigidModes().cols()==(rank==0 ? 3 : (rank==1 ? 1 : 0)),"Point constraints removed the wrong rigid modes");
131 // Homogeneous Dirichlet restrictions must also remove modes from the gauge.
132 for(size_t i=0;i<u->gSize();++i)
133 {
134 u->dirichletConditions()[i]=solution(i);
135 }
136 require(penalty->lowRankPenaltyRows().rows()==0,"Fixed field still penalizes rigid modes");
137 problem->solveWithMasterSlaveConstraints();
138 require((u->dofVector()-solution).norm()<1.e-10,"Fixed solution changed");
139}
140
141
143{
144 Eigen::Matrix3d plastic=Eigen::Matrix3d::Zero();
146 Eigen::Matrix3d averagePlasticDistortion() const override
147 {
148 return plastic;
149 }
150};
151void physicsIntegration(const std::filesystem::path& root,int rank,bool rotated,int mode,bool withSource=false,bool withPin=false,bool irregular=false)
152{
153 using V=Eigen::Matrix<double,6,1>;
154 using M=Eigen::Matrix<double,6,6>;
155 const std::string meshName=irregular ? "polycrystalCube_10_periodicXY.msh" : "bicrystal_48.msh";
156 const Eigen::Matrix3d Q=rotated ? Eigen::AngleAxisd(0.63,Eigen::Vector3d(1,2,3).normalized()).toRotationMatrix() : Eigen::Matrix3d::Identity();
157 StaticID<Polyhedron<3,3-1>>::force_count(0);
158 std::set<int> seeds;
159 {
160 SimplicialMesh<3> mesh((root/"Library/Meshes"/meshName).string(),Q,Eigen::Vector3d::Zero(),{-1});
161 for(const auto& region : mesh.regions())
162 {
163 for(const auto& face : region.second->faces())
164 {
165 const auto& pair=face.second->periodicFacePair;
166 for(int i=0;i<rank;++i)
167 {
168 const int axis=i==0 ? 1 : (i==1 ? 0 : 2);
169 if(pair.second && pair.first.normalized().cross(Q.col(axis)).norm()<1.e-8)
170 {
171 seeds.insert(face.first);
172 }
173 }
174 }
175 }
176 }
177 StaticID<Polyhedron<3,3-1>>::force_count(0);
178 const auto folder=std::filesystem::temp_directory_path()/("elastic-mixed-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
179 const auto input=folder/"inputFiles";
180 std::filesystem::create_directories(input);
181 std::filesystem::create_directories(folder/"config");
182 std::filesystem::create_directories(folder/"F");
183 std::filesystem::copy_file(root/"Library/Materials/W.txt",input/"W.txt");
184 std::filesystem::copy_file(root/"Library/Meshes"/meshName,input/"mesh.msh");
185 V strain0,strainRate,stress0,stressRate,alpha;
186 strain0<<0.002,0.001,-0.0005,0.0007,0.0003,-0.0004;
187 strainRate=0.1*strain0;
188 stress0<<0.001,-0.0004,0.0007,0.0003,-0.0002,0.0004;
189 stressRate=0.2*stress0;
190 if(mode==0)
191 {
192 alpha.setZero();
193 }
194 else if(mode==1)
195 {
196 alpha.setConstant(1.e20);
197 }
198 else
199 {
200 alpha<<0,1.e20,2,0.5,1.e20,0;
201 }
202 if(irregular)
203 {
204 strain0.setZero();
205 strainRate.setZero();
206 strainRate(2)=0.001;
207 stress0.setZero();
208 stressRate.setZero();
209 alpha.setZero();
210 alpha(2)=1.e20;
211 }
212 const auto writeLoading=[&](bool fem,const V& ratios,const V& stresses,const V& rates)
213 {
214 std::ofstream out(input/"ElasticDeformation.txt");
215 out<<std::setprecision(17)<<"useElasticDeformationFEM="<<fem<<";\ninertiaReliefPenaltyFactor=0.001;\nExternalStrain0="<<strain0.transpose()<<";\nExternalStrainRate="<<strainRate.transpose()<<";\nExternalStress0="<<stresses.transpose()<<";\nExternalStressRate="<<rates.transpose()<<";\nstiffnessRatio="<<ratios.transpose()<<";\n";
216 };
217 {
218 std::ofstream dc(input/"DefectiveCrystal.txt");
219 dc<<"physics=ElasticDeformation;\nuseFEM=1;\nNsteps=0;\nmaxResolveSteps=0;\ndtMax=1;\noutputFrequency=1;\noutputBinary=0;\nstartAtTimeStep=0;\nperiodicImageSize=";
220 for(int i=0;i<rank;++i)
221 {
222 dc<<"0 ";
223 }
224 dc<<";\nEwaldLengthFactor=0;\n";
225 const auto matrixText=[](const Eigen::Matrix3d& matrix)
226 {
227 std::ostringstream result;
228 result<<std::setprecision(17);
229 for(int row=0;row<3;++row)
230 {
231 for(int col=0;col<3;++col)
232 {
233 result<<matrix(row,col)<<" ";
234 }
235 }
236 return result.str();
237 };
238 std::ofstream poly(input/"polycrystal.txt");
239 poly<<"materialFile=W.txt;\nmeshFile=mesh.msh;\nabsoluteTemperature=300;\nC2G1="<<matrixText(Q)<<";\nC2G2="<<matrixText(Q)<<";\nF="<<matrixText(3671.9477120460206*Q)<<";\nX0=0 0 0;\nperiodicFaceIDs=";
240 for(int seed : seeds)
241 {
242 poly<<seed<<" ";
243 }
244 poly<<";\n";
245 if(irregular)
246 {
247 for(int grain=3;grain<=10;++grain)
248 {
249 poly<<"C2G"<<grain<<"="<<matrixText(Q)<<";\n";
250 }
251 }
252 writeLoading(true,alpha,stress0,stressRate);
253 }
254 {
255 DefectiveCrystalBase<3> base(folder.string());
256 require(base.fe!=nullptr,"Explicit FEM request was disabled in a periodic domain");
257 require(base.periodicLatticeBasis.cols()==rank,"Unexpected active periodic rank");
258 // Each physics configuration has its own immutable context. Do not
259 // change the meaning of an existing base by rewriting its input file.
260 std::unique_ptr<DefectiveCrystalBase<3>> sourceBase;
261 std::unique_ptr<MicrostructureContainer<3>> sourceContainer;
262 MicrostructureContainer<3> container(base);
263 ElasticDeformation<3> physics(container);
264 physics.initializeSolver();
265 auto u=physics.elasticDeformationFEM->u;
266 if(withPin)
267 {
268 const size_t node=base.fe->periodicNodePairs().front().sink;
269 for(int c=0;c<3;++c)
270 {
271 u->dirichletConditions()[3*node+c]=0.0;
272 }
273 }
274 physics.zDot=Eigen::VectorXd::Zero(u->gSize());
275 const M C=physics.elasticDeformationFEM->C;
276 V eigenstrain=V::Zero();
277 V sourceStress=V::Zero();
278 if(withSource)
279 {
280 sourceStress<<0.0002,0.0003,-0.0001,0.0002,0,-0.0001;
281 writeLoading(false,V::Zero(),sourceStress,V::Zero());
282 StaticID<Polyhedron<3,3-1>>::force_count(0);
283 sourceBase=std::make_unique<DefectiveCrystalBase<3>>(folder.string());
284 sourceContainer=std::make_unique<MicrostructureContainer<3>>(*sourceBase);
285 auto source=std::make_shared<UniformSource>(*sourceContainer);
286 eigenstrain<<0.0004,0.0001,0,0.0002,0,0;
287 source->plastic=base.voigtTraits.v2m(eigenstrain,true);
288 source->uniformLoadController->gs=eigenstrain;
289 container.microstructures().push_back(source);
290 writeLoading(true,alpha,stress0,stressRate);
291 }
292 size_t count=0;
293 for(double time : {7.0,12.0,12.0})
294 {
295 base.simulationParameters.totalTime=time;
296 if(sourceBase)
297 {
298 sourceBase->simulationParameters.totalTime=time;
299 }
300 physics.solve();
301 for(const auto& bc : u->dirichletConditions())
302 {
303 require(std::abs(u->dofVector()(bc.first)-bc.second)<1.e-9,"Mixed elastic controller moved a pinned node");
304 }
305 M system=C;
306 V target=stress0+time*stressRate+C*eigenstrain;
307 for(int i=0;i<6;++i)
308 {
309 if(alpha(i)>=1.e15)
310 {
311 system.row(i).setZero();
312 system(i,i)=1.0;
313 target(i)=strain0(i)+time*strainRate(i);
314 }
315 else
316 {
317 const double device=alpha(i)*C(i,i);
318 system(i,i)+=device;
319 target(i)+=device*(strain0(i)+time*strainRate(i));
320 }
321 }
322 const V expectedTotal=system.fullPivLu().solve(target);
323 const V offset=eigenstrain+C.ldlt().solve(sourceStress);
324 const V expected=expectedTotal-offset;
325 require((physics.femLoadController->averageStrain()-expected).norm()<1.e-9,"Mixed FEM average strain differs from uniform controller");
326 require((base.voigtTraits.m2v(physics.averageStress(),false)+sourceStress-C*(expectedTotal-eigenstrain)).norm()<1.e-9,"Mixed FEM average stress differs from uniform controller");
327 const Eigen::Matrix3d expectedStrain=base.voigtTraits.v2m(expected,true);
328 const double length=(base.mesh.xMax()-base.mesh.xMin()).norm();
329 Eigen::MatrixXd coordinates(base.fe->nodeSize(),4),values(base.fe->nodeSize(),3);
330 for(const auto& node : base.fe->nodes())
331 {
332 coordinates.block<1,3>(node.gID,0)=node.P0.transpose()/length;
333 coordinates(node.gID,3)=1.0;
334 values.row(node.gID)=u->dofVector().segment<3>(3*node.gID).transpose();
335 }
336 const Eigen::MatrixXd fit=coordinates.colPivHouseholderQr().solve(values);
337 const Eigen::Matrix3d expectedGradient=fit.topRows(3).transpose()/length;
338 require((0.5*(expectedGradient+expectedGradient.transpose())-expectedStrain).norm()<1.e-9,"Mixed loading produced incorrect homogeneous strain");
339 if(!withPin)
340 {
341 require((expectedGradient-expectedGradient.transpose()).norm()<1.e-9,"Global stabilization introduced mean rotation");
342 Eigen::Vector3d centroid=Eigen::Vector3d::Zero();
343 double volume=0.0;
344 for(const auto& entry : base.fe->elements())
345 {
346 centroid+=entry.second.simplex.vol0*entry.second.simplex.center();
347 volume+=entry.second.simplex.vol0;
348 }
349 centroid/=volume;
350 const double meanDisplacement=(expectedGradient*centroid+fit.row(3).transpose()).norm();
351 const double displacementScale=std::max(1.0,values.norm()/std::sqrt(double(values.rows())));
352 // The volume-normalized penalty has weight 1e-3 against dimensional
353 // stiffness; test drift relative to the physical displacement scale.
354 require(meanDisplacement<1.e-6*displacementScale,"Mean affine displacement is not zero");
355 if(irregular)
356 {
357 require(std::abs(expectedGradient(2,0))+std::abs(expectedGradient(2,1))<1.e-9,"Horizontal plane has a spurious u3 gradient");
358 }
359 }
360 require((coordinates*fit-values).norm()<2.e-7*std::max(1.0,values.norm()),"Mixed loading introduced nonuniform deformation");
361 Eigen::Matrix3d projection=Eigen::Matrix3d::Zero();
362 if(rank>0)
363 {
364 const auto& B=base.periodicLatticeBasis;
365 projection=B*(B.transpose()*B).inverse()*B.transpose();
366 }
367 require((u->periodicGradient()-expectedGradient*projection).norm()<1.e-8,"Solved affine gradient used inactive periodic directions");
368 for(const auto& pair : base.fe->periodicNodePairs())
369 {
370 const Eigen::Vector3d jump=u->dofVector().segment<3>(3*pair.sink)-u->dofVector().segment<3>(3*pair.source);
371 require((jump-u->periodicGradient()*pair.shift).norm()<1.e-8,"Solved periodic jump is inconsistent");
372 }
373 if(count>0)
374 {
375 require(physics.weakProblemSolver->factorizationCount()==count,"Changing mixed load refactorized stiffness");
376 }
377 count=physics.weakProblemSolver->factorizationCount();
378 }
379 if(mode==2)
380 {
381 // Independently check the finite-ratio case using the actual uniform controller.
382 V finiteAlpha=alpha;
383 finiteAlpha(1)=3.0;
384 finiteAlpha(4)=4.0;
385 writeLoading(true,finiteAlpha,stress0,stressRate);
386 ElasticDeformation<3> finite(container);
387 finite.zDot=Eigen::VectorXd::Zero(u->gSize());
388 finite.solve();
389 UniformController<6> reference(0.0,C,finiteAlpha,strain0,strainRate,stress0,stressRate);
390 reference.gs=eigenstrain;
391 const V offset=eigenstrain+C.ldlt().solve(sourceStress);
392 require((finite.femLoadController->averageStrain()+offset-reference.grad(base.simulationParameters.totalTime)).norm()<1.e-9,"Finite FEM device stiffness disagrees with UniformController");
393 }
394 }
395 std::filesystem::remove_all(folder);
396}
397int main(int argc,char** argv)
398{
399 try
400 {
401 require(argc==2,"Expected repository path");
402 for(int rank=0;rank<=3;++rank)
403 {
404 exercise(argv[1],rank);
405 }
406 for(int rank=0;rank<=3;++rank)
407 {
408 for(int mode=0;mode<3;++mode)
409 {
410 physicsIntegration(argv[1],rank,false,mode);
411 }
412 }
413 physicsIntegration(argv[1],1,true,2);
414 physicsIntegration(argv[1],2,true,2,true);
415 physicsIntegration(argv[1],1,true,2,false,true);
416 for(int rank=0;rank<=2;++rank)
417 {
418 physicsIntegration(argv[1],rank,false,1,false,false,true);
419 }
420 std::cout<<"Global elastic penalty tests passed\n";
421 }
422 catch(const std::exception& e)
423 {
424 std::cerr<<e.what()<<'\n';
425 return 1;
426 }
427}
Eigen::Matrix3d M
Eigen::Vector3d V
A class template that implements a counter of the number of instances of Derived type that are create...
Definition StaticID.h:29
initial point of the source segment B
function infiniteDisplacement syms Ax Ay Az Bx By Bz qx qy qz u L x y z real A
the magnitude of the R vector q
c
Definition int_R.m:15
b
Definition int_R.m:16
auto def(const std::shared_ptr< T > &x)
Definition TrialDef.h:64
auto weakProblem(std::shared_ptr< BWF > bwf, std::shared_ptr< LWF > lwf, WeakProblemOptions options={})
size_t InstanceCount< Derived >::count
double SplineSegment< dim, corder >::alpha
std::shared_ptr< Trial > trial
Eigen::VectorXd globalVector() const
const auto & field() const
Eigen::VectorXd value
Eigen::Matrix3d averagePlasticDistortion() const override
int regions
Definition triangle.cpp:696
MatrixDim averageStress() const override
const std::unique_ptr< ElasticDeformationFEM< dim > > elasticDeformationFEM
std::shared_ptr< FEMControllerType > femLoadController
ElasticDeformation(MicrostructureContainerType &mc)
std::shared_ptr< WeakProblemType > weakProblemSolver
Class template defining the GaussLegendre rules for determination of quadrature abscissas and weights...
VectorVoigt grad(const double &t) const
std::string text(Config &config)
void physicsIntegration(const std::filesystem::path &root, int rank, bool rotated, int mode, bool withSource=false, bool withPin=false, bool irregular=false)
void require(bool v, const char *text)
void exercise(const std::filesystem::path &root, int rank)
Eigen::MatrixXd matrix(const Form &form)
Q
void out
void argv
REAL u[4]
int axis
void b int argc
int i
int e
b poly
result
REAL bc[4]
REAL v[4]