50 auto fe=std::make_shared<Space>(
mesh);
51 auto other=std::make_shared<Space>(
makeMesh(root,2.0));
52 auto u=std::make_shared<Field>(*fe);
54 auto v=std::make_shared<Field>(*fe);
55 auto w=std::make_shared<Field>(*other);
58 const auto& ele=fe->elements().begin()->second;
59 const Eigen::Vector4d bary=Eigen::Vector4d::Constant(.25);
60 require(std::abs((*
eval(
u))(ele,bary)(0)-2.0)<1.e-12,
"Second same-type field overwrote first field");
61 require(std::abs((*
eval(
v))(ele,bary)(0)-7.0)<1.e-12,
"Independent field values lost");
62 require(&
u->fe()==fe.get() && &
w->fe()==other.get(),
"FEM pointer shared across instances");
63 u->dirichletConditions().emplace(0,2.0);
64 u->dirichletNodeMap()[0][0]=
true;
65 require(
v->dirichletConditions().empty() &&
w->dirichletNodeMap().empty(),
"Constraints leaked across fields");
68 require(std::abs((*
eval(
u))(ele,bary)(0)-3.0)<1.e-12,
"Copy handle lost field identity");
69 auto beforeAssignment=
eval(
u);
72 require(std::abs((*beforeAssignment)(ele,bary)(0)-7.0)<1.e-12,
"Assignment rebound an existing expression");
73 auto savedGradient=[&]()
75 auto local=2.0*
grad(
u);
78 require((*savedGradient)(ele,bary).norm()<1.e-11,
"Saved intermediate expression lost ownership");
79 std::weak_ptr<Field> coefficientField;
80 auto coefficient=[&]()
82 auto c=std::make_shared<Field>(*fe);
87 require(std::abs((*
eval(coefficient))(ele,bary)(0)-28.0)<1.e-11,
"Evaluated coefficient did not retain its field");
89 require(!coefficientField.expired(),
"Coefficient field was not retained");
91 static_assert(std::is_same_v<
decltype(sum),std::shared_ptr<TrialSum<Field,Field>>>);
92 static_assert(!std::is_copy_constructible_v<Field>);
93 require(sum->op1.get()==
u.get() && sum->op2.get()==
u.get(),
"Sum copied its operands");
94 require(std::abs((*
eval(sum))(ele,bary)(0)-14.0)<1.e-12,
"Trial sum evaluation changed");
95 rejects([&]() {
auto invalid=
u+
v; },
"Sum accepted independent fields");
96 rejects([&]() {
auto invalid=
grad(std::shared_ptr<Field>{}); },
"Null operand accepted");
97 require((*
eval(
grad(2.0*
u)))(ele,bary).norm()<1.e-11,
"Gradient of scalar product changed");
105 Eigen::MatrixXd expectedM=Eigen::MatrixXd::Zero(
u->gSize(),
u->gSize());
106 Eigen::MatrixXd expectedK=expectedM;
107 Eigen::VectorXd expectedLoad=Eigen::VectorXd::Zero(
u->gSize());
108 for(
const auto& entry : fe->elements())
110 const auto&
e=entry.second;
111 Eigen::Matrix4d coordinates;
114 coordinates(
i,0)=1.0;
115 coordinates.row(
i).tail<3>()=
e.node(
i).P0.transpose();
117 const Eigen::Matrix<double,3,4> gradients=coordinates.inverse().bottomRows<3>();
120 const size_t I=
e.node(
i).gID;
121 expectedLoad(
I)+=
e.simplex.vol0/4.0;
124 const size_t J=
e.node(
j).gID;
125 expectedM(
I,J)+=
e.simplex.vol0*(
i==
j ? 2.0 : 1.0)/20.0;
126 expectedK(
I,J)+=
e.simplex.vol0*gradients.col(
i).dot(gradients.col(
j));
130 require((
M-expectedM).norm()<1.e-11*expectedM.norm(),
"Mass assembly changed");
131 require((K-expectedK).norm()<1.e-11*expectedK.norm(),
"Diffusion assembly changed");
132 require((load->globalVector()-expectedLoad).norm()<1.e-11*expectedLoad.norm(),
"Load assembly changed");
133 require((
matrix(mass+mass)-2*
M).norm()<1.e-11*
M.norm(),
"Bilinear sum changed");
134 require(((load+load-load)->globalVector()-expectedLoad).norm()<1.e-11*expectedLoad.norm(),
"Linear sum/difference changed");
141 auto retainedMass=[&]()
144 auto localForm=(
test(
u),
u);
145 return localForm*localDomain;
147 require((
matrix(retainedMass)-
M).norm()<1.e-12,
"Temporary integration domain was borrowed");
156 area+=
e.first->simplex.child(
e.second).vol0;
158 require(std::abs(boundary->globalVector().sum()-
area)<1.e-10*
area,
"Boundary assembly changed");
161 u->dirichletConditions()[0]=2.0;
163 Eigen::VectorXd desired=Eigen::VectorXd::Constant(
u->gSize(),2.0);
164 require((solver.
solve(
M*desired)-desired).norm()<1.e-10,
"Fixed Dirichlet solve changed");
166 u->dirichletConditions()[0]=5.0;
167 desired.setConstant(5.0);
168 require((solver.
solve(
M*desired)-desired).norm()<1.e-10,
"Solver lost instance-owned constraints");
169 require(!fe->periodicNodePairs().empty(),
"Periodic node pairing lost");
174 std::weak_ptr<const Mesh> weakMesh;
175 std::weak_ptr<Space> weakSpace;
176 std::weak_ptr<const void> weakField;
178 auto expression=[&]()
181 auto fe=std::make_shared<Space>(
mesh);
182 auto u=std::make_shared<Field>(*fe);
187 auto intermediate=2.0*
u;
188 return eval(intermediate);
190 require(!weakField.expired() && !weakSpace.expired() && !weakMesh.expired(),
"Expression failed to retain field/space/mesh");
191 const auto&
e=expression->field().fe().elements().begin()->second;
192 require(std::abs((*expression)(
e,Eigen::Vector4d::Constant(.25))(0)-12.0)<1.e-11,
"Detached expression evaluates incorrectly");
193 auto copy=expression;
194 require(std::abs((*copy)(
e,Eigen::Vector4d::Constant(.25))(0)-12.0)<1.e-11,
"Copied evaluator evaluates incorrectly");
196 require(weakField.expired() && weakSpace.expired() && weakMesh.expired(),
"Shared ownership cycle leaked FEM objects");
198 auto retainedDomain=[&]()
200 auto fe=std::make_shared<Space>(
makeMesh(root));
204 require(!weakSpace.expired(),
"Domain did not retain its FEM space");
205 require(retainedDomain.element(0).simplex.vol0>0,
"Retained domain is invalid");
207 require(weakSpace.expired(),
"Domain leaked its FEM space");
212 auto fe=std::make_shared<Space>(
makeMesh(root));
213 auto u=std::make_shared<Field>(*fe);
215 u->dirichletConditions()[0]=3.0;
216 weakField=
u->ownership();
219 rhs=
matrix(form)*
u->dofVector();
222 require(!weakField.expired() && !weakSpace.expired(),
"Solver failed to retain field state");
223 require((solver.
solve(rhs).array()-3.0).matrix().norm()<1.e-10,
"Detached solver lost constraints or DOFs");
225 require(weakField.expired() && weakSpace.expired(),
"Solver leaked its field state");
230 auto u=std::make_shared<Field>(stackSpace);
233 rejects([&]() { (*expired)(Eigen::Vector3d::Zero()); },
"Expired borrowed FEM space was dereferenced");
234 auto borrowedMesh=[&]()
237 auto fe=std::make_shared<Space>(*temporaryMesh);
238 auto u=std::make_shared<Field>(*fe);
241 rejects([&]() { (*borrowedMesh)(Eigen::Vector3d::Zero()); },
"Expired borrowed mesh was dereferenced");
245void affine(
const std::filesystem::path& root)
248 auto fe=std::make_shared<FE>(
makeMesh(root));
249 auto u=fe->template trial<3>();
250 const Eigen::Matrix3d
A=(Eigen::Matrix3d()<<1,2,3,4,5,6,7,8,9).finished();
251 for(
size_t n=0;n<fe->nodeSize();++n)
253 u->dofVector().template segment<3>(3*n)=
A*fe->node(n).P0+Eigen::Vector3d(2,3,4);
260 Eigen::Matrix<double,6,1> expectedStrain;
261 expectedStrain<<1,5,9,6,14,10;
262 for(
const auto& entry : fe->elements())
264 const auto&
e=entry.second;
265 const Eigen::Vector4d bary=Eigen::Vector4d::Constant(.25);
266 require(((*
eval(
u))(
e,bary)-
A*
e.position(bary)-Eigen::Vector3d(2,3,4)).norm()<1.e-10,
"Affine interpolation changed");
267 const Eigen::Matrix<double,9,1> expected=
A.transpose().reshaped();
268 require(((*gradient)(
e,bary)-expected).norm()<1.e-10,
"Affine gradient changed");
269 require(((*sumGradient)(
e,bary)-2*expected).norm()<1.e-10,
"Gradient of trial sum changed");
270 require(((*scaledGradient)(
e,bary)-2*expected).norm()<1.e-10,
"Gradient of scaled trial changed");
271 require(((*sumStrain)(
e,bary)-2*expectedStrain).norm()<1.e-10,
"Strain of trial sum changed");
272 require(((*strain)(
e,bary)-expectedStrain).norm()<1.e-10,
"Engineering strain changed");
274 auto dV=fe->template domain<EntireDomain,4,GaussLegendre>();
276 const double integrated=
u->dofVector().dot(energy*
u->dofVector());
277 const double exact=fe->mesh.volume()*expectedStrain.squaredNorm();
278 require(std::abs(integrated-exact)<1.e-10*exact,
"Elastic strain energy changed");