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)
43 const auto& fe=
u->fe();
45 std::vector<size_t> roots(fe.nodeSize());
46 std::iota(roots.begin(),roots.end(),0);
47 for(
const auto& pair : fe.periodicNodePairs())
49 const auto a=roots[pair.source];
50 const auto b=roots[pair.sink];
51 for(
auto& root : roots)
53 if(root==
a || root==
b)
59 const Eigen::Index n=
u->gSize();
61 std::map<size_t,double> fixed;
62 for(
size_t node=0;node<fe.nodeSize();++node)
64 g.segment(node*components,components)=
u->periodicGradient()*(fe.node(node).P0-fe.node(roots[node]).P0);
66 for(
const auto&
bc :
u->dirichletConditions())
68 fixed[roots[
bc.first/components]*components+
bc.first%components]=
bc.second-g(
bc.first);
70 std::map<size_t,size_t> columns;
71 for(
size_t i=0;
i<size_t(n);++
i)
73 const auto root=roots[
i/components]*components+
i%components;
74 if(!fixed.count(root))
76 columns.emplace(root,columns.size());
79 Eigen::MatrixXd T=Eigen::MatrixXd::Zero(n,columns.size());
80 for(
size_t i=0;
i<size_t(n);++
i)
82 const auto root=roots[
i/components]*components+
i%components;
89 T(
i,columns.at(root))=1.0;
92 Eigen::MatrixXd
A=Eigen::MatrixXd::Zero(n,n);
93 for(
const auto&
t : bwf->globalTriplets())
95 A(
t.row(),
t.col())+=
t.value();
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");
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;
116 const Eigen::Matrix<double,components,1> ones=Eigen::Matrix<double,components,1>::Ones();
123 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,
"First solve claimed reuse");
125 require((
u->dofVector().array()-1.0).matrix().norm()<1.e-7,
"Constant solution changed");
126 require(problem->factorizationCount()==1,
"Initial setup count incorrect");
129 problem->setRightHandSide(lwf);
130 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,
"New RHS lost factorization");
132 require(problem->factorizationCount()==1,
"New RHS refactorized matrix");
133 const auto previous=
u->dofVector().eval();
135 rejects([&]() { problem->setRightHandSide(foreign); },
"Foreign RHS accepted");
137 require((
u->dofVector()-previous).norm()==0.0,
"Failed RHS replacement modified the field");
139 u->dirichletConditions()[0]=2.0;
140 problem->solveWithMasterSlaveConstraints();
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");
147 Eigen::Matrix<double,components,3> gradient;
148 for(
int c=0;
c<components;++
c)
150 gradient.row(
c)<<0.2*(
c+1),-0.3*(
c+1),0.4*(
c+1);
152 u->setPeriodicGradient(gradient);
153 require(problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Affine offset change refactorized matrix");
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();
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();
175 for(
const auto& face : region.second->faces())
177 const auto&
p=face.second->periodicFacePair;
178 if(
p.second &&
p.first.cross(
mesh->periodicBasis().col(0)).norm()<1.e-8)
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");
189 mesh->identifyParallelFaces({});
190 fe->updatePeriodicNodePairs();
191 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Removed periodicity did not rebuild");
194 problem->reassembleMatrix();
195 require(!problem->solveWithMasterSlaveConstraints().reusedFactorization,
"Explicit reassembly reused stale setup");
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>();
211 problem->solveWithMasterSlaveConstraints();
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");
220 const auto saved=
u->dofVector().eval();
222 problem->reassembleMatrix();
223 rejects([&]() { problem->solveWithMasterSlaveConstraints(); },
"Singular system accepted");
224 require((
u->dofVector()-saved).norm()==0.0,
"Failed solve modified field");
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>();
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");
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)
245 rhsField->dofVector()(
i)=std::sin(0.37*
double(
i));
252 rejects([&]() { nonconverging->solveWithMasterSlaveConstraints(); },
"Insufficient iterative solve accepted");
253 require((
u->dofVector()-beforeInvalid).norm()==0.0,
"Nonconverged iteration changed trial");
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)
259 u->dirichletConditions()[
i]=double(
i)/10;
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)
267 require(
u->dofVector()(
i)==
double(
i)/10,
"Prescribed-only reconstruction failed");
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");
278 std::weak_ptr<Field> weakField;
279 std::weak_ptr<FE> weakSpace;
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>();
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");
294 require(weakField.expired() && weakSpace.expired(),
"WeakProblem leaked an ownership cycle");