GetFEM  5.5
getfem_assembling_tensors.cc
1 /*===========================================================================
2 
3  Copyright (C) 2003-2026 Julien Pommier
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see https://www.gnu.org/licenses/.
18 
19 ===========================================================================*/
20 
21 #include "gmm/gmm_blas_interface.h"
23 #include "getfem/getfem_locale.h"
24 #include "getfem/getfem_mat_elem.h"
25 
26 namespace getfem {
27  size_type vdim_specif_list::nb_mf() const {
28  return std::count_if(begin(), end(),
29  std::mem_fn(&vdim_specif::is_mf_ref));
30  }
31  size_type vdim_specif_list::nbelt() const {
32  size_type sz = 1;
33  for (const_iterator it = begin(); it != end(); ++it) sz *= (*it).dim;
34  return sz;
35  }
36  void vdim_specif_list::build_strides_for_cv
37  (size_type cv, tensor_ranges& r, std::vector<tensor_strides >& str) const {
38  stride_type s = 1, cnt = 0;
39  str.resize(size());
40  r.resize(size());
41  for (const_iterator it = begin(); it != end(); ++it, ++cnt) {
42  if ((*it).is_mf_ref()) {
43  r[cnt] = unsigned((*it).pmf->nb_basic_dof_of_element(cv));
44  //mesh_fem::ind_dof_ct::const_iterator ii
45  // = (*it).pmf->ind_basic_dof_of_element(cv).begin();
46  str[cnt].resize(r[cnt]);
47  for (index_type j=0; j < r[cnt]; ++j) {
48  str[cnt][j] = int((*it).pmf->ind_basic_dof_of_element(cv)[j]*s);
49  }
50  } else {
51  r[cnt] = int((*it).dim);
52  str[cnt].resize(r[cnt]);
53  for (index_type j=0; j < (*it).dim; ++j) {
54  str[cnt][j] = j*s;
55  }
56  }
57  s *= stride_type((*it).dim);
58  }
59  }
60 
61  void ATN::update_childs_required_shape() {
62  for (dim_type i=0; i < nchilds(); ++i) {
63  child(i).merge_required_shape(tensor_shape(child(i).ranges()));
64  }
65  }
66  void ATN::set_number(unsigned &gcnt) {
67  if (number_ == unsigned(-1)) {
68  for (unsigned i=0; i < nchilds(); ++i) child(i).set_number(gcnt);
69  number_ = ++gcnt;
70  }
71  }
72 
73  bool ATN::is_zero_size() {
74  return child(0).is_zero_size();
75  }
76 
77  /*
78  general class for tensor who store their data
79  */
80  class ATN_tensor_w_data : public ATN_tensor {
81  TDIter data_base;
82  protected:
83  std::vector<scalar_type> data;
84  void reinit_();
85  void reinit0()
86  { ATN_tensor_w_data::reinit_(); std::fill(data.begin(), data.end(),0); }
87  };
88 
89  /* note that the data is NOT filled with zeros */
90  void ATN_tensor_w_data::reinit_() {
91  tr.assign_shape(req_shape);
92  tr.init_strides();
93  if (tr.card() > 10000000) {
94  cerr << "warning, a tensor of size " << tr.card()
95  << " will be created, it needs "
96  << tr.card()*sizeof(scalar_type) << " bytes of memory\n";
97  }
98  if (tr.card() == 0) {
99  cerr << "WARNING: tensor " << name()
100  << " will be created with a size of "
101  << ranges() << " and 0 non-null elements!" << endl;
102  }
103  data.resize(tr.card());
104  data_base = &data[0];
105  tr.set_base(data_base);
106  }
107 
108 
109  /*
110  general class for the computation of a reduced product of tensors
111  (templated by the number of product tensors)
112 
113  should be very effective.
114  */
115  typedef std::vector<std::pair<ATN_tensor*,std::string> >
116  reduced_tensor_arg_type;
117 
118  class ATN_reduced_tensor : public ATN_tensor_w_data {
119  /* used for specification of tensors and reduction indices , see below */
120  reduced_tensor_arg_type red;
121  bgeot::tensor_reduction tred;
122  public:
123  void check_shape_update(size_type , dim_type) {
124  shape_updated_ = false;
125  for (dim_type i=0; i < nchilds(); ++i) {
126  if (child(i).is_shape_updated()) {
127  shape_updated_ = true;
128  }
129  }
130  if (shape_updated_) {
131  r_.resize(0);
132  for (dim_type i=0; i < nchilds(); ++i) {
133  std::string s = red_n(i);
134  if (s.size() != child(i).ranges().size()) {
135  ASM_THROW_TENSOR_ERROR("wrong number of indexes for the "
136  << int(i+1)
137  << "th argument of the reduction "
138  << name()
139  << " (ranges=" << child(i).ranges() << ")");
140  }
141  for (size_type j=0; j < s.length(); ++j) {
142  if (s[j] == ' ') r_.push_back(child(i).ranges()[j]);
143  }
144  }
145  }
146  }
147  void update_childs_required_shape() {
148  /* pourrait etre mieux, cf les commentaires de la fonction
149  tensor_reduction::required_shape */
150  for (dim_type n=0; n < nchilds(); ++n) {
151  tensor_shape ts(child(n).ranges());
152  tensor_ranges rn(child(n).ranges());
153  const std::string &s = red[n].second;
154  GMM_ASSERT1(rn.size() == s.size(), "Wrong size !");
155  for (unsigned i=0; i < rn.size(); ++i) {
156  if (s[i] != ' ') {
157  size_type p = s.find(s[i]);
158  if (p != size_type(-1) && p < i && rn[p] != rn[i])
159  ASM_THROW_TENSOR_ERROR("can't reduce the diagonal of a tensor "
160  "of size " << rn << " with '"
161  << s << "'");
162  }
163  }
164  bgeot::tensor_reduction::diag_shape(ts, red[n].second);
165  child(n).merge_required_shape(ts);
166  }
167  }
168 
169  /*
170  r is a container of pair<vtensor&,std::string>
171  where the strings specify the reduction indices:
172 
173  if a_{ik}b_{kjl} is reduced against k and l, then the strings are
174  " k" and "k l"
175  */
176  ATN_reduced_tensor(reduced_tensor_arg_type& r) : red(r) {
177  for (size_type i=0; i < r.size(); ++i) add_child(*red[i].first);
178  }
179 
180  std::string red_n(size_type n) {
181  std::string s = red[n].second;
182  if (s.length() == 0)
183  s.append(red[n].first->ranges().size(), ' ');
184  return s;
185  }
186 
187  private:
188  void reinit_() {
189  tred.clear();
190  for (dim_type i=0; i < red.size(); ++i) {
191  // cerr << "ATN_reduced_tensor::reinit : insertion of r(" << red_n(i)
192  // << "), tr[" << red[i].first->ranges() << "\n"
193  // << red[i].first->tensor() << endl;*/
194  // if (red[i].first->ranges().size() != red_n(i).length()) {
195  // ASM_THROW_TENSOR_ERROR("wrong number of indexes for the "
196  // << int(i+1)
197  // << "th argument of the reduction " << name()
198  // << " (ranges=" << red[i].first->ranges()
199  // << ")");
200  // }
201  tred.insert(red[i].first->tensor(), red_n(i));
202  }
203  /* reserve the memory for the output
204  the memory is set to zero since the reduction may only affect a
205  subset of this tensor hence a part of it would not be initialized
206  */
207  ATN_tensor_w_data::reinit0();
208  /* on fournit notre propre tenseur pour stocker les resultats */
209  tred.prepare(&tensor());
210  }
211 
212  void exec_(size_type , dim_type ) {
213  std::fill(data.begin(), data.end(), 0.); /* do_reduction ne peut pas */
214  /* le faire puisque ce n'est pas lui le proprietaire du tenseur de */
215  /* sortie. */
216  tred.do_reduction();
217  }
218  };
219 
220 
221  /* slice tensor:
222  no need of a temporary storage for the slice, since direct access
223  can be provided via strides.
224  */
225  class ATN_sliced_tensor : public ATN_tensor {
226  dim_type slice_dim;
227  size_type slice_idx;
228  public:
229  ATN_sliced_tensor(ATN_tensor& a, dim_type slice_dim_,
230  size_type slice_idx_) :
231  slice_dim(slice_dim_), slice_idx(slice_idx_) { add_child(a); }
232  void check_shape_update(size_type , dim_type) {
233  if ((shape_updated_ = child(0).is_shape_updated())) {
234  if (slice_dim >= child(0).ranges().size() ||
235  slice_idx >= child(0).ranges()[slice_dim]) {
236  ASM_THROW_TENSOR_ERROR("can't slice tensor " << child(0).ranges()
237  << " at index " << int(slice_idx)
238  << " of dimension " << int(slice_dim));
239  }
240  r_ = child(0).ranges(); r_.erase(r_.begin()+slice_dim);
241  }
242  }
243  void update_childs_required_shape() {
244  tensor_shape ts = req_shape;
245  ts.set_ndim_noclean(dim_type(ts.ndim()+1));
246  ts.shift_dim_num_ge(slice_dim,+1);
247  ts.push_mask(tensor_mask(child(0).ranges()[slice_dim],
248  tensor_mask::Slice(slice_dim, index_type(slice_idx))));
249  child(0).merge_required_shape(ts);
250  }
251  private:
252  void reinit_() {
253  tensor() = tensor_ref(child(0).tensor(),
254  tensor_mask::Slice(slice_dim, index_type(slice_idx)));
255  }
256  void exec_(size_type, dim_type) {}
257  };
258 
259  /* tensor with reoderer indices:
260  t{i,j,k} -> t{j,i,k}
261  reorder= 0 1 2 1 0 2
262  */
263  class ATN_permuted_tensor : public ATN_tensor {
264  std::vector<dim_type> reorder;
265  public:
266  /* attention on ne s'assure pas que reorder est une permutation */
267  ATN_permuted_tensor(ATN_tensor& a, const std::vector<dim_type>& reorder_) :
268  reorder(reorder_) { add_child(a); }
269  private:
270  void check_shape_update(size_type , dim_type) {
271  if ((shape_updated_ = child(0).is_shape_updated())) {
272  if (reorder.size() != child(0).ranges().size())
273  ASM_THROW_TENSOR_ERROR("can't reorder tensor '" << name()
274  << "' of dimensions " << child(0).ranges()
275  << " with this permutation: " << vref(reorder));
276  r_.resize(reorder.size());
277  std::fill(r_.begin(), r_.end(), dim_type(-1));
278 
279  /*
280  --- TODO: A VERIFIER !!!!! ---
281  */
282  for (size_type i=0; i < reorder.size(); ++i)
283  r_[i] = child(0).ranges()[reorder[i]];
284  }
285  }
286  void update_childs_required_shape() {
287  tensor_shape ts = req_shape;
288  ts.permute(reorder, true);
289  child(0).merge_required_shape(ts);
290  }
291  void reinit_() {
292  tensor() = child(0).tensor();
293  tensor().permute(reorder);
294  }
295  void exec_(size_type, dim_type) {}
296  };
297 
298  /* diagonal tensor: take the "diagonal" of a tensor
299  (ie diag(t(i,j,k), {i,k}) == t(i,j,i))
300 
301  /!\ the number of dimensions DO NOT change
302  */
303  class ATN_diagonal_tensor : public ATN_tensor {
304  dim_type i1, i2;
305  public:
306  ATN_diagonal_tensor(ATN_tensor& a, dim_type i1_, dim_type i2_) :
307  i1(i1_), i2(i2_) { add_child(a); }
308  private:
309  void check_shape_update(size_type , dim_type) {
310  if ((shape_updated_ = child(0).is_shape_updated())) {
311  if (i1 >= child(0).ranges().size() || i2 >= child(0).ranges().size() ||
312  i1 == i2 || child(0).ranges()[i1] != child(0).ranges()[i2])
313  ASM_THROW_TENSOR_ERROR("can't take the diagonal of a tensor of "
314  "sizes " << child(0).ranges() <<
315  " at indexes " << int(i1) << " and "
316  << int(i2));
317  r_ = child(0).ranges();
318  }
319  }
320  void update_childs_required_shape() {
321  tensor_shape ts = req_shape.diag_shape(tensor_mask::Diagonal(i1,i2));
322  child(0).merge_required_shape(ts);
323  }
324  void reinit_() {
325  tensor() = tensor_ref(child(0).tensor(), tensor_mask::Diagonal(i1,i2));
326  }
327  void exec_(size_type, dim_type) {}
328  };
329 
330  /* called (if possible, i.e. if not an exact integration) for each
331  integration point during mat_elem->compute() */
332  struct computed_tensor_integration_callback
333  : public mat_elem_integration_callback {
334  bgeot::tensor_reduction red;
335  bool was_called;
336  std::vector<TDIter> tensor_bases; /* each tref of 'red' has a */
337  /* reference into this vector */
338  virtual void exec(bgeot::base_tensor &t, bool first, scalar_type c) {
339  if (first) {
340  resize_t(t);
341  std::fill(t.begin(), t.end(), 0.);
342  was_called = true;
343  }
344  assert(t.size());
345  for (unsigned k=0; k!=eltm.size(); ++k) { /* put in the 'if (first)' ? */
346  tensor_bases[k] = const_cast<TDIter>(&(*eltm[k]->begin()));
347  }
348  red.do_reduction();
349 #if defined(GMM_USES_BLAS)
350  BLAS_INT one = BLAS_INT(1), n = BLAS_INT(red.out_data.size());
351  assert(n);
352  gmm::daxpy_(&n, &c, const_cast<double*>(&(red.out_data[0])),
353  &one, (double*)&(t[0]), &one);
354 #else
355  size_type n = red.out_data.size();
356  assert(n);
357  for (size_type k=0; k < n; ++k)
358  t[k] += c * red.out_data[k];
359  // gmm::add(gmm::scaled(red.out_data, c), t.as_vector())
360 #endif
361  }
362  void resize_t(bgeot::base_tensor &t) {
363  bgeot::multi_index r;
364  if (red.reduced_range.size())
365  r.assign(red.reduced_range.begin(), red.reduced_range.end());
366  else { r.resize(1); r[0]=1; }
367  t.adjust_sizes(r);
368  }
369  };
370 
371  /*
372  ATN_computed_tensor , the largest of all
373 
374  This object has become quite complex. It is the glue with the
375  mat_elem_*. It is able to perform an inline reduction (i.e. a
376  reduction applied during the mat_elem->compute()) when it is
377  allowed (i.e. no exact integration), or do the same reduction
378  after the mat_elem->compute().
379  The reduction may also involve other ATN_tensors.
380  */
381 
382  struct mf_comp_vect;
383 
384  struct mf_comp {
385  pnonlinear_elem_term nlt;
386  const mesh_fem* pmf; /* always defined except when op_type == NORMAL */
387  mf_comp_vect *owner;
388 
389  ATN_tensor *data;
390  std::vector<const mesh_fem*> auxmf; /* used only by nonlinear terms */
391  typedef enum { BASE=1, GRAD=2, HESS=3, NORMAL=4, GRADGT=5, GRADGTINV=6,
392  NONLIN=7, DATA=8 } op_type;
393  typedef enum { PLAIN_SHAPE = 0, VECTORIZED_SHAPE = 1,
394  MATRIXIZED_SHAPE = 2 } field_shape_type;
395  op_type op; /* the numerical values indicates the number
396  of dimensions in the tensor */
397  field_shape_type vshape; /* VECTORIZED_SHAPE if vectorization was required
398  (adds an addiational dimension to the tensor
399  which represents the component number.
400  MATRIXIZED_SHAPE for "matricization" of the
401  field.
402  */
403  std::string reduction;
404  /*
405  vectorization of non-vector FEM:
406 
407  phi1 0 0
408  0 phi1 0
409  0 0 phi1
410  phi2 0 0
411  0 phi2 0
412  0 0 phi2
413  ...
414  */
415  mf_comp(mf_comp_vect *ow, const mesh_fem* pmf_, op_type op_,
416  field_shape_type fst) :
417  nlt(0), pmf(pmf_), owner(ow), data(0), op(op_), vshape(fst) { }
418  mf_comp(mf_comp_vect *ow, const std::vector<const mesh_fem*> vmf,
419  pnonlinear_elem_term nlt_) :
420  nlt(nlt_), pmf(vmf[0]), owner(ow), data(0),
421  auxmf(vmf.begin()+1, vmf.end()), op(NONLIN),
422  vshape(PLAIN_SHAPE) { }
423  mf_comp(mf_comp_vect *ow, ATN_tensor *t) :
424  nlt(0), pmf(0), owner(ow), data(t), op(DATA), vshape(PLAIN_SHAPE) {}
425  void push_back_dimensions(size_type cv, tensor_ranges &rng,
426  bool only_reduced=false) const;
427  bool reduced(unsigned i) const {
428  if (i >= reduction.size()) return false;
429  else return reduction[i] != ' ';
430  }
431  };
432 
433  struct mf_comp_vect : public std::vector<mf_comp> {
434  const mesh_im *main_im;
435  public:
436  mf_comp_vect() : std::vector<mf_comp>(), main_im(0) { }
437  mf_comp_vect(const mf_comp_vect &other)
438  : std::vector<mf_comp>(other), main_im(other.main_im) {
439  for (size_type i=0; i < size(); ++i) (*this)[i].owner = this;
440  }
441  void set_im(const mesh_im &mim) { main_im = &mim; }
442  const mesh_im& get_im() const { return *main_im; }
443  private:
444  mf_comp_vect& operator=(const mf_comp_vect &other);
445  };
446 
447  void mf_comp::push_back_dimensions(size_type cv, tensor_ranges &rng,
448  bool only_reduced) const {
449  switch (op) {
450  case NONLIN:
451  {
452  const bgeot::multi_index &sizes = nlt->sizes(cv);
453  for (unsigned j=0; j < sizes.size(); ++j)
454  if (!only_reduced || !reduced(j))
455  rng.push_back(short_type(sizes[j]));
456  }
457  break;
458  case DATA:
459  for (unsigned i=0; i < data->ranges().size(); ++i)
460  if (!only_reduced || !reduced(i))
461  rng.push_back(data->ranges()[i]);
462  break;
463  case NORMAL:
464  assert(pmf==0);
465  assert(&owner->get_im());
466  assert(owner->get_im().linked_mesh().dim() != dim_type(-1));
467  rng.push_back(owner->get_im().linked_mesh().dim());
468  break;
469  case GRADGT:
470  case GRADGTINV:
471  assert(pmf==0);
472  assert(&owner->get_im());
473  rng.push_back(owner->get_im().linked_mesh().dim()); // N
474  rng.push_back(owner->get_im().linked_mesh().structure_of_convex(cv)->dim()); // P
475  break;
476  default:
477  unsigned d = 0;
478  if (!only_reduced || !reduced(d))
479  rng.push_back(unsigned(pmf->nb_basic_dof_of_element(cv)));
480  ++d;
481  if (vshape == mf_comp::VECTORIZED_SHAPE) {
482  if (!only_reduced || !reduced(d)) rng.push_back(pmf->get_qdim());
483  ++d;
484  }
485  if (vshape == mf_comp::MATRIXIZED_SHAPE) {
486  if (!only_reduced || !reduced(d)) {
487  GMM_ASSERT1(pmf->get_qdims().size() == 2, "Non matrix field");
488  rng.push_back(dim_type(pmf->get_qdims()[0]));
489  }
490  ++d;
491  if (!only_reduced || !reduced(d)) rng.push_back(dim_type(pmf->get_qdims()[1]));
492  ++d;
493  }
494 
495  if (op == GRAD || op == HESS) {
496  if (!only_reduced || !reduced(d))
497  rng.push_back(pmf->linked_mesh().dim());
498  ++d;
499  }
500  if (op == HESS) {
501  if (!only_reduced || !reduced(d))
502  rng.push_back(pmf->linked_mesh().dim());
503  ++d;
504  }
505  break;
506  }
507  }
508 
509 
510  class ATN_computed_tensor : public ATN_tensor {
511  mf_comp_vect mfcomp;
512  mat_elem_pool mep;
513  pmat_elem_computation pmec;
514  pmat_elem_type pme;
515  pintegration_method pim;
517  base_tensor t;
518  std::vector<scalar_type> data;
519  TDIter data_base;
520  stride_type tsize;
521  dal::bit_vector req_bv; /* bit_vector of values the mat_elem has to compute
522  (useful when only a subset is required from the
523  possibly very large elementary tensor) */
524  bool has_inline_reduction; /* true if used with reductions inside the comp, for example:
525  "comp(Grad(#1)(:,i).Grad(#2)(:,i))" */
526  computed_tensor_integration_callback icb; /* callback for inline reductions */
527 
528  /* if inline reduction are to be done, but were not possible (i.e. if exact
529  integration was used) then a fallback is used: apply the reduction
530  afterward, on the large expanded tensor */
531  bgeot::tensor_reduction fallback_red;
532  bool fallback_red_uptodate;
533  TDIter fallback_base;
534 
535  size_type cv_shape_update;
536  //mat_elem_inline_reduction inline_red;
537  public:
538  ATN_computed_tensor(const mf_comp_vect &mfcomp_) :
539  mfcomp(mfcomp_), pmec(0), pme(0), pim(0), pgt(0), data_base(0) {
540  has_inline_reduction = false;
541  bool in_data = false;
542  for (size_type i=0; i < mfcomp.size(); ++i) {
543  if (mfcomp[i].reduction.size() || mfcomp[i].op == mf_comp::DATA) {
544  has_inline_reduction = true;
545  if (mfcomp[i].op == mf_comp::DATA) { add_child(*mfcomp[i].data); in_data = true; }
546  }
547  if (mfcomp[i].op != mf_comp::DATA && in_data) {
548  /* constraint of fallback 'do_post_reduction' */
549  ASM_THROW_ERROR("data tensors inside comp() cannot be intermixed with Grad() and Base() etc., they must appear LAST");
550  }
551  }
552  }
553 
554  private:
555  /* mostly for non-linear terms, such as a 3x3x3x3 tensor which may have
556  many symmetries or many null elements.. in that case, it is preferable
557  for getfem_mat_elem to handle only a sufficient subset of the tensor,
558  and build back the full tensor via adequate strides and masks */
559 
560  /* append a dimension (full) to tref */
561  stride_type add_dim(const tensor_ranges& rng, dim_type d, stride_type s, tensor_ref &tref) {
562  assert(d < rng.size());
563  tensor_strides v;
564  index_type r = rng[d];
565  tensor_mask m; m.set_full(d, r);
566  v.resize(r);
567  for (index_type i=0; i < r; ++i) v[i] = s*i;
568  assert(tref.masks().size() == tref.strides().size());
569  tref.set_ndim_noclean(dim_type(tref.ndim()+1));
570  tref.push_mask(m);
571  tref.strides().push_back(v);
572  return s*r;
573  }
574 
575  /* append a vectorized dimension to tref -- works also for cases
576  where target_dim > 1
577  */
578  stride_type add_vdim(const tensor_ranges& rng, dim_type d,
579  index_type target_dim, stride_type s,
580  tensor_ref &tref) {
581  assert(d < rng.size()-1);
582  index_type r = rng[d], q=rng[d+1];
583  index_type qmult = q/target_dim;
584  assert(r%qmult == 0); assert(q%qmult==0);
585 
586  tensor_strides v;
587  tensor_ranges trng(2); trng[0] = q; trng[1] = r;
588  index_set ti(2); ti[0] = dim_type(d+1); ti[1] = d;
589  tensor_mask m(trng,ti);
590  v.resize(r*target_dim);
591  tensor_ranges cnt(2);
592  for (index_type i=0; i < r; ++i) {
593  // the value in cnt[1] is not directly used as the loop variable
594  // as this makes the INTEL 2019 compiler wrongly optimize the loop check,
595  // making the outer loop go one more than it needs to;
596  // creating SEH exceptions
597  cnt[1] = i;
598  for (index_type k=0; k < target_dim; ++k) {
599  cnt[0] = k*qmult + (cnt[1]%qmult); //(cnt[1] % qmult)*target_dim + k;
600  m.set_mask_val(m.lpos(cnt), true);
601  v[cnt[1]*target_dim+k] = s*( k * r/qmult + cnt[1]/qmult); //s*((cnt[1]/qmult)*target_dim + k);
602  }
603  }
604  assert(tref.masks().size() == tref.strides().size());
605  tref.set_ndim_noclean(dim_type(tref.ndim()+2));
606  tref.push_mask(m);
607  tref.strides().push_back(v);
608  return s*(r/qmult)*target_dim;
609  }
610 
611  /* append a matrixized dimension to tref -- works also for cases
612  where target_dim > 1 (in that case the rows are "vectorized")
613 
614  for example, the Base(RT0) in 2D (3 dof, target_dim=2) is:
615  [0 1 2;
616  3 4 5]
617 
618 
619  if we set it in a mesh_fem of qdim = 3x2 , we produce the sparse
620  elementary tensor 9x3x2 =
621 
622  x x x y y y
623  0 . . 3 . . <- phi1
624  . 0 . . 3 . <- phi2
625  . . 0 . . 3 <- phi3
626  1 . . 4 . . <- phi4
627  . 1 . . 4 . <- phi5
628  . . 1 . . 4 <- phi6
629  2 . . 5 . . <- phi7
630  . 2 . . 5 . <- phi8
631  . . 2 . . 5 <- phi9
632 
633  */
634  stride_type add_mdim(const tensor_ranges& rng, dim_type d,
635  index_type target_dim, stride_type s, tensor_ref &tref) {
636  assert(d < rng.size()-2);
637 
638  /* r = nb_dof, q = nb_rows, p = nb_cols */
639  index_type r = rng[d], q=rng[d+1], p=rng[d+2];
640  index_type qmult = (q*p)/target_dim;
641 
642  assert(r % q == 0);
643  assert(p % target_dim == 0);
644  assert(r % (p/target_dim) == 0);
645 
646  tensor_strides v;
647  tensor_ranges trng(3); trng[0] = q; trng[1] = p; trng[2] = r;
648  index_set ti(3); ti[0] = dim_type(d+1); ti[1] = dim_type(d+2); ti[2] = d;
649  tensor_mask m(trng,ti);
650  v.resize(r*target_dim);
651  tensor_ranges cnt(3);
652  for (cnt[2]=0; cnt[2] < r; cnt[2]++) { // loop over virtual dof number {
653  for (index_type k=0; k < target_dim; ++k) {
654  unsigned pos = (cnt[2]*target_dim+k) % (q*p);
655  //unsigned ii = (pos%q), jj = (pos/q);
656  unsigned ii = (pos/p), jj = (pos%p);
657  cnt[0] = ii; cnt[1] = jj;
658  //cerr << " set_mask_val(lpos(" << cnt[0] << "," << cnt[1] << "," << cnt[2] << ") = " << m.lpos(cnt) << ")\n";
659  m.set_mask_val(m.lpos(cnt), true);
660  v[cnt[2]*target_dim+k] = s*(k * r/qmult + cnt[2]/qmult); //s*((cnt[2]/qmult)*target_dim + k);
661  }
662  }
663  assert(tref.masks().size() == tref.strides().size());
664  tref.set_ndim_noclean(dim_type(tref.ndim()+3));
665  tref.push_mask(m);
666  // cerr << "rng = " << rng << "\nr=" << r << ", q=" << q << ", p="
667  // << p << ", qmult =" << qmult << ", target_dim= " << target_dim
668  // << "\n" << "m = " << m << "\nv=" << v << "\n";
669  tref.strides().push_back(v);
670  return s*(r/qmult)*target_dim;
671  }
672 
673 
674  /* called when the FEM has changed */
675  void update_pmat_elem(size_type cv) {
676  pme = 0;
677  for (size_type i=0; i < mfcomp.size(); ++i) {
678  if (mfcomp[i].op == mf_comp::DATA) continue;
679  pfem fem = (mfcomp[i].pmf ? mfcomp[i].pmf->fem_of_element(cv)
680  : NULL);
681  pmat_elem_type pme2 = 0;
682  switch (mfcomp[i].op) {
683  case mf_comp::BASE: pme2 = mat_elem_base(fem); break;
684  case mf_comp::GRAD: pme2 = mat_elem_grad(fem); break;
685  case mf_comp::HESS: pme2 = mat_elem_hessian(fem); break;
686  case mf_comp::NORMAL: pme2 = mat_elem_unit_normal(); break;
687  case mf_comp::GRADGT:
688  case mf_comp::GRADGTINV:
689  pme2 = mat_elem_grad_geotrans(mfcomp[i].op == mf_comp::GRADGTINV);
690  break;
691  case mf_comp::NONLIN: {
692  std::vector<pfem> ftab(1+mfcomp[i].auxmf.size());
693  ftab[0] = fem;
694  for (unsigned k=0; k < mfcomp[i].auxmf.size(); ++k)
695  ftab[k+1] = mfcomp[i].auxmf[k]->fem_of_element(cv);
696  pme2 = mat_elem_nonlinear(mfcomp[i].nlt, ftab);
697  } break;
698  case mf_comp::DATA: /*ignore*/;
699  }
700  if (pme == 0) pme = pme2;
701  else pme = mat_elem_product(pme, pme2);
702  }
703 
704  if (pme == 0) pme = mat_elem_empty();
705  //ASM_THROW_ERROR("no Base() or Grad() or etc!");
706  }
707 
708 
709 
710  size_type
711  push_back_mfcomp_dimensions(size_type cv, const mf_comp& mc,
712  unsigned &d, const bgeot::tensor_ranges &rng,
713  bgeot::tensor_ref &tref, size_type tsz=1) {
714  if (mc.op == mf_comp::NONLIN) {
715  for (size_type j=0; j < mc.nlt->sizes(cv).size(); ++j)
716  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
717  } else if (mc.op == mf_comp::DATA) {
718  assert(tsz == 1);
719  tref = mc.data->tensor();
720  tsz *= tref.card();
721  d += tref.ndim();
722  } else if (mc.op == mf_comp::NORMAL) {
723  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
724  } else if (mc.op == mf_comp::GRADGT ||
725  mc.op == mf_comp::GRADGTINV) {
726  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
727  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
728  } else {
729  size_type target_dim = mc.pmf->fem_of_element(cv)->target_dim();
730  size_type qdim = mc.pmf->get_qdim();
731 
732  //cerr << "target_dim = " << target_dim << ", qdim = " << qdim << ", vectorize=" << mc.vectorize << ", rng=" << rng << " d=" << d << ", tsz=" << tsz << "\n";
733  if (mc.vshape == mf_comp::VECTORIZED_SHAPE) {
734  if (target_dim == qdim) {
735  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
736  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
737  } else {
738  tsz = add_vdim(rng,dim_type(d),index_type(target_dim),
739  stride_type(tsz), tref);
740  d += 2;
741  }
742  } else if (mc.vshape == mf_comp::MATRIXIZED_SHAPE) {
743  if (target_dim == qdim) {
744  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
745  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
746  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
747  } else {
748  tsz = add_mdim(rng, dim_type(d), index_type(target_dim),
749  stride_type(tsz), tref);
750  d += 3;
751  }
752  } else tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
753  if (mc.op == mf_comp::GRAD || mc.op == mf_comp::HESS) {
754  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
755  }
756  if (mc.op == mf_comp::HESS) {
757  tsz = add_dim(rng, dim_type(d++), stride_type(tsz), tref);
758  }
759  }
760  return tsz;
761  }
762 
763  void update_shape_with_inline_reduction(size_type cv) {
764  fallback_red_uptodate = false;
765  icb.tensor_bases.resize(mfcomp.size()); /* todo : resize(nb_mfcomp_not_data) */
766  icb.red.clear();
767  for (size_type i=0; i < mfcomp.size(); ++i) {
768  tensor_ref tref;
769  tensor_ranges rng;
770  unsigned d = 0;
771  mfcomp[i].push_back_dimensions(cv,rng);
772  push_back_mfcomp_dimensions(cv,mfcomp[i], d, rng, tref);
773  assert(tref.ndim() == rng.size() && d == rng.size());
774  if (mfcomp[i].reduction.size() == 0)
775  mfcomp[i].reduction.insert(size_type(0), tref.ndim(), ' ');
776  if (mfcomp[i].op != mf_comp::DATA) /* should already have the correct base */
777  tref.set_base(icb.tensor_bases[i]);
778  tref.update_idx2mask();
779  if (mfcomp[i].reduction.size() != tref.ndim()) {
780  ASM_THROW_TENSOR_ERROR("wrong number of indices for the "<< int(i+1)
781  << "th argument of the reduction "<< name()
782  << " (expected " << int(tref.ndim())
783  << " indexes, got "
784  << mfcomp[i].reduction.size());
785  }
786  icb.red.insert(tref, mfcomp[i].reduction);
787  }
788  icb.red.prepare();
789  icb.red.result(tensor());
790  r_.resize(tensor().ndim());
791  for (dim_type i=0; i < tensor().ndim(); ++i) r_[i] = tensor().dim(i);
792  tsize = tensor().card();
793  //cerr << "update_shape_with_inline_reduction: tensor=" << tensor()
794  // << "\nr_=" << r_ << ", tsize=" << tsize << "\n";
795  }
796 
797  void update_shape_with_expanded_tensor(size_type cv) {
798  icb.red.clear();
799  unsigned d = 0;
800  for (size_type i=0; i < mfcomp.size(); ++i) {
801  tsize = stride_type(push_back_mfcomp_dimensions(cv, mfcomp[i], d, r_, tensor(), tsize));
802  }
803  assert(d == r_.size());
804  tensor().update_idx2mask();
805  }
806 
807  void check_shape_update(size_type cv, dim_type) {
808  const mesh_im& mi = mfcomp.get_im();
809  pintegration_method pim2;
811  bool fem_changed = false;
812  pgt2 = mi.linked_mesh().trans_of_convex(cv);
813  pim2 = mi.int_method_of_element(cv);
814  // cerr << "computed tensor cv=" << cv << " f=" << int(face) << "\n";
815  /* shape is considered for update only if the FEM changes,
816  changes of pgt or integration method does not affect shape
817  (only the mat_elem) */
818  cv_shape_update = cv;
819  shape_updated_ = (pme == 0); //false;
820  for (size_type i=0; i < nchilds(); ++i)
821  shape_updated_ = shape_updated_ || child(i).is_shape_updated();
822  for (size_type i=0; shape_updated_ == false && i < mfcomp.size(); ++i) {
823  if (mfcomp[i].pmf == 0) continue;
824  if (current_cv == size_type(-1)) {
825  shape_updated_ = true; fem_changed = true;
826  } else {
827  fem_changed = fem_changed ||
828  (mfcomp[i].pmf->fem_of_element(current_cv) !=
829  mfcomp[i].pmf->fem_of_element(cv));
830  /* for FEM with non-constant nb_dof.. */
831  shape_updated_ = shape_updated_ ||
832  (mfcomp[i].pmf->nb_basic_dof_of_element(current_cv) !=
833  mfcomp[i].pmf->nb_basic_dof_of_element(cv));
834  }
835  }
836  if (shape_updated_) {
837  r_.resize(0);
838  /* get the new ranges */
839  for (unsigned i=0; i < mfcomp.size(); ++i)
840  mfcomp[i].push_back_dimensions(cv, r_, true);
841  }
842  if (fem_changed || shape_updated_) {
843  /* build the new mat_elem structure */
844  update_pmat_elem(cv);
845  }
846  if (shape_updated_ || fem_changed || pgt != pgt2 || pim != pim2) {
847  pgt = pgt2; pim = pim2;
848  pmec = mep(pme, pim, pgt, has_inline_reduction);
849  }
850  }
851 
852  void reinit_() {
853  if (!shape_updated_) return;
854  tensor().clear();
855  tsize = 1;
856  if (has_inline_reduction) {
857  update_shape_with_inline_reduction(cv_shape_update);
858  } else {
859  update_shape_with_expanded_tensor(cv_shape_update);
860  }
861  data_base = 0;
862  tensor().set_base(data_base);
863  }
864  void update_childs_required_shape() {
865  }
866 
867  /* fallback when inline reduction is not possible:
868  do the reduction after evaluation of the mat_elem */
869  void do_post_reduction(size_type cv) {
870  if (!fallback_red_uptodate) {
871  fallback_red_uptodate = true;
872  std::string s;
873  size_type sz = 1;
874  tensor_ref tref; tensor_ranges r;
875  unsigned cnt, d=0;
876  /* insert the tensorial product of Grad() etc */
877  for (cnt=0; cnt < mfcomp.size() && mfcomp[cnt].op != mf_comp::DATA; ++cnt) {
878  mfcomp[cnt].push_back_dimensions(cv,r);
879  sz = push_back_mfcomp_dimensions(cv, mfcomp[cnt], d, r, tref, sz);
880  s += mfcomp[cnt].reduction;
881  }
882  fallback_red.clear();
883  tref.set_base(fallback_base);
884  fallback_red.insert(tref, s);
885  /* insert the optional data tensors */
886  for ( ; cnt < mfcomp.size(); ++cnt) {
887  assert(mfcomp[cnt].op == mf_comp::DATA);
888  fallback_red.insert(mfcomp[cnt].data->tensor(), mfcomp[cnt].reduction);
889  }
890  fallback_red.prepare();
891  fallback_red.result(tensor()); /* this SHOULD NOT, IN ANY CASE change the shape or strides of tensor() .. */
892  assert(icb.red.reduced_range == fallback_red.reduced_range);
893  }
894  icb.resize_t(t);
895  fallback_base = &(*t.begin());
896  fallback_red.do_reduction();
897  }
898 
899  void exec_(size_type cv, dim_type face) {
900  const mesh_im& mim = mfcomp.get_im();
901  for (unsigned i=0; i < mfcomp.size(); ++i) {
902  if (mfcomp[i].op == mf_comp::DATA) {
903  size_type fullsz = 1;
904  for (unsigned j=0; j < mfcomp[i].data->ranges().size(); ++j)
905  fullsz *= mfcomp[i].data->ranges()[j];
906  if (fullsz != size_type(mfcomp[i].data->tensor().card()))
907  ASM_THROW_TENSOR_ERROR("aaarg inline reduction will explode with non-full tensors. "
908  "Complain to the author, I was too lazy to do that properly");
909  }
910  }
911  icb.was_called = false;
912  if (face == dim_type(-1)) {
913  pmec->gen_compute(t, mim.linked_mesh().points_of_convex(cv), cv,
914  has_inline_reduction ? &icb : 0);
915  } else {
916  pmec->gen_compute_on_face(t, mim.linked_mesh().points_of_convex(cv), face, cv,
917  has_inline_reduction ? &icb : 0);
918  }
919 
920  if (has_inline_reduction && icb.was_called == false) {
921  do_post_reduction(cv);
922  data_base = &fallback_red.out_data[0];
923  } else data_base = &(*t.begin());
924  GMM_ASSERT1(t.size() == size_type(tsize),
925  "Internal error: bad size " << t.size() << " should be " << tsize);
926  }
927  };
928 
929 
930  /* extract data for each dof of the convex */
931  class ATN_tensor_from_dofs_data : public ATN_tensor_w_data {
932  const base_asm_data *basm; //scalar_type* global_array;
933  vdim_specif_list vdim;
934  multi_tensor_iterator mti;
935  tensor_ranges e_r;
936  std::vector< tensor_strides > e_str;
937  public:
938  ATN_tensor_from_dofs_data(const base_asm_data *basm_,
939  const vdim_specif_list& d) :
940  basm(basm_), vdim(d) {
941  }
942  void check_shape_update(size_type cv, dim_type) {
943  shape_updated_ = false;
944  r_.resize(vdim.size());
945  for (dim_type i=0; i < vdim.size(); ++i) {
946  if (vdim[i].is_mf_ref()) {
947  size_type nbde = vdim[i].pmf->nb_basic_dof_of_element(cv);
948  if (nbde != ranges()[i])
949  { r_[i] = unsigned(nbde); shape_updated_ = true; }
950  } else if (vdim[i].dim != ranges()[i]) {
951  r_[i] = unsigned(vdim[i].dim);
952  shape_updated_ = true;
953  }
954  }
955  }
956  virtual void init_required_shape() { req_shape = tensor_shape(ranges()); }
957 
958  private:
959  void reinit_() {
960  ATN_tensor_w_data::reinit_();
961  mti.assign(tensor(), true);
962  }
963  void exec_(size_type cv, dim_type ) {
964  vdim.build_strides_for_cv(cv, e_r, e_str);
965  assert(e_r == ranges());
966  mti.rewind();
967  basm->copy_with_mti(e_str, mti, (vdim.nb_mf() >= 1) ? vdim[0].pmf : 0);
968  }
969  };
970 
971  /* enforce symmetry of a 2D tensor
972  (requiring only the upper-triangle of its child and
973  duplicating it) */
974  class ATN_symmetrized_tensor : public ATN_tensor_w_data {
975  multi_tensor_iterator mti;
976  public:
977  ATN_symmetrized_tensor(ATN_tensor& a) { add_child(a); }
978  void check_shape_update(size_type , dim_type) {
979  if ((shape_updated_ = child(0).is_shape_updated())) {
980  if (child(0).ranges().size() != 2 ||
981  child(0).ranges()[0] != child(0).ranges()[1])
982  ASM_THROW_TENSOR_ERROR("can't symmetrize a non-square tensor "
983  "of sizes " << child(0).ranges());
984  r_ = child(0).ranges();
985  }
986  }
987  void update_childs_required_shape() {
988  tensor_shape ts = req_shape;
989  tensor_shape ts2 = req_shape;
990  index_set perm(2); perm[0] = 1; perm[1] = 0; ts2.permute(perm);
991  ts.merge(ts2, false);
992  tensor_mask dm; dm.set_triangular(ranges()[0],0,1);
993  tensor_shape tsdm(2); tsdm.push_mask(dm);
994  ts.merge(tsdm, true);
995  child(0).merge_required_shape(ts);
996  }
997 
998  private:
999  void reinit_() {
1000  req_shape.set_full(ranges()); // c'est plus simple comme ca
1001  ATN_tensor_w_data::reinit0();
1002  mti.assign(child(0).tensor(),true);
1003  }
1004  void exec_(size_type, dim_type) {
1005  std::fill(data.begin(), data.end(), 0.);
1006  mti.rewind();
1007  index_type n = ranges()[0];
1008  do {
1009  index_type i=mti.index(0), j=mti.index(1);
1010  data[i*n+j]=data[j*n+i]=mti.p(0);
1011  } while (mti.qnext1());
1012  }
1013  };
1014 
1015 
1016  template<class UnaryOp> class ATN_unary_op_tensor
1017  : public ATN_tensor_w_data {
1018  multi_tensor_iterator mti;
1019  public:
1020  ATN_unary_op_tensor(ATN_tensor& a) { add_child(a); }
1021  void check_shape_update(size_type , dim_type) {
1022  if ((shape_updated_ = (ranges() != child(0).ranges())))
1023  r_ = child(0).ranges();
1024  }
1025  private:
1026  void reinit_() {
1027  ATN_tensor_w_data::reinit0();
1028  mti.assign(tensor(), child(0).tensor(),false);
1029  }
1030  void update_cv_(size_type, dim_type) {
1031  mti.rewind();
1032  do {
1033  mti.p(0) = UnaryOp()(mti.p(1));
1034  } while (mti.qnext2());
1035  }
1036  };
1037 
1038  /* sum AND scalar scaling */
1039  class ATN_tensors_sum_scaled : public ATN_tensor_w_data {
1040  std::vector<multi_tensor_iterator> mti;
1041  std::vector<scalar_type> scales; /* utile pour des somme "scaled" du genre 0.5*t1 + 0.5*t2 */
1042  public:
1043  ATN_tensors_sum_scaled(ATN_tensor& t1, scalar_type s1) {
1044  add_child(t1);
1045  scales.resize(1); scales[0]=s1;
1046  }
1047  void push_scaled_tensor(ATN_tensor& t, scalar_type s) {
1048  add_child(t); scales.push_back(s);
1049  }
1050  void check_shape_update(size_type , dim_type) {
1051  if ((shape_updated_ = child(0).is_shape_updated()))
1052  r_ = child(0).ranges();
1053  for (size_type i=1; i < nchilds(); ++i)
1054  if (ranges() != child(i).ranges())
1055  ASM_THROW_TENSOR_ERROR("can't add two tensors of sizes " <<
1056  ranges() << " and " << child(i).ranges());
1057  }
1058  void apply_scale(scalar_type s) {
1059  for (size_type i=0; i < scales.size(); ++i) scales[i] *= s;
1060  }
1061  ATN_tensors_sum_scaled* is_tensors_sum_scaled() { return this; }
1062  private:
1063  void reinit_() {
1064  ATN_tensor_w_data::reinit0();
1065  mti.resize(nchilds());
1066  for (size_type i=0; i < nchilds(); ++i)
1067  mti[i].assign(tensor(), child(i).tensor(),false);
1068  }
1069  void exec_(size_type, dim_type) {
1070  //if (cv == 0) {
1071  // cerr << "ATN_tensors_sum["<< name() << "] req_shape="
1072  // << req_shape << endl;
1073  //}
1074  std::fill(data.begin(), data.end(), 0.);
1075  mti[0].rewind();
1076  do {
1077  mti[0].p(0) = mti[0].p(1)*scales[0];
1078  } while (mti[0].qnext2());
1079  for (size_type i=1; i < nchilds(); ++i) {
1080  mti[i].rewind();
1081  do {
1082  mti[i].p(0) = mti[i].p(0)+mti[i].p(1)*scales[i];
1083  } while (mti[i].qnext2());
1084  }
1085  }
1086  };
1087 
1088  class ATN_tensor_scalar_add : public ATN_tensor_w_data {
1089  scalar_type v;
1090  multi_tensor_iterator mti;
1091  int sgn; /* v+t or v-t ? */
1092  public:
1093  ATN_tensor_scalar_add(ATN_tensor& a, scalar_type v_, int sgn_) :
1094  v(v_), sgn(sgn_) { add_child(a); }
1095  void check_shape_update(size_type , dim_type) {
1096  if ((shape_updated_ = child(0).is_shape_updated()))
1097  r_ = child(0).ranges();
1098  }
1099  private:
1100  void reinit_() {
1101  ATN_tensor_w_data::reinit_();
1102  mti.assign(tensor(), child(0).tensor(),false);
1103  }
1104  void exec_(size_type, dim_type) {
1105  std::fill(data.begin(), data.end(), v);
1106  mti.rewind();
1107  do {
1108  if (sgn > 0)
1109  mti.p(0) += mti.p(1);
1110  else mti.p(0) -= mti.p(1);
1111  } while (mti.qnext2());
1112  }
1113  };
1114 
1115  class ATN_print_tensor : public ATN {
1116  std::string name;
1117  public:
1118  ATN_print_tensor(ATN_tensor& a, std::string n_)
1119  : name(n_) { add_child(a); }
1120  private:
1121  void reinit_() {}
1122  void exec_(size_type cv, dim_type face) {
1123  multi_tensor_iterator mti(child(0).tensor(), true);
1124  cout << "------- > evaluation of " << name << ", at" << endl;
1125  cout << "convex " << cv;
1126  if (face != dim_type(-1)) cout << ", face " << int(face);
1127  cout << endl;
1128  cout << " size = " << child(0).ranges() << endl;
1129  mti.rewind();
1130  do {
1131  cout << " @[";
1132  for (size_type i=0; i < child(0).ranges().size(); ++i)
1133  cout <<(i>0 ? "," : "") << mti.index(dim_type(i));
1134  cout << "] = " << mti.p(0) << endl;
1135  } while (mti.qnext1());
1136  }
1137  };
1138 
1139 
1140  /*
1141  -------------------
1142  analysis of the supplied string
1143  -----------------
1144  */
1145 
1146  std::string asm_tokenizer::syntax_err_print() {
1147  std::string s;
1148  if (tok_pos - err_msg_mark > 80) err_msg_mark = tok_pos - 40;
1149  if (str.length() - err_msg_mark < 80) s = tok_substr(err_msg_mark, str.length());
1150  else { s = tok_substr(err_msg_mark,err_msg_mark+70); s.append(" ... (truncated)"); }
1151  s += "\n" + std::string(std::max(int(tok_pos - err_msg_mark),0), '-') + "^^";
1152  return s;
1153  }
1154 
1155  void asm_tokenizer::get_tok() {
1156  standard_locale sl;
1157  curr_tok_ival = -1;
1158  while (tok_pos < str.length() && isspace(str[tok_pos])) ++tok_pos;
1159  if (tok_pos == str.length()) {
1160  curr_tok_type = END; tok_len = 0;
1161  } else if (strchr("{}(),;:=-.*/+", str[tok_pos])) {
1162  curr_tok_type = tok_type_enum(str[tok_pos]); tok_len = 1;
1163  } else if (str[tok_pos] == '$' || str[tok_pos] == '#' || str[tok_pos] == '%') {
1164  curr_tok_type = str[tok_pos] == '$' ? ARGNUM_SELECTOR :
1165  (str[tok_pos] == '#' ? MFREF : IMREF);
1166  tok_len = 1;
1167  curr_tok_ival = 0;
1168  while (isdigit(str[tok_pos+tok_len])) {
1169  curr_tok_ival*=10;
1170  curr_tok_ival += str[tok_pos+tok_len] - '0';
1171  ++tok_len;
1172  }
1173  curr_tok_ival--;
1174  } else if (isalpha(str[tok_pos])) {
1175  curr_tok_type = IDENT;
1176  tok_len = 0;
1177  while (isalnum(str[tok_pos+tok_len]) || str[tok_pos+tok_len] == '_') ++tok_len;
1178  } else if (isdigit(str[tok_pos])) {
1179  curr_tok_type = NUMBER;
1180  char *p;
1181  curr_tok_dval = strtod(&str[0]+tok_pos, &p);
1182  tok_len = p - &str[0] - tok_pos;
1183  }
1184  if (tok_pos < str.length())
1185  curr_tok = str.substr(tok_pos, tok_len);
1186  else
1187  curr_tok.clear();
1188  }
1189 
1190  const mesh_fem& generic_assembly::do_mf_arg_basic() {
1191  if (tok_type() != MFREF) ASM_THROW_PARSE_ERROR("expecting mesh_fem reference");
1192  if (tok_mfref_num() >= mftab.size())
1193  ASM_THROW_PARSE_ERROR("reference to a non-existant mesh_fem #" << tok_mfref_num()+1);
1194  const mesh_fem& mf_ = *mftab[tok_mfref_num()]; advance();
1195  return mf_;
1196  }
1197 
1198  const mesh_fem& generic_assembly::do_mf_arg(std::vector<const mesh_fem*> * multimf) {
1199  if (!multimf) advance(); // special hack for NonLin$i(#a,#b,..)
1200  accept(OPEN_PAR,"expecting '('");
1201  const mesh_fem &mf_ = do_mf_arg_basic();
1202  if (multimf) {
1203  multimf->resize(1); (*multimf)[0] = &mf_;
1204  while (advance_if(COMMA)) {
1205  if (tok_type() != MFREF) ASM_THROW_PARSE_ERROR("expecting mesh_fem reference");
1206  if (tok_mfref_num() >= mftab.size())
1207  ASM_THROW_PARSE_ERROR("reference to a non-existant mesh_fem #" << tok_mfref_num()+1);
1208  multimf->push_back(mftab[tok_mfref_num()]); advance();
1209  }
1210  }
1211  accept(CLOSE_PAR, "expecting ')'");
1212  return mf_;
1213  }
1214 
1215  /* "inline" reduction operations inside comp(..) */
1216  std::string generic_assembly::do_comp_red_ops() {
1217  std::string s;
1218  if (advance_if(OPEN_PAR)) {
1219  size_type j = 0;
1220  do {
1221  if (tok_type() == COLON) {
1222  s.push_back(' '); advance(); j++;
1223  } else if (tok_type() == IDENT) {
1224  if ((tok().length()==1 && isalpha(tok()[0])) || islower(tok()[0])) {
1225  s.push_back(tok()[0]); advance(); j++;
1226  } else ASM_THROW_PARSE_ERROR("invalid reduction index '" << tok() <<
1227  "', only lower case characters allowed");
1228  }
1229  } while (advance_if(COMMA));
1230  accept(CLOSE_PAR, "expecting ')'");
1231  }
1232  return s;
1233  }
1234 
1235  static mf_comp::field_shape_type get_shape(const std::string &s) {
1236  if (s[0] == 'v') return mf_comp::VECTORIZED_SHAPE;
1237  else if (s[0] == 'm') return mf_comp::MATRIXIZED_SHAPE;
1238  else return mf_comp::PLAIN_SHAPE;
1239  }
1240 
1241  ATN_tensor* generic_assembly::do_comp() {
1242  accept(OPEN_PAR, "expecting '('");
1243  mf_comp_vect what;
1244  bool in_data = false;
1245  /* the first optional argument is the "main" mesh_im, i.e. the one
1246  whose integration methods are used, (and whose linked_mesh is
1247  used for mf_comp::NORMAL, mf_comp::GRADGT etc computations). If
1248  not given, then the first mesh_im pushed is used (then expect
1249  problems when assembling simultaneously on two different
1250  meshes).
1251  */
1252  if (tok_type() == IMREF) {
1253  if (tok_imref_num() >= imtab.size())
1254  ASM_THROW_PARSE_ERROR("reference to a non-existant mesh_im %" << tok_imref_num()+1);
1255  what.set_im(*imtab[tok_imref_num()]); advance();
1256  accept(COMMA, "expecting ','");
1257  } else {
1258  what.set_im(*imtab[0]);
1259  }
1260  do {
1261  if (tok_type() == CLOSE_PAR) break;
1262  if (tok_type() != IDENT) ASM_THROW_PARSE_ERROR("expecting Base or Grad or Hess, Normal, etc..");
1263  std::string f = tok();
1264  const mesh_fem *pmf = 0;
1265  if (f.compare("Base")==0 || f.compare("vBase")==0 || f.compare("mBase")==0) {
1266  pmf = &do_mf_arg();
1267  what.push_back(mf_comp(&what, pmf, mf_comp::BASE, get_shape(f)));
1268  } else if (f.compare("Grad")==0 || f.compare("vGrad")==0 || f.compare("mGrad")==0) {
1269  pmf = &do_mf_arg();
1270  what.push_back(mf_comp(&what, pmf, mf_comp::GRAD, get_shape(f)));
1271  } else if (f.compare("Hess")==0 || f.compare("vHess")==0 || f.compare("mHess")==0) {
1272  pmf = &do_mf_arg();
1273  what.push_back(mf_comp(&what, pmf, mf_comp::HESS, get_shape(f)));
1274  } else if (f.compare("NonLin")==0) {
1275  size_type num = 0; /* default value */
1276  advance();
1277  if (tok_type() == ARGNUM_SELECTOR) { num = tok_argnum(); advance(); }
1278  if (num >= innonlin.size()) ASM_THROW_PARSE_ERROR("NonLin$" << num << " does not exist");
1279  std::vector<const mesh_fem*> allmf;
1280  pmf = &do_mf_arg(&allmf); what.push_back(mf_comp(&what, allmf, innonlin[num]));
1281  } else if (f.compare("Normal") == 0) {
1282  advance();
1283  accept(OPEN_PAR,"expecting '('"); accept(CLOSE_PAR,"expecting ')'");
1284  pmf = 0; what.push_back(mf_comp(&what, pmf, mf_comp::NORMAL, mf_comp::PLAIN_SHAPE));
1285  } else if (f.compare("GradGT") == 0 ||
1286  f.compare("GradGTInv") == 0) {
1287  advance();
1288  accept(OPEN_PAR,"expecting '('"); accept(CLOSE_PAR,"expecting ')'");
1289  pmf = 0;
1290  what.push_back(mf_comp(&what, pmf,
1291  f.compare("GradGT") == 0 ?
1292  mf_comp::GRADGT :
1293  mf_comp::GRADGTINV, mf_comp::PLAIN_SHAPE));
1294  } else {
1295  if (vars.find(f) != vars.end()) {
1296  what.push_back(mf_comp(&what, vars[f]));
1297  in_data = true;
1298  advance();
1299  } else {
1300  ASM_THROW_PARSE_ERROR("expecting Base, Grad, vBase, NonLin ...");
1301  }
1302  }
1303 
1304  if (!in_data && f[0] != 'v' && f[0] != 'm' && pmf && pmf->get_qdim() != 1 && f.compare("NonLin")!=0) {
1305  ASM_THROW_PARSE_ERROR("Attempt to use a vector mesh_fem as a scalar mesh_fem");
1306  }
1307  what.back().reduction = do_comp_red_ops();
1308  } while (advance_if(PRODUCT));
1309  accept(CLOSE_PAR, "expecting ')'");
1310 
1311  return record(std::make_unique<ATN_computed_tensor>(what));
1312  }
1313 
1314  void generic_assembly::do_dim_spec(vdim_specif_list& lst) {
1315  lst.resize(0);
1316  accept(OPEN_PAR, "expecting '('");
1317  while (true) {
1318  if (tok_type() == IDENT) {
1319  if (tok().compare("mdim")==0) lst.push_back(vdim_specif(do_mf_arg().linked_mesh().dim()));
1320  else if (tok().compare("qdim")==0) lst.push_back(vdim_specif(do_mf_arg().get_qdim()));
1321  else ASM_THROW_PARSE_ERROR("expecting mdim(#mf) or qdim(#mf) or a number or a mesh_fem #id");
1322  } else if (tok_type() == NUMBER) {
1323  lst.push_back(vdim_specif(tok_number_ival()+1));
1324  advance();
1325  } else if (tok_type() == MFREF) {
1326  lst.push_back(vdim_specif(&do_mf_arg_basic()));
1327  } else if (tok_type() != CLOSE_PAR) ASM_THROW_PARSE_ERROR("expecting mdim(#mf) or qdim(#mf) or a number or a mesh_fem #id");
1328  /* if (mfcnt && !lst.back().is_mf_ref())
1329  ASM_THROW_PARSE_ERROR("#mf argument must be given after numeric dimensions");*/
1330  if (advance_if(CLOSE_PAR)) break;
1331  accept(COMMA,"expecting ',' or ')'");
1332  }
1333  }
1334 
1335 
1336  ATN_tensor* generic_assembly::do_data() {
1337  // ATN_tensor *t;
1338  size_type datanum = 0; /* par defaut */
1339  if (tok_type() != OPEN_PAR) { /* on peut oublier le numero de dataset */
1340  if (tok_type() != ARGNUM_SELECTOR)
1341  ASM_THROW_PARSE_ERROR("expecting dataset number");
1342  datanum = tok_argnum();
1343  advance();
1344  }
1345  if (datanum >= indata.size())
1346  ASM_THROW_PARSE_ERROR("wrong dataset number: " << datanum);
1347 
1348  vdim_specif_list sz;
1349  do_dim_spec(sz);
1350 
1351  if (sz.nbelt() != indata[datanum]->vect_size())
1352  ASM_THROW_PARSE_ERROR("invalid size for data argument " << datanum+1 <<
1353  " real size is " << indata[datanum]->vect_size()
1354  << " expected size is " << sz.nbelt());
1355  return record(std::make_unique<ATN_tensor_from_dofs_data>(indata[datanum].get(), sz));
1356  }
1357 
1358  std::pair<ATN_tensor*, std::string>
1359  generic_assembly::do_red_ops(ATN_tensor* t) {
1360  std::string s;
1361 
1362  if (advance_if(OPEN_PAR)) {
1363  size_type j = 0;
1364  do {
1365  if (tok_type() == COLON) {
1366  s.push_back(' '); advance(); j++;
1367  } else if (tok_type() == NUMBER) {
1368  t = record(std::make_unique<ATN_sliced_tensor>(*t, dim_type(j),
1369  tok_number_ival())); advance();
1370  } else if (tok_type() == IDENT) {
1371  if ((tok().length()==1 && isalpha(tok()[0])) || islower(tok()[0])) {
1372  s.push_back(tok()[0]); advance(); j++;
1373  } else ASM_THROW_PARSE_ERROR("invalid reduction index '" << tok() <<
1374  "', only lower case chars allowed");
1375  }
1376  } while (advance_if(COMMA));
1377  accept(CLOSE_PAR, "expecting ')'");
1378  }
1379  return std::pair<ATN_tensor*,std::string>(t,s);
1380  }
1381 
1382  /*
1383  ( expr )
1384  variable
1385  comp(..)
1386  data(data)
1387  */
1388  tnode generic_assembly::do_tens() {
1389  tnode t;
1390  push_mark();
1391  if (advance_if(OPEN_PAR)) {
1392  t = do_expr();
1393  accept(CLOSE_PAR, "expecting ')'");
1394  } else if (tok_type() == NUMBER) {
1395  t.assign(tok_number_dval()); advance();
1396  } else if (tok_type() == IDENT) {
1397  if (vars.find(tok()) != vars.end()) {
1398  t.assign(vars[tok()]); advance();
1399  } else if (tok().compare("comp")==0) {
1400  advance(); t.assign(do_comp());
1401  } else if (tok().compare("data")==0) {
1402  advance(); t.assign(do_data());
1403  } else if (tok().compare("sym")==0) {
1404  advance();
1405  tnode t2 = do_expr();
1406  if (t2.type() != tnode::TNTENSOR)
1407  ASM_THROW_PARSE_ERROR("can't symmetrise a scalar!");
1408  t.assign(record(std::make_unique<ATN_symmetrized_tensor>(*t2.tensor())));
1409  } else ASM_THROW_PARSE_ERROR("unknown identifier: " << tok());
1410  } else ASM_THROW_PARSE_ERROR("unexpected token: " << tok());
1411  pop_mark();
1412  return t;
1413  }
1414 
1415  /*
1416  handle tensorial product/reduction
1417 
1418  a(:,i).b(j,i).(c)(1,:,i)
1419  */
1420  tnode generic_assembly::do_prod() {
1421  reduced_tensor_arg_type ttab;
1422 
1423  do {
1424  tnode t = do_tens();
1425  if (t.type() == tnode::TNCONST) {
1426  if (ttab.size() == 0) return t;
1427  else ASM_THROW_PARSE_ERROR("can't mix tensor and scalar into a "
1428  "reduction expression");
1429  }
1430  ttab.push_back(do_red_ops(t.tensor()));
1431  } while (advance_if(PRODUCT));
1432  if (ttab.size() == 1 && ttab[0].second.length() == 0) {
1433  return tnode(ttab[0].first);
1434  } else {
1435  return tnode(record(std::make_unique<ATN_reduced_tensor>(ttab)));
1436  }
1437  }
1438 
1439  /* calls do_prod() once,
1440  and handle successive reordering/diagonals transformations */
1441  tnode generic_assembly::do_prod_trans() {
1442  tnode t = do_prod();
1443  while (advance_if(OPEN_BRACE)) {
1444  index_set reorder;
1445  size_type j = 0;
1446  dal::bit_vector check_permut;
1447  if (t.type() != tnode::TNTENSOR)
1448  ASM_THROW_PARSE_ERROR("can't use reorder braces on a constant!");
1449  for (;; ++j) {
1450  size_type i;
1451  if (tok_type() == COLON) i = j;
1452  else if (tok_type() == NUMBER) i = tok_number_ival(1000);
1453  else ASM_THROW_PARSE_ERROR("only numbers or colons allowed here");
1454  if (check_permut.is_in(i)) { /* on prend la diagonale du tenseur */
1455  t = tnode(record(std::make_unique<ATN_diagonal_tensor>(*t.tensor(), dim_type(i),
1456  dim_type(j))));
1457  check_permut.add(j);
1458  reorder.push_back(dim_type(j));
1459  } else {
1460  check_permut.add(i);
1461  reorder.push_back(dim_type(i));
1462  }
1463  advance();
1464  if (advance_if(CLOSE_BRACE)) break;
1465  accept(COMMA, "expecting ','");
1466  }
1467  if (check_permut.first_false() != reorder.size()) {
1468  cerr << check_permut << endl;
1469  cerr << vref(reorder) << endl;
1470  ASM_THROW_PARSE_ERROR("you did not give a real permutation:"
1471  << vref(reorder));
1472  }
1473  t = tnode(record(std::make_unique<ATN_permuted_tensor>(*t.tensor(), reorder)));
1474  }
1475  return t;
1476  }
1477 
1478  /*
1479  term := prod_trans*prod_trans/prod_trans ...
1480  */
1481  tnode generic_assembly::do_term() {
1482  push_mark();
1483  err_set_mark();
1484  tnode t = do_prod_trans();
1485  while (true) {
1486  bool mult;
1487  if (advance_if(MULTIPLY)) mult = true;
1488  else if (advance_if(DIVIDE)) mult = false;
1489  else break;
1490  tnode t2 = do_prod();
1491  if (mult == false && t.type() == tnode::TNCONST
1492  && t2.type() == tnode::TNTENSOR)
1493  ASM_THROW_PARSE_ERROR("can't divide a constant by a tensor");
1494  if (t.type() == tnode::TNTENSOR && t2.type() == tnode::TNTENSOR) {
1495  ASM_THROW_PARSE_ERROR("tensor term-by-term productor division not "
1496  "implemented yet! are you sure you need it ?");
1497  } else if (t.type() == tnode::TNCONST && t2.type() == tnode::TNCONST) {
1498  if (mult)
1499  t.assign(t.xval()*t2.xval());
1500  else {
1501  t2.check0();
1502  t.assign(t.xval()/t2.xval());
1503  }
1504  } else {
1505  if (t.type() != tnode::TNTENSOR) std::swap(t,t2);
1506  scalar_type v = t2.xval();
1507  if (!mult) {
1508  if (v == 0.) { ASM_THROW_PARSE_ERROR("can't divide by zero"); }
1509  else v = 1./v;
1510  }
1511  if (t.tensor()->is_tensors_sum_scaled() && !t.tensor()->is_frozen()) {
1512  t.tensor()->is_tensors_sum_scaled()->apply_scale(v);
1513  } else {
1514  t.assign(record(std::make_unique<ATN_tensors_sum_scaled>(*t.tensor(), v)));
1515  }
1516  }
1517  }
1518  pop_mark();
1519  return t;
1520  }
1521 
1522  /*
1523  expr := term + term - term + ...
1524  suboptimal for things like t1+1-2-1 (which gives (((t1+1)-2)-1) )
1525  ... could be fixed but noone needs that i guess
1526  */
1527  tnode generic_assembly::do_expr() {
1528  bool negt=false;
1529  push_mark();
1530  if (advance_if(MINUS)) negt = true;
1531  tnode t = do_term();
1532  if (negt) {
1533  if (t.type() == tnode::TNCONST) t.assign(-t.xval());
1534  else t.assign(record(std::make_unique<ATN_tensor_scalar_add>(*t.tensor(), 0., -1)));
1535  }
1536  while (true) {
1537  int plus;
1538  if (advance_if(PLUS)) plus = +1;
1539  else if (advance_if(MINUS)) plus = -1;
1540  else break;
1541  tnode t2 = do_term();
1542  if (t.type() == tnode::TNTENSOR && t2.type() == tnode::TNTENSOR) {
1543  if (!t.tensor()->is_tensors_sum_scaled() || t.tensor()->is_frozen()) {
1544  t.assign(record(std::make_unique<ATN_tensors_sum_scaled>(*t.tensor(), +1)));
1545  }
1546  t.tensor()->is_tensors_sum_scaled()
1547  ->push_scaled_tensor(*t2.tensor(), scalar_type(plus));
1548  } else if (t.type() == tnode::TNCONST && t2.type() == tnode::TNCONST) {
1549  t.assign(t.xval()+t2.xval()*plus);
1550  } else {
1551  int tsgn = 1;
1552  if (t.type() != tnode::TNTENSOR)
1553  { std::swap(t,t2); if (plus<0) tsgn = -1; }
1554  else if (plus<0) t2.assign(-t2.xval());
1555  t.assign(record(std::make_unique<ATN_tensor_scalar_add>(*t.tensor(), t2.xval(),
1556  tsgn)));
1557  }
1558  }
1559  pop_mark();
1560  return t;
1561  }
1562 
1563  /* instr := ident '=' expr |
1564  print expr |
1565  M(#mf,#mf) '+=' expr |
1566  V(#mf) '+=' expr */
1567  void generic_assembly::do_instr() {
1568  enum { wALIAS, wOUTPUT_ARRAY, wOUTPUT_MATRIX, wPRINT, wERROR }
1569  what = wERROR;
1570  std::string ident;
1571 
1572  /* get the rhs */
1573  if (tok_type() != IDENT) ASM_THROW_PARSE_ERROR("expecting identifier");
1574  if (vars.find(tok()) != vars.end())
1575  ASM_THROW_PARSE_ERROR("redefinition of identifier " << tok());
1576 
1577  push_mark();
1578  ident = tok();
1579  advance();
1580 
1581  size_type print_mark = 0;
1582  size_type arg_num = size_type(-1);
1583 
1584  vdim_specif_list vds;
1585 
1586  if (ident.compare("print") == 0) {
1587  print_mark = tok_mark();
1588  what = wPRINT;
1589  } else if (tok_type() == ARGNUM_SELECTOR ||
1590  tok_type() == OPEN_PAR) {
1591  if (tok_type() == ARGNUM_SELECTOR) {
1592  arg_num = tok_argnum();
1593  advance();
1594  } else { arg_num = 0; }
1595 
1596  do_dim_spec(vds);
1597 
1598  /* check the validity of the output statement */
1599  if (ident.compare("V")==0) {
1600  what = wOUTPUT_ARRAY;
1601  if (arg_num >= outvec.size())
1602  { outvec.resize(arg_num+1); outvec[arg_num] = 0; }
1603  /* if we are allowed to dynamically create vectors */
1604  if (outvec[arg_num] == 0) {
1605  if (vec_fact != 0) {
1606  tensor_ranges r(vds.size());
1607  for (size_type i=0; i < vds.size(); ++i)
1608  r[i] = unsigned(vds[i].dim);
1609  outvec[arg_num] = std::shared_ptr<base_asm_vec>(std::shared_ptr<base_asm_vec>(), vec_fact->create_vec(r));
1610  }
1611  else ASM_THROW_PARSE_ERROR("output vector $" << arg_num+1
1612  << " does not exist");
1613  }
1614  } else if (vds.nb_mf()==2 && vds.size() == 2 && ident.compare("M")==0) {
1615  what = wOUTPUT_MATRIX;
1616  if (arg_num >= outmat.size())
1617  { outmat.resize(arg_num+1); outmat[arg_num] = 0; }
1618  /* if we are allowed to dynamically create matrices */
1619  if (outmat[arg_num] == 0) {
1620  if (mat_fact != 0)
1621  outmat[arg_num] = std::shared_ptr<base_asm_mat>
1622  (std::shared_ptr<base_asm_mat>(),
1623  mat_fact->create_mat(vds[0].pmf->nb_dof(),
1624  vds[1].pmf->nb_dof()));
1625  else ASM_THROW_PARSE_ERROR("output matrix $" << arg_num+1
1626  << " does not exist");
1627  }
1628  } else ASM_THROW_PARSE_ERROR("not a valid output statement");
1629 
1630  accept(PLUS);
1631  accept(EQUAL);
1632  } else if (advance_if(EQUAL)) {
1633  what = wALIAS;
1634  } else ASM_THROW_PARSE_ERROR("missing '=' or ':='");
1635 
1636  tnode t = do_expr();
1637  if (t.type() != tnode::TNTENSOR)
1638  ASM_THROW_PARSE_ERROR("left hand side is a constant, not a tensor!");
1639 
1640  switch (what) {
1641  case wPRINT: {
1642  record_out(std::make_unique<ATN_print_tensor>(*t.tensor(), tok_substr(print_mark,
1643  tok_mark())));
1644  } break;
1645  case wOUTPUT_ARRAY: {
1646  record_out(outvec[arg_num]->build_output_tensor(*t.tensor(), vds));
1647  } break;
1648  case wOUTPUT_MATRIX: {
1649  record_out(outmat[arg_num]->build_output_tensor(*t.tensor(),
1650  *vds[0].pmf,
1651  *vds[1].pmf));
1652  } break;
1653  case wALIAS: {
1654  vars[ident] = t.tensor(); t.tensor()->freeze();
1655  } break;
1656  default: GMM_ASSERT3(false, ""); break;
1657  }
1658  pop_mark();
1659  }
1660 
1661  struct atn_number_compare {
1662  bool operator()(const std::unique_ptr<ATN_tensor> &a,
1663  const std::unique_ptr<ATN_tensor> &b) {
1664  assert(a.get() && b.get());
1665  return (a->number() < b->number());
1666  }
1667  };
1668 
1669  struct outvar_compare {
1670  bool operator()(const std::unique_ptr<ATN> &a,
1671  const std::unique_ptr<ATN> &b) {
1672  assert(a.get() && b.get());
1673  return (a->number() < b->number());
1674  }
1675  };
1676 
1677  void generic_assembly::parse() {
1678  if (parse_done) return;
1679  do {
1680  if (tok_type() == END) break;
1681  do_instr();
1682  } while (advance_if(SEMICOLON));
1683  if (tok_type() != END) ASM_THROW_PARSE_ERROR("unexpected token: '"
1684  << tok() << "'");
1685  if (outvars.size() == 0) cerr << "warning: assembly without output\n";
1686 
1687  /* reordering of atn_tensors and outvars */
1688  unsigned gcnt = 0;
1689  for (size_type i=0; i < outvars.size(); ++i)
1690  outvars[i]->set_number(gcnt);
1691 
1692  std::sort(atn_tensors.begin(), atn_tensors.end(), atn_number_compare());
1693  std::sort(outvars.begin(), outvars.end(), outvar_compare());
1694 
1695  /* remove non-numbered (ie unused) atn_tensors */
1696  while (atn_tensors.size()
1697  && atn_tensors.back()->number() == unsigned(-1)) {
1698  cerr << "warning: the expression " << atn_tensors.back()->name()
1699  << " won't be evaluated since it is not used!\n";
1700  atn_tensors.pop_back();
1701  }
1702  parse_done = true;
1703  }
1704 
1705  /* caution: the order of the loops is really important */
1706  void generic_assembly::exec(size_type cv, dim_type face) {
1707  bool update_shapes = false;
1708  for (size_type i=0; i < atn_tensors.size(); ++i) {
1709  atn_tensors[i]->check_shape_update(cv,face);
1710  update_shapes = (update_shapes || atn_tensors[i]->is_shape_updated());
1711  /* if (atn_tensors[i]->is_shape_updated()) {
1712  cerr << "[cv=" << cv << ",f=" << int(face) << "], shape_updated: "
1713  << typeid(*atn_tensors[i]).name()
1714  << " [" << atn_tensors[i]->name()
1715  << "]\n -> r=" << atn_tensors[i]->ranges() << "\n ";
1716  }
1717  */
1718  }
1719 
1720  if (update_shapes) {
1721 
1722  /*cerr << "updated shapes: cv=" << cv << " face=" << int(face) << ": ";
1723  for (size_type k=0; k < mftab.size(); ++k)
1724  cerr << mftab[k]->nb_basic_dof_of_element(cv) << " "; cerr << "\n";
1725  */
1726 
1727  for (auto &&t : atn_tensors)
1728  t->init_required_shape();
1729 
1730  for (auto &&v : outvars)
1731  v->update_childs_required_shape();
1732 
1733  for (size_type i=atn_tensors.size()-1; i!=size_type(-1); --i)
1734  atn_tensors[i]->update_childs_required_shape();
1735 
1736  for (auto &&t : atn_tensors)
1737  t->reinit();
1738 
1739  for (auto &&v : outvars)
1740  v->reinit();
1741  }
1742  for (auto &&t : atn_tensors)
1743  t->exec(cv,face);
1744  for (auto &&v : outvars)
1745  v->exec(cv, face);
1746  }
1747 
1748  struct cv_fem_compare {
1749  const std::vector<const mesh_fem *> &mf;
1750  cv_fem_compare(const std::vector<const mesh_fem *>& mf_) : mf(mf_) {}
1751  bool operator()(size_type a, size_type b) const {
1752  for (size_type i=0; i < mf.size(); ++i) {
1753  pfem pfa(mf[i]->fem_of_element(a));
1754  pfem pfb(mf[i]->fem_of_element(b));
1755  /* sort by nb_dof and then by fem */
1756  unsigned nba = unsigned(pfa->nb_dof(a));
1757  unsigned nbb = unsigned(pfb->nb_dof(b));
1758  if (nba < nbb) {
1759  return true;
1760  } else if (nba > nbb) {
1761  return false;
1762  } else if (pfa < pfb) {
1763  return true;
1764  }
1765  }
1766  return false;
1767  }
1768  };
1769 
1770  /* reorder the convexes in order to minimize the number of
1771  shape modifications during the assembly (since this can be
1772  very expensive) */
1773  static void get_convex_order(const dal::bit_vector& cvlst,
1774  const std::vector<const mesh_im *>& imtab,
1775  const std::vector<const mesh_fem *>& mftab,
1776  const dal::bit_vector& candidates,
1777  std::vector<size_type>& cvorder) {
1778  cvorder.reserve(candidates.card()); cvorder.resize(0);
1779 
1780  for (dal::bv_visitor cv(candidates); !cv.finished(); ++cv) {
1781  if (cvlst.is_in(cv) &&
1782  imtab[0]->int_method_of_element(cv) != im_none()) {
1783  bool ok = true;
1784  for (size_type i=0; i < mftab.size(); ++i) {
1785  if (!mftab[i]->convex_index().is_in(cv)) {
1786  ok = false;
1787  // ASM_THROW_ERROR("the convex " << cv << " has no FEM for the #"
1788  // << i+1 << " mesh_fem");
1789  }
1790  }
1791  if (ok) {
1792  cvorder.push_back(cv);
1793  }
1794  } else if (!imtab[0]->linked_mesh().convex_index().is_in(cv)) {
1795  ASM_THROW_ERROR("the convex " << cv << " is not part of the mesh");
1796  } else {
1797  /* skip convexes without integration method */
1798  }
1799  }
1800  //std::sort(cvorder.begin(), cvorder.end(), cv_fem_compare(mftab));
1801  }
1802 
1803  void generic_assembly::consistency_check() {
1804  //if (mftab.size() == 0) ASM_THROW_ERROR("no mesh_fem for assembly!");
1805  if (imtab.size() == 0)
1806  ASM_THROW_ERROR("no mesh_im (integration methods) given for assembly!");
1807  const mesh& m = imtab[0]->linked_mesh();
1808  for (unsigned i=0; i < mftab.size(); ++i) {
1809  if (&mftab[i]->linked_mesh() != &m)
1810  ASM_THROW_ERROR("the mesh_fem/mesh_im live on different meshes!");
1811  }
1812  for (unsigned i=0; i < imtab.size(); ++i) {
1813  if (&imtab[i]->linked_mesh() != &m)
1814  ASM_THROW_ERROR("the mesh_fem/mesh_im live on different meshes!");
1815  }
1816  if (imtab.size() == 0)
1817  ASM_THROW_ERROR("no integration method !");
1818  }
1819 
1821  std::vector<size_type> cv;
1822  r.from_mesh(imtab.at(0)->linked_mesh());
1823  r.error_if_not_homogeneous();
1824 
1825 
1826  consistency_check();
1827  get_convex_order(imtab.at(0)->convex_index(), imtab, mftab, r.index(), cv);
1828  parse();
1829 
1830  for (size_type i=0; i < cv.size(); ++i) {
1831  mesh_region::face_bitset nf = r[cv[i]];
1832  dim_type f = dim_type(-1);
1833  while (nf.any())
1834  {
1835  if (nf[0]) exec(cv[i],f);
1836  nf >>= 1;
1837  f++;
1838  }
1839  }
1840  }
1841 } /* end of namespace */
void assembly(const mesh_region &region=mesh_region::all_convexes())
do the assembly on the specified region (boundary or set of convexes)
structure used to hold a set of convexes and/or convex faces.
const mesh_region & from_mesh(const mesh &m) const
For regions which have been built with just a number 'id', from_mesh(m) sets the current region to 'm...
const dal::bit_vector & index() const
Index of the region convexes, or the convexes from the partition on the current thread.
Generic assembly implementation.
thread safe standard locale with RAII semantics
elementary computations (used by the generic assembly).
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*‍/
Definition: gmm_blas.h:1663
gmm interface for fortran BLAS.
std::shared_ptr< const getfem::virtual_fem > pfem
type of pointer on a fem description
Definition: getfem_fem.h:243
gmm::uint16_type short_type
used as the common short type integer in the library
Definition: bgeot_config.h:72
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48
std::shared_ptr< const bgeot::geometric_trans > pgeometric_trans
pointer type for a geometric transformation
GEneric Tool for Finite Element Methods.
pmat_elem_type mat_elem_unit_normal(void)
Give a pointer to the structure describing the elementary matrix which compute the unit normal on the...
pmat_elem_type mat_elem_hessian(pfem pfi)
Give a pointer to the structure describing the elementary matrix which compute the integral of the he...
pmat_elem_type mat_elem_nonlinear(pnonlinear_elem_term, std::vector< pfem > pfi)
Give a pointer to the structure describing the elementary matrix which compute the integral of a nonl...
pmat_elem_type mat_elem_base(pfem pfi)
Give a pointer to the structure describing the elementary matrix which compute the integral of the ba...
pmat_elem_type mat_elem_grad_geotrans(bool inverted)
Return the gradient of the geometrical transformation ("K" in the getfem++ kernel doc....
pmat_elem_type mat_elem_product(pmat_elem_type a, pmat_elem_type b)
Give a pointer to the structure describing the elementary matrix which computes the integral of produ...
pintegration_method im_none(void)
return IM_NONE
pmat_elem_type mat_elem_grad(pfem pfi)
Give a pointer to the structure describing the elementary matrix which compute the integral of the gr...