Line data Source code
1 : // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : #include "materialoperator.hpp"
5 :
6 : #include <cmath>
7 : #include <functional>
8 : #include <limits>
9 : #include "linalg/densematrix.hpp"
10 : #include "utils/communication.hpp"
11 : #include "utils/geodata.hpp"
12 : #include "utils/iodata.hpp"
13 :
14 : namespace palace
15 : {
16 :
17 : namespace internal::mat
18 : {
19 :
20 : template <std::size_t N>
21 272 : bool IsOrthonormal(const config::SymmetricMatrixData<N> &data)
22 : {
23 :
24 : // All the vectors are normalized.
25 : constexpr auto tol = 1.0e-6;
26 : auto UnitNorm = [&](const std::array<double, N> &x)
27 : {
28 : double s = -1.0;
29 3244 : for (const auto &i : x)
30 : {
31 2433 : s += std::pow(i, 2);
32 : }
33 : return std::abs(s) < tol;
34 : };
35 : bool valid = std::all_of(data.v.begin(), data.v.end(), UnitNorm);
36 :
37 : // All the vectors are orthogonal.
38 1088 : for (std::size_t i1 = 0; i1 < N; i1++)
39 : {
40 : const auto &v1 = data.v.at(i1);
41 1632 : for (std::size_t i2 = i1 + 1; i2 < N; i2++)
42 : {
43 : const auto &v2 = data.v.at(i2);
44 : double s = 0.0;
45 3264 : for (std::size_t j = 0; j < N; j++)
46 : {
47 2448 : s += v1[j] * v2[j];
48 : }
49 816 : valid &= std::abs(s) < tol;
50 : }
51 : }
52 272 : return valid;
53 : }
54 :
55 : template <std::size_t N>
56 114 : bool IsValid(const config::SymmetricMatrixData<N> &data)
57 : {
58 114 : return IsOrthonormal(data) && std::all_of(data.s.begin(), data.s.end(),
59 114 : [](auto d) { return std::abs(d) > 0.0; });
60 : }
61 :
62 : template <std::size_t N>
63 146 : bool IsIsotropic(const config::SymmetricMatrixData<N> &data)
64 : {
65 146 : return IsOrthonormal(data) &&
66 430 : std::all_of(data.s.begin(), data.s.end(), [&](auto d) { return d == data.s[0]; });
67 : }
68 :
69 : template <std::size_t N>
70 9 : bool IsIdentity(const config::SymmetricMatrixData<N> &data)
71 : {
72 9 : return IsOrthonormal(data) &&
73 9 : std::all_of(data.s.begin(), data.s.end(), [](auto d) { return d == 1.0; });
74 : }
75 :
76 : template <std::size_t N>
77 190 : mfem::DenseMatrix ToDenseMatrix(const config::SymmetricMatrixData<N> &data)
78 : {
79 190 : mfem::DenseMatrix M(N, N);
80 : mfem::Vector V(N);
81 760 : for (std::size_t i = 0; i < N; i++)
82 : {
83 2280 : for (std::size_t j = 0; j < N; j++)
84 : {
85 1710 : V(j) = data.v[i][j];
86 : }
87 570 : AddMult_a_VVt(data.s[i], V, M);
88 : }
89 190 : return M;
90 0 : }
91 :
92 : } // namespace internal::mat
93 :
94 78 : MaterialOperator::MaterialOperator(const IoData &iodata, const Mesh &mesh) : mesh(mesh)
95 : {
96 39 : SetUpMaterialProperties(iodata, mesh);
97 39 : }
98 :
99 39 : void MaterialOperator::SetUpMaterialProperties(const IoData &iodata,
100 : const mfem::ParMesh &mesh)
101 : {
102 : // Check that material attributes have been specified correctly. The mesh attributes may
103 : // be non-contiguous and when no material attribute is specified the elements are deleted
104 : // from the mesh so as to not cause problems.
105 39 : MFEM_VERIFY(!iodata.domains.materials.empty(), "Materials must be non-empty!");
106 : {
107 39 : int attr_max = mesh.attributes.Size() ? mesh.attributes.Max() : 0;
108 : mfem::Array<int> attr_marker(attr_max);
109 : attr_marker = 0;
110 78 : for (auto attr : mesh.attributes)
111 : {
112 39 : attr_marker[attr - 1] = 1;
113 : }
114 78 : for (const auto &data : iodata.domains.materials)
115 : {
116 77 : for (auto attr : data.attributes)
117 : {
118 38 : MFEM_VERIFY(
119 : attr > 0 && attr <= attr_max,
120 : "Material attribute tags must be non-negative and correspond to attributes "
121 : "in the mesh!");
122 38 : MFEM_VERIFY(attr_marker[attr - 1], "Unknown material attribute " << attr << "!");
123 : }
124 : }
125 : }
126 :
127 : // Set up material properties of the different domain regions, represented with element-
128 : // wise constant matrix-valued coefficients for the relative permeability, permittivity,
129 : // and other material properties.
130 39 : const auto &loc_attr = this->mesh.GetCeedAttributes();
131 39 : mfem::Array<int> mat_marker(iodata.domains.materials.size());
132 : mat_marker = 0;
133 : int nmats = 0;
134 78 : for (std::size_t i = 0; i < iodata.domains.materials.size(); i++)
135 : {
136 39 : const auto &data = iodata.domains.materials[i];
137 39 : for (auto attr : data.attributes)
138 : {
139 38 : if (loc_attr.find(attr) != loc_attr.end())
140 : {
141 38 : mat_marker[i] = 1;
142 38 : nmats++;
143 38 : break;
144 : }
145 : }
146 : }
147 39 : attr_mat.SetSize(loc_attr.size());
148 : attr_mat = -1;
149 :
150 39 : attr_is_isotropic.SetSize(nmats);
151 :
152 : const int sdim = mesh.SpaceDimension();
153 39 : mat_muinv.SetSize(sdim, sdim, nmats);
154 39 : mat_epsilon.SetSize(sdim, sdim, nmats);
155 39 : mat_epsilon_imag.SetSize(sdim, sdim, nmats);
156 39 : mat_epsilon_abs.SetSize(sdim, sdim, nmats);
157 39 : mat_invz0.SetSize(sdim, sdim, nmats);
158 39 : mat_c0.SetSize(sdim, sdim, nmats);
159 39 : mat_sigma.SetSize(sdim, sdim, nmats);
160 39 : mat_invLondon.SetSize(sdim, sdim, nmats);
161 39 : mat_c0_min.SetSize(nmats);
162 39 : mat_c0_max.SetSize(nmats);
163 39 : mat_muinvkx.SetSize(sdim, sdim, nmats);
164 39 : mat_kxTmuinvkx.SetSize(sdim, sdim, nmats);
165 39 : mat_kx.SetSize(sdim, sdim, nmats);
166 39 : has_losstan_attr = has_conductivity_attr = has_london_attr = has_wave_attr = false;
167 :
168 : // Set up Floquet wave vector for periodic meshes with phase-delay constraints.
169 39 : SetUpFloquetWaveVector(iodata, mesh);
170 :
171 : int count = 0;
172 78 : for (std::size_t i = 0; i < iodata.domains.materials.size(); i++)
173 : {
174 39 : if (!mat_marker[i])
175 : {
176 1 : continue;
177 : }
178 : const auto &data = iodata.domains.materials[i];
179 38 : if (iodata.problem.type == ProblemType::ELECTROSTATIC)
180 : {
181 3 : MFEM_VERIFY(internal::mat::IsValid(data.epsilon_r),
182 : "Material has no valid permittivity defined!");
183 3 : if (!internal::mat::IsIdentity(data.mu_r) || internal::mat::IsValid(data.sigma) ||
184 3 : std::abs(data.lambda_L) > 0.0)
185 : {
186 0 : Mpi::Warning(
187 : "Electrostatic problem type does not account for material permeability,\n"
188 : "electrical conductivity, or London depth!\n");
189 : }
190 : }
191 35 : else if (iodata.problem.type == ProblemType::MAGNETOSTATIC)
192 : {
193 3 : MFEM_VERIFY(internal::mat::IsValid(data.mu_r),
194 : "Material has no valid permeability defined!");
195 6 : if (!internal::mat::IsIdentity(data.epsilon_r) ||
196 6 : internal::mat::IsValid(data.tandelta) || internal::mat::IsValid(data.sigma) ||
197 3 : std::abs(data.lambda_L) > 0.0)
198 : {
199 0 : Mpi::Warning(
200 : "Magnetostatic problem type does not account for material permittivity,\n"
201 : "loss tangent, electrical conductivity, or London depth!\n");
202 : }
203 : }
204 : else
205 : {
206 32 : MFEM_VERIFY(internal::mat::IsValid(data.mu_r) &&
207 : internal::mat::IsValid(data.epsilon_r),
208 : "Material has no valid permeability or no valid permittivity defined!");
209 32 : if (iodata.problem.type == ProblemType::TRANSIENT)
210 : {
211 3 : MFEM_VERIFY(!internal::mat::IsValid(data.tandelta),
212 : "Transient problem type does not support material loss tangent, use "
213 : "electrical conductivity instead!");
214 : }
215 : else
216 : {
217 29 : MFEM_VERIFY(
218 : !(internal::mat::IsValid(data.tandelta) && internal::mat::IsValid(data.sigma)),
219 : "Material loss model should probably use only one of loss tangent or "
220 : "electrical conductivity!");
221 : }
222 : }
223 :
224 112 : attr_is_isotropic[i] = internal::mat::IsIsotropic(data.mu_r) &&
225 71 : internal::mat::IsIsotropic(data.epsilon_r) &&
226 107 : internal::mat::IsIsotropic(data.tandelta) &&
227 34 : internal::mat::IsIsotropic(data.sigma);
228 :
229 : // Map all attributes to this material property index.
230 76 : for (auto attr : data.attributes)
231 : {
232 : auto it = loc_attr.find(attr);
233 38 : if (it != loc_attr.end())
234 : {
235 38 : MFEM_VERIFY(
236 : attr_mat[it->second - 1] < 0,
237 : "Detected multiple definitions of material properties for domain attribute "
238 : << attr << "!");
239 38 : attr_mat[it->second - 1] = count;
240 : }
241 : }
242 :
243 : // Compute the inverse of the input permeability matrix.
244 38 : mfem::DenseMatrix mat_mu = internal::mat::ToDenseMatrix(data.mu_r);
245 76 : mfem::DenseMatrixInverse(mat_mu, true).GetInverseMatrix(mat_muinv(count));
246 :
247 : // Material permittivity: Re{ε} = ε, Im{ε} = -ε * tan(δ)
248 38 : mfem::DenseMatrix T(sdim, sdim);
249 76 : mat_epsilon(count) = internal::mat::ToDenseMatrix(data.epsilon_r);
250 76 : Mult(mat_epsilon(count), internal::mat::ToDenseMatrix(data.tandelta), T);
251 38 : T *= -1.0;
252 38 : mat_epsilon_imag(count) = T;
253 38 : if (mat_epsilon_imag(count).MaxMaxNorm() > 0.0)
254 : {
255 1 : has_losstan_attr = true;
256 : }
257 :
258 : // ε * √(I + tan(δ) * tan(δ)ᵀ)
259 38 : MultAAt(internal::mat::ToDenseMatrix(data.tandelta), T);
260 152 : for (int d = 0; d < T.Height(); d++)
261 : {
262 114 : T(d, d) += 1.0;
263 : }
264 76 : Mult(mat_epsilon(count), linalg::MatrixSqrt(T), mat_epsilon_abs(count));
265 :
266 : // √(μ⁻¹ ε)
267 38 : Mult(mat_muinv(count), mat_epsilon(count), mat_invz0(count));
268 76 : mat_invz0(count) = linalg::MatrixSqrt(mat_invz0(count));
269 :
270 : // √((μ ε)⁻¹)
271 38 : Mult(mat_mu, mat_epsilon(count), T);
272 76 : mat_c0(count) = linalg::MatrixPow(T, -0.5);
273 38 : mat_c0_min[count] = linalg::SingularValueMin(mat_c0(count));
274 38 : mat_c0_max[count] = linalg::SingularValueMax(mat_c0(count));
275 :
276 : // Electrical conductivity, σ
277 76 : mat_sigma(count) = internal::mat::ToDenseMatrix(data.sigma);
278 38 : if (mat_sigma(count).MaxMaxNorm() > 0.0)
279 : {
280 1 : has_conductivity_attr = true;
281 : }
282 :
283 : // λ⁻² * μ⁻¹
284 38 : mat_invLondon(count) = mat_muinv(count);
285 : mat_invLondon(count) *=
286 76 : std::abs(data.lambda_L) > 0.0 ? std::pow(data.lambda_L, -2.0) : 0.0;
287 38 : if (mat_invLondon(count).MaxMaxNorm() > 0.0)
288 : {
289 0 : has_london_attr = true;
290 : }
291 :
292 : // μ⁻¹ [k x]
293 38 : Mult(mat_muinv(count), wave_vector_cross, mat_muinvkx(count));
294 :
295 : // [k x]^T μ⁻¹ [k x]
296 38 : T.Transpose(wave_vector_cross);
297 38 : Mult(T, mat_muinvkx(count), mat_kxTmuinvkx(count));
298 :
299 : // [k x]
300 38 : mat_kx(count) = wave_vector_cross;
301 :
302 38 : count++;
303 38 : }
304 39 : bool has_attr[4] = {has_losstan_attr, has_conductivity_attr, has_london_attr,
305 39 : has_wave_attr};
306 : Mpi::GlobalOr(4, has_attr, mesh.GetComm());
307 39 : has_losstan_attr = has_attr[0];
308 39 : has_conductivity_attr = has_attr[1];
309 39 : has_london_attr = has_attr[2];
310 39 : has_wave_attr = has_attr[3];
311 39 : }
312 :
313 39 : void MaterialOperator::SetUpFloquetWaveVector(const IoData &iodata,
314 : const mfem::ParMesh &mesh)
315 : {
316 : const int sdim = mesh.SpaceDimension();
317 : const double tol = std::numeric_limits<double>::epsilon();
318 :
319 : // Get Floquet wave vector.
320 : mfem::Vector wave_vector(sdim);
321 39 : wave_vector = 0.0;
322 : const auto &data = iodata.boundaries.periodic;
323 39 : MFEM_VERIFY(static_cast<int>(data.wave_vector.size()) == sdim,
324 : "Floquet wave vector size must equal the spatial dimension.");
325 : std::copy(data.wave_vector.begin(), data.wave_vector.end(), wave_vector.GetData());
326 39 : has_wave_attr = (wave_vector.Norml2() > tol);
327 :
328 39 : MFEM_VERIFY(!has_wave_attr || iodata.problem.type == ProblemType::DRIVEN ||
329 : iodata.problem.type == ProblemType::EIGENMODE,
330 : "Quasi-periodic Floquet boundary conditions are only available for "
331 : " frequency domain driven or eigenmode simulations!");
332 : MFEM_VERIFY(!has_wave_attr || sdim == 3,
333 : "Quasi-periodic Floquet periodic boundary conditions are only available "
334 : " in 3D!");
335 :
336 : // Get mesh dimensions in x/y/z coordinates.
337 : mfem::Vector bbmin, bbmax;
338 39 : mesh::GetAxisAlignedBoundingBox(mesh, bbmin, bbmax);
339 39 : bbmax -= bbmin;
340 :
341 : // Ensure Floquet wave vector components are in range [-π/L, π/L].
342 156 : for (int i = 0; i < sdim; i++)
343 : {
344 117 : if (wave_vector[i] > M_PI / bbmax[i])
345 : {
346 0 : wave_vector[i] =
347 0 : -M_PI / bbmax[i] + fmod(wave_vector[i] + M_PI / bbmax[i], 2 * M_PI / bbmax[i]);
348 : }
349 117 : else if (wave_vector[i] < M_PI / bbmax[i])
350 : {
351 117 : wave_vector[i] =
352 117 : M_PI / bbmax[i] + fmod(wave_vector[i] - M_PI / bbmax[i], 2 * M_PI / bbmax[i]);
353 : }
354 : }
355 :
356 : // Matrix representation of cross product with wave vector
357 : // [k x] = | 0 -k3 k2|
358 : // | k3 0 -k1|
359 : // |-k2 k1 0 |
360 39 : wave_vector_cross.SetSize(3);
361 39 : wave_vector_cross = 0.0;
362 39 : wave_vector_cross(0, 1) = -wave_vector[2];
363 39 : wave_vector_cross(0, 2) = wave_vector[1];
364 39 : wave_vector_cross(1, 0) = wave_vector[2];
365 39 : wave_vector_cross(1, 2) = -wave_vector[0];
366 39 : wave_vector_cross(2, 0) = -wave_vector[1];
367 39 : wave_vector_cross(2, 1) = wave_vector[0];
368 39 : }
369 :
370 0 : mfem::Array<int> MaterialOperator::GetBdrAttributeToMaterial() const
371 : {
372 : // Construct map from all (contiguous) local libCEED boundary attributes to the material
373 : // index in the neighboring element.
374 0 : mfem::Array<int> bdr_attr_mat(mesh.MaxCeedBdrAttribute());
375 : bdr_attr_mat = -1;
376 0 : for (const auto &[attr, bdr_attr_map] : mesh.GetCeedBdrAttributes())
377 : {
378 0 : for (auto it = bdr_attr_map.begin(); it != bdr_attr_map.end(); ++it)
379 : {
380 : MFEM_ASSERT(it->second > 0 && it->second <= bdr_attr_mat.Size(),
381 : "Invalid libCEED boundary attribute " << it->second << "!");
382 0 : bdr_attr_mat[it->second - 1] = AttrToMat(it->first);
383 : }
384 : }
385 0 : return bdr_attr_mat;
386 : }
387 :
388 3684 : MaterialPropertyCoefficient::MaterialPropertyCoefficient(int attr_max)
389 : {
390 3684 : attr_mat.SetSize(attr_max);
391 : attr_mat = -1;
392 3684 : }
393 :
394 7432 : MaterialPropertyCoefficient::MaterialPropertyCoefficient(
395 7432 : const mfem::Array<int> &attr_mat_, const mfem::DenseTensor &mat_coeff_, double a)
396 7432 : : attr_mat(attr_mat_), mat_coeff(mat_coeff_)
397 : {
398 7432 : *this *= a;
399 7432 : }
400 :
401 : namespace
402 : {
403 :
404 0 : void UpdateProperty(mfem::DenseTensor &mat_coeff, int k, double coeff, double a)
405 : {
406 : // Constant diagonal coefficient.
407 0 : if (mat_coeff.SizeI() == 0 && mat_coeff.SizeJ() == 0)
408 : {
409 : // Initialize the coefficient material properties.
410 0 : MFEM_VERIFY(k == 0 && mat_coeff.SizeK() == 1,
411 : "Unexpected initial size for MaterialPropertyCoefficient!");
412 0 : mat_coeff.SetSize(1, 1, mat_coeff.SizeK());
413 0 : mat_coeff(0, 0, k) = a * coeff;
414 : }
415 : else
416 : {
417 0 : MFEM_VERIFY(mat_coeff.SizeI() == mat_coeff.SizeJ(),
418 : "Invalid dimensions for MaterialPropertyCoefficient update!");
419 0 : for (int i = 0; i < mat_coeff.SizeI(); i++)
420 : {
421 0 : mat_coeff(i, i, k) += a * coeff;
422 : }
423 : }
424 0 : }
425 :
426 0 : void UpdateProperty(mfem::DenseTensor &mat_coeff, int k, const mfem::DenseMatrix &coeff,
427 : double a)
428 : {
429 0 : if (mat_coeff.SizeI() == 0 && mat_coeff.SizeJ() == 0)
430 : {
431 : // Initialize the coefficient material properties.
432 0 : MFEM_VERIFY(k == 0 && mat_coeff.SizeK() == 1,
433 : "Unexpected initial size for MaterialPropertyCoefficient!");
434 0 : mat_coeff.SetSize(coeff.Height(), coeff.Width(), mat_coeff.SizeK());
435 0 : mat_coeff(k).Set(a, coeff);
436 : }
437 0 : else if (coeff.Height() == mat_coeff.SizeI() && coeff.Width() == mat_coeff.SizeJ())
438 : {
439 : // Add as full matrix.
440 0 : mat_coeff(k).Add(a, coeff);
441 : }
442 0 : else if (coeff.Height() == 1 && coeff.Width() == 1)
443 : {
444 : // Add as diagonal.
445 0 : UpdateProperty(mat_coeff, k, coeff(0, 0), a);
446 : }
447 0 : else if (mat_coeff.SizeI() == 1 && mat_coeff.SizeJ() == 1)
448 : {
449 : // Convert to matrix coefficient and previous data add as diagonal.
450 0 : mfem::DenseTensor mat_coeff_scalar(mat_coeff);
451 0 : mat_coeff.SetSize(coeff.Height(), coeff.Width(), mat_coeff_scalar.SizeK());
452 0 : mat_coeff = 0.0;
453 0 : for (int l = 0; l < mat_coeff.SizeK(); l++)
454 : {
455 0 : UpdateProperty(mat_coeff, l, mat_coeff_scalar(0, 0, l), 1.0);
456 : }
457 0 : mat_coeff(k).Add(a, coeff);
458 : }
459 : else
460 : {
461 0 : MFEM_ABORT("Invalid dimensions when updating material property at index " << k << "!");
462 : }
463 0 : }
464 :
465 0 : bool Equals(const mfem::DenseMatrix &mat_coeff, double coeff, double a)
466 : {
467 0 : MFEM_VERIFY(mat_coeff.Height() == mat_coeff.Width(),
468 : "Invalid dimensions for MaterialPropertyCoefficient update!");
469 : constexpr double tol = 1.0e-9;
470 0 : for (int i = 0; i < mat_coeff.Height(); i++)
471 : {
472 0 : if (std::abs(mat_coeff(i, i) - a * coeff) >= tol * std::abs(mat_coeff(i, i)))
473 : {
474 : return false;
475 : }
476 0 : for (int j = 0; j < mat_coeff.Width(); j++)
477 : {
478 0 : if (j != i && std::abs(mat_coeff(i, j)) > 0.0)
479 : {
480 : return false;
481 : }
482 : }
483 : }
484 : return true;
485 : }
486 :
487 0 : bool Equals(const mfem::DenseMatrix &mat_coeff, const mfem::DenseMatrix &coeff, double a)
488 : {
489 0 : if (coeff.Height() == 1 && coeff.Width() == 1)
490 : {
491 0 : return Equals(mat_coeff, coeff(0, 0), a);
492 : }
493 : else
494 : {
495 : constexpr double tol = 1.0e-9;
496 0 : mfem::DenseMatrix T(mat_coeff);
497 0 : T.Add(-a, coeff);
498 0 : return (T.MaxMaxNorm() < tol * mat_coeff.MaxMaxNorm());
499 0 : }
500 : }
501 :
502 : } // namespace
503 :
504 12 : void MaterialPropertyCoefficient::AddCoefficient(const mfem::Array<int> &attr_mat_,
505 : const mfem::DenseTensor &mat_coeff_,
506 : double a)
507 : {
508 12 : if (empty())
509 : {
510 12 : MFEM_VERIFY(attr_mat_.Size() == attr_mat.Size(),
511 : "Invalid resize of attribute to material property map in "
512 : "MaterialPropertyCoefficient::AddCoefficient!");
513 12 : attr_mat = attr_mat_;
514 12 : mat_coeff = mat_coeff_;
515 12 : *this *= a;
516 : }
517 0 : else if (attr_mat_ == attr_mat)
518 : {
519 0 : MFEM_VERIFY(mat_coeff_.SizeK() == mat_coeff.SizeK(),
520 : "Invalid dimensions for MaterialPropertyCoefficient::AddCoefficient!");
521 0 : for (int k = 0; k < mat_coeff.SizeK(); k++)
522 : {
523 0 : UpdateProperty(mat_coeff, k, mat_coeff_(k), a);
524 : }
525 : }
526 : else
527 : {
528 0 : for (int k = 0; k < mat_coeff_.SizeK(); k++)
529 : {
530 : // Get list of all attributes which use this material property.
531 : mfem::Array<int> attr_list;
532 : attr_list.Reserve(attr_mat_.Size());
533 0 : for (int i = 0; i < attr_mat_.Size(); i++)
534 : {
535 0 : if (attr_mat_[i] == k)
536 : {
537 0 : attr_list.Append(i + 1);
538 : }
539 : }
540 :
541 : // Add or update the material property.
542 0 : AddMaterialProperty(attr_list, mat_coeff_(k), a);
543 : }
544 : }
545 12 : }
546 :
547 : template <typename T>
548 0 : void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array<int> &attr_list,
549 : const T &coeff, double a)
550 : {
551 : // Preprocess the attribute list. If any of the given attributes already have material
552 : // properties assigned, then they all need to point to the same material and it is
553 : // updated in place. Otherwise a new material is added for these attributes.
554 0 : if (attr_list.Size() == 0)
555 : {
556 : // No attributes, nothing to add.
557 : return;
558 : }
559 :
560 : int mat_idx = -1;
561 0 : for (auto attr : attr_list)
562 : {
563 0 : MFEM_VERIFY(attr <= attr_mat.Size(),
564 : "Out of bounds access for attribute "
565 : << attr << " in MaterialPropertyCoefficient::AddMaterialProperty!");
566 0 : if (mat_idx < 0)
567 : {
568 0 : mat_idx = attr_mat[attr - 1];
569 : }
570 : else
571 : {
572 0 : MFEM_VERIFY(mat_idx == attr_mat[attr - 1],
573 : "All attributes for MaterialPropertyCoefficient::AddMaterialProperty "
574 : "must correspond to the same "
575 : "existing material if it exists!");
576 : }
577 : }
578 :
579 0 : if (mat_idx < 0)
580 : {
581 : // Check if we can reuse an existing material.
582 0 : for (int k = 0; k < mat_coeff.SizeK(); k++)
583 : {
584 0 : if (Equals(mat_coeff(k), coeff, a))
585 : {
586 : mat_idx = k;
587 : break;
588 : }
589 : }
590 0 : if (mat_idx < 0)
591 : {
592 : // Append a new material and assign the attributes to it.
593 0 : const mfem::DenseTensor mat_coeff_backup(mat_coeff);
594 0 : mat_coeff.SetSize(mat_coeff_backup.SizeI(), mat_coeff_backup.SizeJ(),
595 : mat_coeff_backup.SizeK() + 1);
596 0 : for (int k = 0; k < mat_coeff_backup.SizeK(); k++)
597 : {
598 0 : mat_coeff(k) = mat_coeff_backup(k);
599 : }
600 0 : mat_idx = mat_coeff.SizeK() - 1;
601 : }
602 0 : mat_coeff(mat_idx) = 0.0; // Zero out so we can add
603 :
604 : // Assign all attributes to this new material.
605 0 : for (auto attr : attr_list)
606 : {
607 0 : attr_mat[attr - 1] = mat_idx;
608 : }
609 : }
610 0 : UpdateProperty(mat_coeff, mat_idx, coeff, a);
611 : }
612 :
613 7444 : MaterialPropertyCoefficient &MaterialPropertyCoefficient::operator*=(double a)
614 : {
615 37112 : for (int k = 0; k < mat_coeff.SizeK(); k++)
616 : {
617 29668 : mat_coeff(k) *= a;
618 : }
619 7444 : return *this;
620 : }
621 :
622 0 : void MaterialPropertyCoefficient::RestrictCoefficient(const mfem::Array<int> &attr_list)
623 : {
624 : // Create a new material property coefficient with materials corresponding to only the
625 : // unique ones in the given attribute list.
626 0 : const mfem::Array<int> attr_mat_orig(attr_mat);
627 0 : const mfem::DenseTensor mat_coeff_orig(mat_coeff);
628 : attr_mat = -1;
629 0 : mat_coeff.SetSize(mat_coeff_orig.SizeI(), mat_coeff_orig.SizeJ(), 0);
630 0 : for (auto attr : attr_list)
631 : {
632 0 : if (attr_mat[attr - 1] >= 0)
633 : {
634 : // Attribute has already been processed.
635 0 : continue;
636 : }
637 :
638 : // Find all attributes in restricted list of attributes which map to this material index
639 : // and process them together.
640 0 : const int orig_mat_idx = attr_mat_orig[attr - 1];
641 : const int new_mat_idx = mat_coeff.SizeK();
642 0 : for (auto attr2 : attr_list)
643 : {
644 0 : if (attr_mat_orig[attr2 - 1] == orig_mat_idx)
645 : {
646 0 : attr_mat[attr2 - 1] = new_mat_idx;
647 : }
648 : }
649 :
650 : // Append the new material property.
651 0 : const mfem::DenseTensor mat_coeff_backup(mat_coeff);
652 0 : mat_coeff.SetSize(mat_coeff_backup.SizeI(), mat_coeff_backup.SizeJ(),
653 : mat_coeff_backup.SizeK() + 1);
654 0 : for (int k = 0; k < mat_coeff_backup.SizeK(); k++)
655 : {
656 0 : mat_coeff(k) = mat_coeff_backup(k);
657 : }
658 0 : mat_coeff(new_mat_idx) = mat_coeff_orig(orig_mat_idx);
659 : }
660 0 : }
661 :
662 0 : void MaterialPropertyCoefficient::NormalProjectedCoefficient(const mfem::Vector &normal)
663 : {
664 0 : mfem::DenseTensor mat_coeff_backup(mat_coeff);
665 0 : mat_coeff.SetSize(1, 1, mat_coeff_backup.SizeK());
666 0 : for (int k = 0; k < mat_coeff.SizeK(); k++)
667 : {
668 0 : mat_coeff(k) = mat_coeff_backup(k).InnerProduct(normal, normal);
669 : }
670 0 : }
671 :
672 : template void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array<int> &,
673 : const mfem::DenseMatrix &,
674 : double);
675 : template void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array<int> &,
676 : const double &, double);
677 :
678 : // Explicit template instantiations for internal::mat functions.
679 : template bool internal::mat::IsOrthonormal(const config::SymmetricMatrixData<3> &);
680 : template bool internal::mat::IsValid(const config::SymmetricMatrixData<3> &);
681 : template bool internal::mat::IsIsotropic(const config::SymmetricMatrixData<3> &);
682 : template bool internal::mat::IsIdentity(const config::SymmetricMatrixData<3> &);
683 :
684 : } // namespace palace
|