29void exercise(
const std::filesystem::path& root,
int rank,
bool clamp)
31 auto mesh=std::make_shared<SimplicialMesh<3>>((root/
"Library/Meshes/bicrystal_48.msh").string(),Eigen::Matrix3d::Identity(),Eigen::Vector3d::Zero(),std::set<int>{-1});
35 for(
const auto& face : region.second->faces())
37 const auto& pair=face.second->periodicFacePair;
38 if(pair.second && (pair.first.normalized().cross(Eigen::Vector3d::UnitY()).norm()<1.e-8 ||
39 (rank==2 && pair.first.normalized().cross(Eigen::Vector3d::UnitX()).norm()<1.e-8)))
41 seeds.insert(face.first);
45 mesh->identifyParallelFaces(seeds);
47 auto fe=std::make_shared<FE>(
mesh);
48 auto u=fe->template trial<components>();
49 using Trial=
typename decltype(
u)::element_type;
50 const auto dV=fe->template domain<EntireDomain,4,GaussLegendre>();
52 Eigen::SparseMatrix<double> K(
u->gSize(),
u->gSize());
53 const auto triplets=bwf->globalTriplets();
54 K.setFromTriplets(triplets.begin(),triplets.end());
55 const auto&
B=
mesh->periodicBasis();
56 const Eigen::MatrixXd
Q=
B.householderQr().householderQ()*Eigen::MatrixXd::Identity(3,rank);
57 Eigen::Matrix<double,components,3> H,G;
58 for(
int c=0;
c<components;++
c)
60 H.row(
c)<<0.13*(
c+1),-0.17*(
c+1),0.03*(
c+1);
61 G.row(
c)<<0.2*(
c+1),0.4*(
c+1),0.3*(
c+1);
64 G.row(
c)<<0,0,0.3*(
c+1);
67 u->setPeriodicGradient(H);
68 Eigen::MatrixXd modes=Eigen::MatrixXd::Zero(
u->gSize(),components*rank);
69 Eigen::VectorXd expected(
u->gSize());
70 for(
size_t n=0;n<fe->nodeSize();++n)
72 const Eigen::VectorXd shift=
Q.transpose()*fe->periodicShift(n);
73 for(
int j=0;
j<rank;++
j)
75 modes.block(n*components,
j*components,components,components)=shift(
j)*Eigen::Matrix<double,components,components>::Identity();
77 const Eigen::Vector3d
x=fe->node(n).P0-Eigen::Vector3d(0,0,
mesh->xMin()(2));
78 expected.segment(n*components,components)=G*
x;
81 expected.segment(n*components,components).array()+=0.7;
83 if(clamp && std::abs(fe->node(n).P0(2)-
mesh->xMin()(2))<1.e-10)
85 for(
int c=0;
c<components;++
c)
87 u->dirichletConditions()[n*components+
c]=0.0;
91 const auto pair=fe->periodicNodePairs().front();
94 for(
int c=0;
c<components;++
c)
96 u->dirichletConditions()[pair.sink*components+
c]=expected(pair.sink*components+
c);
99 auto rhs=std::make_shared<Load<Trial>>(
Load<Trial>{
u,K*expected});
101 problem->setAffineModes(modes);
104 require((
u->dofVector()-expected).norm()<1.e-8*std::max(1.0,expected.norm()),
"Coupled affine/Dirichlet solution is wrong");
105 Eigen::Matrix<double,components,3> gradient=H;
106 for(
int j=0;
j<rank;++
j)
108 gradient+=problem->affineCoefficients().segment(
j*components,components)*
Q.col(
j).transpose();
110 require(((gradient-G)*
Q).norm()<1.e-8,
"Original affine coefficients were not reconstructed");
111 for(
const auto&
bc :
u->dirichletConditions())
113 require(std::abs(
u->dofVector()(
bc.first)-
bc.second)<1.e-9,
"Prescribed node moved");
116 problem->solveWithMasterSlaveConstraints();
122 u->dirichletConditions().clear();
123 for(
int c=0;
c<components;++
c)
125 u->dirichletConditions()[pair.source*components+
c]=expected(pair.source*components+
c);
127 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Changed affine anchor reused a stale matrix");
129 for(
int c=0;
c<components;++
c)
131 u->dirichletConditions()[pair.sink*components+
c]=expected(pair.sink*components+
c);
133 problem->solveWithMasterSlaveConstraints();
138 Eigen::MatrixXd rows=Eigen::MatrixXd::Zero(2,
u->gSize());
139 size_t source=pair.source,sink=pair.sink;
142 for(
const auto&
p : fe->periodicNodePairs())
144 if(
u->dirichletConditions().count(
p.source*components) &&
u->dirichletConditions().count(
p.sink*components))
152 rows(0,source*components)=-1.0;
153 rows(0,sink*components)=1.0;
154 rows.row(1)=2.0*rows.row(0);
155 problem->setLinearConstraints(rows);
156 problem->setLinearConstraintValues(rows*expected);
157 problem->solveWithMasterSlaveConstraints();
159 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Unchanged coupled constraints did not reuse factorization");
163 rhs->values=K*expected;
164 for(
auto&
bc :
u->dirichletConditions())
166 bc.second=expected(
bc.first);
169 problem->setLinearConstraintValues(rows*expected);
170 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Compatible value changes refactorized the matrix");
172 const Eigen::VectorXd saved=
u->dofVector();
173 const Eigen::VectorXd coefficients=problem->affineCoefficients();
174 auto incompatible=(rows*expected).
eval();
175 incompatible(0)+=1.0;
176 incompatible(1)+=2.0;
177 problem->setLinearConstraintValues(incompatible);
181 problem->solveWithMasterSlaveConstraints();
183 catch(
const std::invalid_argument&
e)
185 rejected=std::string(
e.what()).find(
"Incompatible")!=std::string::npos;
187 require(rejected,
"Conflicting affine loading and clamps were not diagnosed");
188 require((
u->dofVector()-saved).norm()==0.0 && (problem->affineCoefficients()-coefficients).norm()==0.0,
"Rejected update changed the solution");
189 problem->setLinearConstraintValues(rows*expected);
190 problem->solveWithMasterSlaveConstraints();
195 u->dirichletConditions().at(sink*components)+=1.0;
199 problem->solveWithMasterSlaveConstraints();
201 catch(
const std::invalid_argument&
e)
203 rejected=std::string(
e.what()).find(
"Incompatible")!=std::string::npos;
205 require(rejected,
"Inconsistent prescribed periodic copies were accepted");
206 require((
u->dofVector()-saved).norm()<1.e-12,
"Incompatible nodal conditions changed field");
209 u->dirichletConditions().clear();
210 for(
size_t i=0;
i<size_t(expected.size());++
i)
212 u->dirichletConditions()[
i]=expected(
i);
214 problem->solveWithMasterSlaveConstraints();