57
78
_initialized = true;
59
80
// ==========================================================================
60
// part 2) piola transform
81
// part 2) axisymmetric coord system handler
61
82
// ==========================================================================
62
83
template<class T, class M>
64
form_element_rep<T,M>::det_jacobian_piola_transformation (const tensor& DF, size_type map_dim) const
85
form_element_rep<T,M>::weight_coordinate_system (
87
const std::vector<size_type>& dis_inod,
66
if (coordinate_dimension() == map_dim) {
67
return DF.determinant (map_dim);
69
/* surface jacobian: references:
70
* Spectral/hp element methods for CFD
71
* G. E. M. Karniadakis and S. J. Sherwin
72
* Oxford university press
78
case 1: return norm(DF.col(0));
79
case 2: return norm(vect(DF.col(0), DF.col(1)));
81
error_macro ("det_jacobian_piola_transformation: unsupported element dimension "
82
<< map_dim << " in " << coordinate_dimension() << "D mesh.");
90
if (coordinate_system() == space_constant::cartesian || ! use_coordinate_system_weight()) {
93
point_basic<T> xq = rheolef::piola_transformation (_omega, _piola_table, K, dis_inod, q);
94
switch (coordinate_system()) {
95
case space_constant::axisymmetric_rz:
96
return use_coordinate_system_dual_weight() ? 1/xq[0] : xq[0];
97
case space_constant::axisymmetric_zr:
98
return use_coordinate_system_dual_weight() ? 1/xq[1] : xq[1];
100
fatal_macro ("unsupported coordinate system `"
101
<< space_constant::coordinate_system_name (coordinate_system()) << "'");
86
// ----------------------------------------------------------
88
// ----------------------------------------------------------
89
template<class T, class M>
91
form_element_rep<T,M>::jacobian_piola_transformation (const geo_element& K, size_type q, tensor& DF) const
93
size_type nt = get_p1_transformation().size(K);
94
check_macro (nt == K.size(), "unsupported transformation " << get_p1_transformation().name());
96
basis_on_quadrature::const_iterator_grad grad_tr
97
= _tr_p1_table.begin_grad (K, q);
98
size_type coord_d = coordinate_dimension();
99
for (size_type k = 0; k < nt; k++, grad_tr++)
100
my_cumul_otimes (DF, dis_vertex(K[k]), *grad_tr, coord_d, K.dimension());
102
// The pseudo inverse extend inv(DF) for face in 3d or edge in 2d
103
// i.e. usefull for Laplacian-Beltrami and others surfacic forms.
105
// pinvDF (hat_xq) = inv DF, if tetra in 3d, tri in 2d, etc
106
// = pseudo-invese, when tri in 3d, edge in 2 or 3d
107
// e.g. on 3d face : pinvDF*DF = [1, 0, 0; 0, 1, 0; 0, 0, 0]
109
// let DF = [u, v, w], where u, v, w are the column vectors of DF
110
// then det(DF) = mixt(u,v,w)
111
// and det(DF)*inv(DF)^T = [v^w, w^u, u^v] where u^v = vect(u,v)
114
// if K=triangle(a,b,c) then u=ab=b-a, v=ac=c-a and w = n = u^v/|u^v|.
115
// Thus DF = [ab,ac,n] and det(DF)=|ab^ac|
116
// and inv(DF)^T = [ac^n/|ab^ac|, -ab^n/|ab^ac|, n]
117
// The pseudo-inverse is obtained by remplacing the last column n by zero.
119
template<class T, class M>
121
form_element_rep<T,M>::pseudo_inverse_jacobian_piola_transformation (
123
size_type map_dim) const
125
size_t d = coordinate_dimension();
127
return inv(DF, map_dim);
131
case 0: { // point in 1D
135
case 1: { // segment in 2D
137
invDF.set_row (t/norm2(t), 0, d);
141
point t0 = DF.col(0);
142
point t1 = DF.col(1);
143
point n = vect(t0,t1);
145
point v0 = vect(t1,n)/det2;
146
point v1 = - vect(t0,n)/det2;
147
invDF.set_row (v0, 0, d);
148
invDF.set_row (v1, 1, d);
152
error_macro ("pseudo_inverse_jacobian_piola_transformation: unsupported element dimension "
153
<< map_dim << " in " << coordinate_dimension() << "D mesh.");
157
106
// ==========================================================================
158
107
// part 3) some few algebra on elementary matrices
242
194
for (size_type q = 0; first != last; first++, q++) {
245
wq *= is_weighted() ? weight(K,q,weight_i_comp) : T(1);
246
wq *= weight_coordinate_system (K, q);
248
jacobian_piola_transformation (K, q, DF);
249
wq *= det_jacobian_piola_transformation (DF, K.dimension());
196
wq *= is_weighted() ? weight(K,q) : T(1);
197
wq *= weight_coordinate_system (K, dis_inod, q);
198
rheolef::jacobian_piola_transformation (_omega, _piola_table, K, dis_inod, q, DF);
199
wq *= rheolef::det_jacobian_piola_transformation (DF, coordinate_dimension(), K.dimension());
250
200
wq *= (*first).w;
202
const point& hat_xq = (*first).x;
203
cerr << setprecision(15)
204
<< "DF(hat_x"<<q<<"="; hat_xq.put (cerr,2); cerr << ")="; DF.put(cerr,2); cerr << endl;
251
206
cumul_otimes (m, wq,
252
207
_by_table.begin (hat_K, q), _by_table.end (hat_K, q),
253
208
_bx_table.begin (hat_K, q), _bx_table.end (hat_K, q));
211
// --------------------------------------------------------------------
212
// general local mass matrix : multi-component spaces and weight
213
// --------------------------------------------------------------------
214
template<class T, class M>
216
form_element_rep<T,M>::build_general_mass (const geo_element& K, ublas::matrix<T>& m) const
218
space_constant::valued_type space_valued = get_first_space().valued_tag();
219
if (space_valued == space_constant::scalar) {
220
build_scalar_mass (K, m);
223
// space is vectorial or tensorial: weight could be scalar, tensor or tensor4
224
size_type n_comp = get_first_space().size();
225
size_type loc_n1dof = get_second_basis().size(K);
226
size_type loc_n2dof = get_first_basis().size(K);
227
m.resize (n_comp*loc_n1dof, n_comp*loc_n2dof);
229
space_constant::valued_type weight_valued = space_constant::scalar;
231
weight_valued = _wh.get_space().get_constitution().valued_tag();
233
if (weight_valued == space_constant::scalar) {
234
// --------------------------------------------------------------------------
235
// all components have the same weight and the local matrix is block-diagonal
236
// --------------------------------------------------------------------------
237
ublas::matrix<T> m_ij;
238
build_scalar_mass (K, m_ij);
239
switch (space_valued) {
240
case space_constant::scalar:
241
case space_constant::vector: {
242
for (size_type i_comp = 0; i_comp < n_comp; i_comp++) {
243
mr_set (m, ublas::range(i_comp*loc_n1dof,(i_comp+1)*loc_n1dof), ublas::range(i_comp*loc_n2dof,(i_comp+1)*loc_n2dof), m_ij);
247
case space_constant::tensor: {
248
// symmetric tensor => 2 factor for non-diagonal subscripts
249
for (size_type ij_comp = 0; ij_comp < n_comp; ij_comp++) {
250
space_constant::coordinate_type sys_coord = coordinate_system();
251
std::pair<size_type,size_type> ij = space_constant::tensor_subscript (space_valued, sys_coord, ij_comp);
252
size_t i_comp = ij.first;
253
size_t j_comp = ij.second;
254
T factor_ij = ((i_comp == j_comp) ? 1 : 2); // symmetry => multiplicity factor: when i_comp != j_comp : 2*w_ij
255
mr_set (m, ublas::range(ij_comp*loc_n1dof,(ij_comp+1)*loc_n1dof), ublas::range(ij_comp*loc_n2dof,(ij_comp+1)*loc_n2dof), factor_ij*m_ij);
260
error_macro ("unexpected `" << space_constant::valued_name(space_valued) << "'-valued space for the `mass' form.");
263
} else { // weight_valued != fem_helper::scalar
264
// ------------------------------------------------------------------------------
265
// components have different weight : the local matrix is no more block-diagonal
266
// ------------------------------------------------------------------------------
268
switch (space_valued) {
269
case fem_helper::vectorial: {
270
// first case : vector space and tensor weight
271
// m(u,v) = int_Omega w_ij u_j v_i dx
273
ublas::matrix<T> m_ij;
274
for (size_type i_comp = 0; i_comp < n_comp; i_comp++) {
275
for (size_type j_comp = 0; j_comp < n_comp; j_comp++) {
276
size_type ij_comp = fem_helper::tensor_index (weight_valued, sys_coord, i_comp, j_comp);
277
build_scalar_mass (K, m_ij, ij_comp);
278
mr_set (m, ublas::range(i_comp*loc_n1dof,(i_comp+1)*loc_n1dof), ublas::range(j_comp*loc_n2dof,(j_comp+1)*loc_n2dof), m_ij);
283
case fem_helper::tensorial: {
284
// second case : tensor space and tensor4 weight
285
// m(tau,gamma) = int_Omega w_ijkl tau_kj gamma_ij dx
286
// and w_ijkl = w_jikl, w_ijkl = w_ijlk and w_ijkl = w_klij
287
// symmetry => multiplicity factor: 4 when i!=j and k!=l or 2 when i!=j xor k!=l
288
ublas::matrix<T> m_ijkl;
289
for (size_type ij_comp = 0; ij_comp < n_comp; ij_comp++) {
290
fem_helper::pair_size_type ij = fem_helper::tensor_subscript (space_valued, sys_coord, ij_comp);
292
size_t j = ij.second;
293
T factor_ij = ((i == j) ? 1 : 2);
294
for (size_type kl_comp = 0; kl_comp < n_comp; kl_comp++) {
295
fem_helper::pair_size_type kl = fem_helper::tensor_subscript (space_valued, sys_coord, kl_comp);
297
size_t l = kl.second;
298
T factor_kl = ((k == l) ? 1 : 2);
299
T factor_ijkl = factor_ij*factor_kl;
300
size_type ijkl_comp = fem_helper::tensor4_index (weight_valued, sys_coord, i, j, k, l);
301
build_scalar_mass (K, m_ijkl, ijkl_comp);
302
mr_set (m, ublas::range(ij_comp*loc_n1dof,(ij_comp+1)*loc_n1dof), ublas::range(kl_comp*loc_n2dof,(kl_comp+1)*loc_n2dof), factor_ijkl*m_ijkl);
308
error_macro ("unexpected " << fem_helper::valued_field_name(space_valued) << "-valued space and `"
309
<< fem_helper::valued_field_name(weight_valued)
310
<< "'-valued weight for the `mass' form.");
314
error_macro ("mass: multi-component with non-scalar weight: not yet");
256
317
// ----------------------------------------------------------
257
318
// elementary grad_grad form:
258
319
// --------------------------------------------------------------------
273
337
for (size_type q = 0; first != last; first++, q++) {
274
jacobian_piola_transformation (K, q, DF);
275
invDF = pseudo_inverse_jacobian_piola_transformation (DF, map_d);
338
rheolef::jacobian_piola_transformation (_omega, _piola_table, K, dis_inod, q, DF);
339
invDF = pseudo_inverse_jacobian_piola_transformation (DF, coordinate_dimension(), map_d);
278
wq *= weight_coordinate_system (K, q);
280
wq *= det_jacobian_piola_transformation (DF, K.dimension());
341
wq *= weight_coordinate_system (K, dis_inod, q);
342
wq *= rheolef::det_jacobian_piola_transformation (DF, coordinate_dimension(), K.dimension());
281
343
wq *= (*first).w;
283
344
if (scalar_diffusion) {
284
345
wq *= (is_weighted() ? weight(K,q) : T(1));
286
346
Dw = wq*invDF*trans(invDF);
288
347
} else { // tensorial diffusion
348
error_macro ("tensorial diffusion: not yet");
289
350
weight (K, q, W);
290
351
Dw = wq*invDF*W*trans(invDF);
293
354
cumul_otimes (a, Dw,
294
355
_by_table.begin_grad (hat_K, q), _by_table.end_grad (hat_K, q),
295
356
_bx_table.begin_grad (hat_K, q), _bx_table.end_grad (hat_K, q),
360
// --------------------------------------------------------------------
362
// Mij += trans(grad_phi_i) * Dw * grad_psi_j
363
// --------------------------------------------------------------------
364
template<class T, class M>
366
form_element_rep<T,M>::build_d_dx (const geo_element& K, ublas::matrix<T>& m, size_type i_comp) const
368
check_macro (get_first_basis().family() == element_constant::Lagrange &&
369
get_second_basis().family() == element_constant::Lagrange,
370
"Only Lagrange elements supported");
371
size_type map_d = K.dimension();
372
check_macro (i_comp < map_d, "unexpected `d_dx" << i_comp << "' on element type "
373
<< "`" << K.name() << "'");
374
check_macro (coordinate_dimension() == K.dimension(),
375
"unsupported `d_dx" << i_comp << "' form on element type `" << K.name()
376
<< " in " << coordinate_dimension() << "D geometry");
377
reference_element hat_K = K;
378
m.resize (get_second_basis().size(hat_K), get_first_basis().size(hat_K));
380
std::vector<size_type> dis_inod;
381
_omega.dis_inod (K, dis_inod);
382
quadrature::const_iterator first = _quad.begin(hat_K);
383
quadrature::const_iterator last = _quad.end (hat_K);
386
for (size_type q = 0; first != last; first++, q++) {
387
rheolef::jacobian_piola_transformation (_omega, _piola_table, K, dis_inod, q, DF);
388
invDF = pseudo_inverse_jacobian_piola_transformation (DF, coordinate_dimension(), map_d);
389
invDF = inv(DF,map_d);
390
T wq = is_weighted() ? weight(K,q) : T(1);
391
wq *= weight_coordinate_system (K, dis_inod, q);
392
wq *= rheolef::det_jacobian_piola_transformation (DF, coordinate_dimension(), K.dimension());
394
point invDFt_icomp_wq = invDF.col(i_comp) * wq;
396
// put in a subroutine:
397
basis_on_quadrature::const_iterator phi = _by_table.begin(hat_K, q);
398
basis_on_quadrature::const_iterator last_phi = _by_table.end(hat_K, q);
399
basis_on_quadrature::const_iterator_grad first_grad_psi = _bx_table.begin_grad (hat_K, q);
400
basis_on_quadrature::const_iterator_grad last_grad_psi = _bx_table.end_grad (hat_K, q);
401
for (size_type i = 0; phi != last_phi; phi++, i++) {
402
basis_on_quadrature::const_iterator_grad grad_psi = first_grad_psi;
404
for (size_type j = 0; grad_psi != last_grad_psi; grad_psi++, j++) {
406
const point& grad_psi_j = *grad_psi;
407
for (size_type k = 0; k < map_d; k++) {
408
sum += phi_i * invDFt_icomp_wq[k] * grad_psi_j[k];
415
// --------------------------------------------------------------------
416
// int_K (gradt u : grad v) w dK = int_K [(M U).V] w dK,
417
// with u = Sum_j,l U_{nx*l+j} psi_j
418
// v = Sum_i,k V_{ny*k+i} phi_i
419
// so M_{ny*k+i,nx*l+j} = int_K d psi_j/dk * d phi_i/dl w dK
420
// --------------------------------------------------------------------
421
template<class T, class M>
423
form_element_rep<T,M>::build_gradt_grad (const geo_element& K, ublas::matrix<T>& m) const
425
check_macro (get_first_basis().family() == element_constant::Lagrange &&
426
get_second_basis().family() == element_constant::Lagrange,
427
"Only Lagrange elements supported");
428
size_type map_d = K.dimension();
429
check_macro (coordinate_dimension() == K.dimension(),
430
"unsupported `gradt_grad' form on element type `" << K.name()
431
<< " in " << coordinate_dimension() << "D geometry");
432
size_type nx = get_first_basis().size(K);
433
size_type ny = get_second_basis().size(K);
434
m.resize (map_d*ny, map_d*nx);
436
std::vector<size_type> dis_inod;
437
_omega.dis_inod (K, dis_inod);
438
quadrature::const_iterator first = _quad.begin(K);
439
quadrature::const_iterator last = _quad.end (K);
444
for (size_type q = 0; first != last; first++, q++) {
445
rheolef::jacobian_piola_transformation (_omega, _piola_table, K, dis_inod, q, DF);
446
invDF = inv(DF,map_d);
447
invDFt = trans(invDF);
448
T wq = is_weighted() ? weight(K,q) : T(1);
449
wq *= weight_coordinate_system (K, dis_inod, q);
450
wq *= rheolef::det_jacobian_piola_transformation (DF, coordinate_dimension(), K.dimension());
453
basis_on_quadrature::const_iterator_grad grad_phi = _by_table.begin_grad(K, q);
454
basis_on_quadrature::const_iterator_grad last_grad_phi = _by_table.end_grad(K, q);
455
basis_on_quadrature::const_iterator_grad first_grad_psi = _bx_table.begin_grad (K, q);
456
basis_on_quadrature::const_iterator_grad last_grad_psi = _bx_table.end_grad (K, q);
457
for (size_type i = 0; grad_phi != last_grad_phi; grad_phi++, i++) {
458
const point& hat_grad_phi_i = *grad_phi;
459
point grad_phi_i = invDFt*hat_grad_phi_i;
460
basis_on_quadrature::const_iterator_grad grad_psi = first_grad_psi;
461
for (size_type j = 0; grad_psi != last_grad_psi; grad_psi++, j++) {
462
const point& hat_grad_psi_j = *grad_psi;
463
point grad_psi_j = invDFt*hat_grad_psi_j;
464
for (size_type k = 0; k < map_d; k++) {
465
for (size_type l = 0; l < map_d; l++) {
466
m(i+ny*k,j+nx*l) += grad_psi_j[k]*grad_phi_i[l]*wq;
473
// --------------------------------------------------------------------
474
// vector-valued : <div_div phi, psi> = (div phi, div psi)
475
// Mij += trans(grad_phi_i) * Dw * grad_psi_j
476
// --------------------------------------------------------------------
477
template<class T, class M>
479
form_element_rep<T,M>::build_div_div (const geo_element& K, ublas::matrix<T>& m) const
481
check_macro (get_first_basis().family() == element_constant::Lagrange &&
482
get_second_basis().family() == element_constant::Lagrange,
483
"Only Lagrange elements supported");
484
size_type map_d = K.dimension();
485
reference_element hat_K = K;
486
check_macro (coordinate_dimension() == K.dimension(),
487
"unsupported `gradt_grad' form on element type `" << K.name()
488
<< "' in " << coordinate_dimension() << "D geometry");
489
size_type nx = get_first_basis().size(hat_K);
490
size_type ny = get_second_basis().size(hat_K);
491
m.resize (map_d*ny, map_d*nx);
493
std::vector<size_type> dis_inod;
494
_omega.dis_inod (K, dis_inod);
495
quadrature::const_iterator first = _quad.begin(hat_K);
496
quadrature::const_iterator last = _quad.end (hat_K);
501
for (size_type q = 0; first != last; first++, q++) {
502
rheolef::jacobian_piola_transformation (_omega, _piola_table, K, dis_inod, q, DF);
503
invDF = inv(DF,map_d);
504
invDFt = trans(invDF);
505
T wq = is_weighted() ? weight(K,q) : T(1);
506
wq *= weight_coordinate_system (K, dis_inod, q);
507
wq *= rheolef::det_jacobian_piola_transformation (DF, coordinate_dimension(), K.dimension());
510
basis_on_quadrature::const_iterator_grad grad_phi = _by_table.begin_grad(hat_K, q);
511
basis_on_quadrature::const_iterator_grad last_grad_phi = _by_table.end_grad(hat_K, q);
512
basis_on_quadrature::const_iterator_grad first_grad_psi = _bx_table.begin_grad (hat_K, q);
513
basis_on_quadrature::const_iterator_grad last_grad_psi = _bx_table.end_grad (hat_K, q);
514
for (size_type i = 0; grad_phi != last_grad_phi; grad_phi++, i++) {
515
const point& hat_grad_phi_i = *grad_phi;
516
point grad_phi_i = invDFt*hat_grad_phi_i;
517
basis_on_quadrature::const_iterator_grad grad_psi = first_grad_psi;
518
for (size_type j = 0; grad_psi != last_grad_psi; grad_psi++, j++) {
519
const point& hat_grad_psi_j = *grad_psi;
520
point grad_psi_j = invDFt*hat_grad_psi_j;
521
for (size_type k = 0; k < map_d; k++) {
522
for (size_type l = 0; l < map_d; l++) {
523
m(i+ny*k,j+nx*l) += grad_psi_j[l]*grad_phi_i[k]*wq;
530
// ----------------------------------------------------------
531
// elementary grad_grad form:
532
// --------------------------------------------------------------------
533
// weighted forms: i-th component on node x_q in K
534
template<class T, class M>
536
form_element_rep<T,M>::set_weight (const field_basic<T,M>& wh) const
539
_wh.dis_dof_update();
540
_bw_table.set (_quad, _wh.get_space().get_numbering().get_basis());
542
_qopt.set_order (0); // will be re-computed at re-initailization
545
template<class T, class M>
547
form_element_rep<T,M>::weight (const geo_element& K, size_type q) const
549
// get wh value at idof elements
550
const basis& bw = _wh.get_space().get_numbering().get_basis();
551
size_t loc_ndof = bw.size (K.variant()) ;
552
std::vector<size_type> dis_idof1 (loc_ndof);
553
_wh.get_space().dis_idof (K, dis_idof1);
555
std::vector<T> dof (loc_ndof);
556
for (size_type loc_idof = 0; loc_idof < loc_ndof; loc_idof++) {
557
dof [loc_idof] = _wh.dis_dof (dis_idof1 [loc_idof]);
559
basis_on_quadrature::const_iterator iter_b = _bw_table.begin (K, q);
561
for (size_type loc_idof = 0; loc_idof < loc_ndof; loc_idof++, iter_b++) {
562
wq += dof [loc_idof] * (*iter_b); // sum_i w_coef(i)*hat_phi(x_i)
299
566
// ==========================================================================
300
567
// instanciation in library
301
568
// ==========================================================================