libMesh
rb_parametrized_function.C
Go to the documentation of this file.
1 // rbOOmit: An implementation of the Certified Reduced Basis method.
2 // Copyright (C) 2009, 2010 David J. Knezevic
3 
4 // This file is part of rbOOmit.
5 
6 // rbOOmit is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 
11 // rbOOmit is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 
20 // libmesh includes
21 #include "libmesh/rb_parametrized_function.h"
22 #include "libmesh/int_range.h"
23 #include "libmesh/point.h"
24 #include "libmesh/libmesh_logging.h"
25 #include "libmesh/utility.h"
26 #include "libmesh/rb_parameters.h"
27 #include "libmesh/system.h"
28 #include "libmesh/elem.h"
29 #include "libmesh/fem_context.h"
30 #include "libmesh/quadrature.h"
32 
33 namespace libMesh
34 {
35 
37 {
38  all_xyz.clear();
39  elem_ids.clear();
40  qps.clear();
41  sbd_ids.clear();
42  all_xyz_perturb.clear();
43  phi_i_qp.clear();
44  side_indices.clear();
45  boundary_ids.clear();
46  node_ids.clear();
47  elem_types.clear();
48 
49  elem_id_to_local_index.clear();
50  JxW_all_qp.clear();
51  phi_i_all_qp.clear();
52  dxyzdxi_elem_center.clear();
53  dxyzdeta_elem_center.clear();
54  qrule_orders.clear();
55 
56  rb_property_map.clear();
57 }
58 
60 :
61 requires_xyz_perturbations(false),
62 requires_all_elem_qp_data(false),
63 requires_all_elem_center_data(false),
64 is_lookup_table(false),
65 fd_delta(1.e-6),
66 _is_nodal_boundary(false)
67 {}
68 
70 
71 Number
73  unsigned int comp,
74  const Point & xyz,
75  dof_id_type elem_id,
76  unsigned int qp,
77  subdomain_id_type subdomain_id,
78  const std::vector<Point> & xyz_perturb,
79  const std::vector<Real> & phi_i_qp)
80 {
81  std::vector<Number> values = evaluate(mu, xyz, elem_id, qp, subdomain_id, xyz_perturb, phi_i_qp);
82 
83  libmesh_error_msg_if(comp >= values.size(), "Error: Invalid value of comp");
84 
85  return values[comp];
86 }
87 
88 Number
90  unsigned int comp,
91  const Point & xyz,
92  dof_id_type elem_id,
93  unsigned int side_index,
94  unsigned int qp,
95  subdomain_id_type subdomain_id,
96  boundary_id_type boundary_id,
97  const std::vector<Point> & xyz_perturb,
98  const std::vector<Real> & phi_i_qp)
99 {
100  std::vector<Number> values = side_evaluate(mu, xyz, elem_id, side_index, qp, subdomain_id, boundary_id, xyz_perturb, phi_i_qp);
101 
102  libmesh_error_msg_if(comp >= values.size(), "Error: Invalid value of comp");
103 
104  return values[comp];
105 }
106 
107 Number
109  unsigned int comp,
110  const Point & xyz,
111  dof_id_type node_id,
112  boundary_id_type boundary_id)
113 {
114  std::vector<Number> values = node_evaluate(mu, xyz, node_id, boundary_id);
115 
116  libmesh_error_msg_if(comp >= values.size(), "Error: Invalid value of comp");
117 
118  return values[comp];
119 }
120 
121 std::vector<Number>
123  const Point & /*xyz*/,
124  dof_id_type /*elem_id*/,
125  unsigned int /*qp*/,
126  subdomain_id_type /*subdomain_id*/,
127  const std::vector<Point> & /*xyz_perturb*/,
128  const std::vector<Real> & /*phi_i_qp*/)
129 {
130  // This method should be overridden in subclasses, so we just give a not implemented error message here
131  libmesh_not_implemented();
132 
133  return std::vector<Number>();
134 }
135 
136 std::vector<Number>
138  const Point & /*xyz*/,
139  dof_id_type /*elem_id*/,
140  unsigned int /*side_index*/,
141  unsigned int /*qp*/,
142  subdomain_id_type /*subdomain_id*/,
143  boundary_id_type /*boundary_id*/,
144  const std::vector<Point> & /*xyz_perturb*/,
145  const std::vector<Real> & /*phi_i_qp*/)
146 {
147  // This method should be overridden in subclasses, so we just give a not implemented error message here
148  libmesh_not_implemented();
149 
150  return std::vector<Number>();
151 }
152 
153 std::vector<Number>
155  const Point & /*xyz*/,
156  dof_id_type /*node_id*/,
157  boundary_id_type /*boundary_id*/)
158 {
159  // This method should be overridden in subclasses, so we just give a not implemented error message here
160  libmesh_not_implemented();
161 
162  return std::vector<Number>();
163 }
164 
165 void RBParametrizedFunction::vectorized_evaluate(const std::vector<RBParameters> & mus,
166  const VectorizedEvalInput & v,
167  std::vector<std::vector<std::vector<Number>>> & output)
168 {
169  LOG_SCOPE("vectorized_evaluate()", "RBParametrizedFunction");
170 
171  output.clear();
172  unsigned int n_points = v.all_xyz.size();
173 
174  libmesh_error_msg_if(v.sbd_ids.size() != n_points, "Error: invalid vector sizes");
175  libmesh_error_msg_if(requires_xyz_perturbations && (v.all_xyz_perturb.size() != n_points), "Error: invalid vector sizes");
176 
177  // Dummy vector to be used when xyz perturbations are not required
178  std::vector<Point> empty_perturbs;
179 
180  // The number of components returned by this RBParametrizedFunction
181  auto n_components = this->get_n_components();
182 
183  // We first loop over all mus and all n_points, calling evaluate()
184  // for each and storing the results. It is easier to first
185  // pre-compute all the values before filling output, since, in the
186  // case of multi-sample RBParameters, the ordering of the loops is a
187  // bit complicated otherwise.
188  std::vector<std::vector<std::vector<Number>>> all_evals(mus.size());
189  for (auto mu_index : index_range(mus))
190  {
191  // Allocate enough space to store all points for the current mu
192  all_evals[mu_index].resize(n_points);
193  for (auto point_index : index_range(all_evals[mu_index]))
194  {
195  // Evaluate all samples for the current mu at the current interpolation point
196  all_evals[mu_index][point_index] =
197  this->evaluate(mus[mu_index],
198  v.all_xyz[point_index],
199  v.elem_ids[point_index],
200  v.qps[point_index],
201  v.sbd_ids[point_index],
202  requires_xyz_perturbations ? v.all_xyz_perturb[point_index] : empty_perturbs,
203  v.phi_i_qp[point_index]);
204 
205  // The vector returned by evaluate() should contain:
206  // n_components * mus[mu_index].n_samples()
207  // entries. That is, for multi-sample RBParameters objects,
208  // the vector will be packed with entries as follows:
209  // [sample0_component0, sample0_component1, ..., sample0_componentN,
210  // sample1_component0, sample1_component1, ..., sample1_componentN,
211  // ...
212  // sampleM_component0, sampleM_component1, ..., sampleM_componentN]
213  auto n_samples = mus[mu_index].n_samples();
214  auto received_data = all_evals[mu_index][point_index].size();
215  libmesh_error_msg_if(received_data != n_components * n_samples,
216  "Recieved " << received_data <<
217  " evaluated values but expected to receive " << n_components * n_samples);
218  }
219  }
220 
221  // TODO: move this code for computing the total number of samples
222  // represented by a std::vector of RBParameters objects to a helper
223  // function.
224  unsigned int output_size = 0;
225  for (const auto & mu : mus)
226  output_size += mu.n_samples();
227 
228  output.resize(output_size);
229 
230  // We use traditional for-loops here (rather than range-based) so that we can declare and
231  // increment multiple loop counters all within the local scope of the for-loop.
232  for (auto [mu_index, output_index] = std::make_tuple(0u, 0u); mu_index < mus.size(); ++mu_index)
233  {
234  auto n_samples = mus[mu_index].n_samples();
235  for (auto mu_sample_idx = 0u; mu_sample_idx < n_samples; ++mu_sample_idx, ++output_index)
236  {
237  output[output_index].resize(n_points);
238  for (auto point_index : make_range(n_points))
239  {
240  output[output_index][point_index].resize(n_components);
241 
242  for (auto comp : make_range(n_components))
243  output[output_index][point_index][comp] = all_evals[mu_index][point_index][n_components*mu_sample_idx + comp];
244  }
245  }
246  }
247 }
248 
249 void RBParametrizedFunction::side_vectorized_evaluate(const std::vector<RBParameters> & mus,
250  const VectorizedEvalInput & v,
251  std::vector<std::vector<std::vector<Number>>> & output)
252 {
253  LOG_SCOPE("side_vectorized_evaluate()", "RBParametrizedFunction");
254 
255  output.clear();
256  unsigned int n_points = v.all_xyz.size();
257 
258  libmesh_error_msg_if(v.sbd_ids.size() != n_points, "Error: invalid vector sizes");
259  libmesh_error_msg_if(requires_xyz_perturbations && (v.all_xyz_perturb.size() != n_points), "Error: invalid vector sizes");
260 
261  // Dummy vector to be used when xyz perturbations are not required
262  std::vector<Point> empty_perturbs;
263 
264  // The number of components returned by this RBParametrizedFunction
265  auto n_components = this->get_n_components();
266 
267  // We first loop over all mus and all n_points, calling side_evaluate()
268  // for each and storing the results. It is easier to first
269  // pre-compute all the values before filling output, since, in the
270  // case of multi-sample RBParameters, the ordering of the loops is a
271  // bit complicated otherwise.
272  std::vector<std::vector<std::vector<Number>>> all_evals(mus.size());
273  for (auto mu_index : index_range(mus))
274  {
275  // Allocate enough space to store all points for the current mu
276  all_evals[mu_index].resize(n_points);
277  for (auto point_index : index_range(all_evals[mu_index]))
278  {
279  // Evaluate all samples for the current mu at the current interpolation point
280  all_evals[mu_index][point_index] =
281  this->side_evaluate(mus[mu_index],
282  v.all_xyz[point_index],
283  v.elem_ids[point_index],
284  v.side_indices[point_index],
285  v.qps[point_index],
286  v.sbd_ids[point_index],
287  v.boundary_ids[point_index],
288  requires_xyz_perturbations ? v.all_xyz_perturb[point_index] : empty_perturbs,
289  v.phi_i_qp[point_index]);
290 
291  // The vector returned by side_evaluate() should contain:
292  // n_components * mus[mu_index].n_samples()
293  // entries. That is, for multi-sample RBParameters objects,
294  // the vector will be packed with entries as follows:
295  // [sample0_component0, sample0_component1, ..., sample0_componentN,
296  // sample1_component0, sample1_component1, ..., sample1_componentN,
297  // ...
298  // sampleM_component0, sampleM_component1, ..., sampleM_componentN]
299  auto n_samples = mus[mu_index].n_samples();
300  auto received_data = all_evals[mu_index][point_index].size();
301  libmesh_error_msg_if(received_data != n_components * n_samples,
302  "Recieved " << received_data <<
303  " evaluated values but expected to receive " << n_components * n_samples);
304  }
305  }
306 
307  // TODO: move this code for computing the total number of samples
308  // represented by a std::vector of RBParameters objects to a helper
309  // function.
310  unsigned int output_size = 0;
311  for (const auto & mu : mus)
312  output_size += mu.n_samples();
313 
314  output.resize(output_size);
315 
316  // We use traditional for-loops here (rather than range-based) so that we can declare and
317  // increment multiple loop counters all within the local scope of the for-loop.
318  for (auto [mu_index, output_index] = std::make_tuple(0u, 0u); mu_index < mus.size(); ++mu_index)
319  {
320  auto n_samples = mus[mu_index].n_samples();
321  for (auto mu_sample_idx = 0u; mu_sample_idx < n_samples; ++mu_sample_idx, ++output_index)
322  {
323  output[output_index].resize(n_points);
324  for (auto point_index : make_range(n_points))
325  {
326  output[output_index][point_index].resize(n_components);
327 
328  for (auto comp : make_range(n_components))
329  output[output_index][point_index][comp] = all_evals[mu_index][point_index][n_components*mu_sample_idx + comp];
330  }
331  }
332  }
333 }
334 
335 void RBParametrizedFunction::node_vectorized_evaluate(const std::vector<RBParameters> & mus,
336  const VectorizedEvalInput & v,
337  std::vector<std::vector<std::vector<Number>>> & output)
338 {
339  LOG_SCOPE("node_vectorized_evaluate()", "RBParametrizedFunction");
340 
341  output.clear();
342  unsigned int n_points = v.all_xyz.size();
343 
344  // The number of components returned by this RBParametrizedFunction
345  auto n_components = this->get_n_components();
346 
347  // We first loop over all mus and all n_points, calling node_evaluate()
348  // for each and storing the results. It is easier to first
349  // pre-compute all the values before filling output, since, in the
350  // case of multi-sample RBParameters, the ordering of the loops is a
351  // bit complicated otherwise.
352  std::vector<std::vector<std::vector<Number>>> all_evals(mus.size());
353  for (auto mu_index : index_range(mus))
354  {
355  // Allocate enough space to store all points for the current mu
356  all_evals[mu_index].resize(n_points);
357  for (auto point_index : index_range(all_evals[mu_index]))
358  {
359  // Evaluate all samples for the current mu at the current interpolation point
360  all_evals[mu_index][point_index] =
361  this->node_evaluate(mus[mu_index],
362  v.all_xyz[point_index],
363  v.node_ids[point_index],
364  v.boundary_ids[point_index]);
365 
366  // The vector returned by node_evaluate() should contain:
367  // n_components * mus[mu_index].n_samples()
368  // entries. That is, for multi-sample RBParameters objects,
369  // the vector will be packed with entries as follows:
370  // [sample0_component0, sample0_component1, ..., sample0_componentN,
371  // sample1_component0, sample1_component1, ..., sample1_componentN,
372  // ...
373  // sampleM_component0, sampleM_component1, ..., sampleM_componentN]
374  auto n_samples = mus[mu_index].n_samples();
375  auto received_data = all_evals[mu_index][point_index].size();
376  libmesh_error_msg_if(received_data != n_components * n_samples,
377  "Recieved " << received_data <<
378  " evaluated values but expected to receive " << n_components * n_samples);
379  }
380  }
381 
382  // TODO: move this code for computing the total number of samples
383  // represented by a std::vector of RBParameters objects to a helper
384  // function.
385  unsigned int output_size = 0;
386  for (const auto & mu : mus)
387  output_size += mu.n_samples();
388 
389  output.resize(output_size);
390 
391  // We use traditional for-loops here (rather than range-based) so that we can declare and
392  // increment multiple loop counters all within the local scope of the for-loop.
393  for (auto [mu_index, output_index] = std::make_tuple(0u, 0u); mu_index < mus.size(); ++mu_index)
394  {
395  auto n_samples = mus[mu_index].n_samples();
396  for (auto mu_sample_idx = 0u; mu_sample_idx < n_samples; ++mu_sample_idx, ++output_index)
397  {
398  output[output_index].resize(n_points);
399  for (auto point_index : make_range(n_points))
400  {
401  output[output_index][point_index].resize(n_components);
402 
403  for (auto comp : make_range(n_components))
404  output[output_index][point_index][comp] = all_evals[mu_index][point_index][n_components*mu_sample_idx + comp];
405  }
406  }
407  }
408 }
409 
411  const std::unordered_map<dof_id_type, std::vector<Point>> & all_xyz,
412  const std::unordered_map<dof_id_type, subdomain_id_type> & sbd_ids,
413  const std::unordered_map<dof_id_type, std::vector<std::vector<Point>> > & all_xyz_perturb,
414  const System & sys)
415 {
417 
418  unsigned int n_elems = all_xyz.size();
419  unsigned int n_points = 0;
420  for (const auto & xyz_pair : all_xyz)
421  {
422  const std::vector<Point> & xyz_vec = xyz_pair.second;
423  n_points += xyz_vec.size();
424  }
425 
427  v.all_xyz.resize(n_points);
428  v.elem_ids.resize(n_points);
429  v.qps.resize(n_points);
430  v.sbd_ids.resize(n_points);
431  v.all_xyz_perturb.resize(n_points);
432  v.phi_i_qp.resize(n_points);
433  v.elem_types.resize(n_points);
434 
436  {
437  v.elem_id_to_local_index.clear();
438  v.JxW_all_qp.resize(n_elems);
439  v.phi_i_all_qp.resize(n_elems);
440  }
442  {
443  v.dxyzdxi_elem_center.resize(n_elems);
444  v.dxyzdeta_elem_center.resize(n_elems);
445  // At the time of writing, the quadrature order is only used in conjunction with element
446  // center data so we should not compute it elsewhere for now.
447  v.qrule_orders.resize(n_elems);
448  }
449 
450  // Empty vector to be used when xyz perturbations are not required
451  std::vector<Point> empty_perturbs;
452 
453  // In order to compute phi_i_qp, we initialize a FEMContext
454  FEMContext con(sys);
455  for (auto dim : con.elem_dimensions())
456  {
457  auto fe = con.get_element_fe(/*var=*/0, dim);
458  fe->get_JxW();
459  fe->get_phi();
460  fe->get_dxyzdxi();
461  fe->get_dxyzdeta();
462  }
463 
464  unsigned int counter = 0;
465  unsigned int elem_counter = 0;
466  for (const auto & [elem_id, xyz_vec] : all_xyz)
467  {
468  subdomain_id_type subdomain_id = libmesh_map_find(sbd_ids, elem_id);
469 
470  // The amount of data to be stored for each component
471  auto n_qp = xyz_vec.size();
472  mesh_to_preevaluated_values_map[elem_id].resize(n_qp);
473 
474  // Also initialize phi in order to compute phi_i_qp
475  const Elem & elem_ref = sys.get_mesh().elem_ref(elem_id);
476  con.pre_fe_reinit(sys, &elem_ref);
477 
478  auto elem_fe = con.get_element_fe(/*var=*/0, elem_ref.dim());
479  const std::vector<std::vector<Real>> & phi = elem_fe->get_phi();
480  const std::vector<Real> & JxW = elem_fe->get_JxW();
481  const auto & dxyzdxi = elem_fe->get_dxyzdxi();
482  const auto & dxyzdeta = elem_fe->get_dxyzdeta();
483 
484  elem_fe->reinit(&elem_ref);
485 
486  for (auto qp : index_range(xyz_vec))
487  {
488  mesh_to_preevaluated_values_map[elem_id][qp] = counter;
489 
490  v.all_xyz[counter] = xyz_vec[qp];
491  v.elem_ids[counter] = elem_id;
492  v.qps[counter] = qp;
493  v.sbd_ids[counter] = subdomain_id;
494  v.elem_types[counter] = elem_ref.type();
495 
496  v.phi_i_qp[counter].resize(phi.size());
497  for(auto i : index_range(phi))
498  v.phi_i_qp[counter][i] = phi[i][qp];
499 
501  {
502  const auto & qps_and_perturbs =
503  libmesh_map_find(all_xyz_perturb, elem_id);
504  libmesh_error_msg_if(qp >= qps_and_perturbs.size(), "Error: Invalid qp");
505 
506  v.all_xyz_perturb[counter] = qps_and_perturbs[qp];
507  }
508  else
509  {
510  v.all_xyz_perturb[counter] = empty_perturbs;
511  }
512 
514  {
515  if (v.elem_id_to_local_index.count(elem_id) == 0)
516  {
517  // In this case we store data for all qps on this element
518  // at each point.
519  v.JxW_all_qp[elem_counter].resize(JxW.size());
520  for(auto i : index_range(JxW))
521  v.JxW_all_qp[elem_counter][i] = JxW[i];
522 
523  v.phi_i_all_qp[elem_counter].resize(phi.size());
524  for(auto i : index_range(phi))
525  {
526  v.phi_i_all_qp[elem_counter][i].resize(phi[i].size());
527  for(auto j : index_range(phi[i]))
528  v.phi_i_all_qp[elem_counter][i][j] = phi[i][j];
529  }
530  v.elem_id_to_local_index[elem_id] = elem_counter;
531  }
532  }
533 
534  counter++;
535  }
536 
537  // Here we presume that if requires_all_elem_center_data is set to true
538  // then requires_all_elem_qp_data is also set to true so that elem_id_to_local_index entry
539  // for this elem has already been set.
541  {
542  // Get data derivatives at vertex average
543  std::vector<Point> nodes = { elem_ref.reference_elem()->vertex_average() };
544  elem_fe->reinit (&elem_ref, &nodes);
545  // Set qrule_order here to prevent calling getter multiple times.
546  Order qrule_order = con.get_element_qrule().get_order();
547 
548  // We add qrule_order in this loop as it is used in conjunction with elem center
549  // quantities for now.
550  v.qrule_orders[elem_counter] = qrule_order;
551  Point dxyzdxi_pt, dxyzdeta_pt;
552  if (con.get_elem_dim()>0)
553  dxyzdxi_pt = dxyzdxi[0];
554  if (con.get_elem_dim()>1)
555  dxyzdeta_pt = dxyzdeta[0];
556  // Here we do an implicit conversion from RealGradient which is a VectorValue<Real>
557  // which in turn is a TypeVector<T> to a Point which is a TypeVector<Real>.
558  // They are essentially the same thing. This helps us limiting the number of includes
559  // in serialization and deserialization as RealGradient is a typedef and we cannot
560  // forward declare typedefs. As a result we leverage the fact that point.h is already
561  // included in most places we need RealGradient.
562  v.dxyzdxi_elem_center[elem_counter] = dxyzdxi_pt;
563  v.dxyzdeta_elem_center[elem_counter] = dxyzdeta_pt;
564  }
565  elem_counter++;
566  }
567 
568  std::vector<RBParameters> mus {mu};
570 
572 }
573 
575  const std::map<std::pair<dof_id_type,unsigned int>, std::vector<Point>> & side_all_xyz,
576  const std::map<std::pair<dof_id_type,unsigned int>, subdomain_id_type> & sbd_ids,
577  const std::map<std::pair<dof_id_type,unsigned int>, boundary_id_type> & side_boundary_ids,
578  const std::map<std::pair<dof_id_type,unsigned int>, unsigned int> & side_types,
579  const std::map<std::pair<dof_id_type,unsigned int>, std::vector<std::vector<Point>> > & side_all_xyz_perturb,
580  const System & sys)
581 {
583 
584  unsigned int n_points = 0;
585  for (const auto & xyz_pair : side_all_xyz)
586  {
587  const std::vector<Point> & xyz_vec = xyz_pair.second;
588  n_points += xyz_vec.size();
589  }
590 
592  v.all_xyz.resize(n_points);
593  v.elem_ids.resize(n_points);
594  v.side_indices.resize(n_points);
595  v.qps.resize(n_points);
596  v.sbd_ids.resize(n_points);
597  v.boundary_ids.resize(n_points);
598  v.all_xyz_perturb.resize(n_points);
599  v.phi_i_qp.resize(n_points);
600 
601  // Empty vector to be used when xyz perturbations are not required
602  std::vector<Point> empty_perturbs;
603 
604  // In order to compute phi_i_qp, we initialize a FEMContext
605  FEMContext con(sys);
606  for (auto dim : con.elem_dimensions())
607  {
608  auto fe = con.get_element_fe(/*var=*/0, dim);
609  fe->get_phi();
610 
611  auto side_fe = con.get_side_fe(/*var=*/0, dim);
612  side_fe->get_phi();
613  }
614 
615  unsigned int counter = 0;
616  for (const auto & xyz_pair : side_all_xyz)
617  {
618  auto elem_side_pair = xyz_pair.first;
619  dof_id_type elem_id = elem_side_pair.first;
620  unsigned int side_index = elem_side_pair.second;
621 
622  const std::vector<Point> & xyz_vec = xyz_pair.second;
623 
624  subdomain_id_type subdomain_id = libmesh_map_find(sbd_ids, elem_side_pair);
625  boundary_id_type boundary_id = libmesh_map_find(side_boundary_ids, elem_side_pair);
626  unsigned int side_type = libmesh_map_find(side_types, elem_side_pair);
627 
628  // The amount of data to be stored for each component
629  auto n_qp = xyz_vec.size();
630  mesh_to_preevaluated_side_values_map[elem_side_pair].resize(n_qp);
631 
632  const Elem & elem_ref = sys.get_mesh().elem_ref(elem_id);
633  con.pre_fe_reinit(sys, &elem_ref);
634 
635  // side_type == 0 --> standard side
636  // side_type == 1 --> shellface
637  if (side_type == 0)
638  {
639  std::unique_ptr<const Elem> elem_side;
640  elem_ref.build_side_ptr(elem_side, side_index);
641 
642  auto side_fe = con.get_side_fe(/*var=*/0, elem_ref.dim());
643  side_fe->reinit(&elem_ref, side_index);
644 
645  const std::vector<std::vector<Real>> & phi = side_fe->get_phi();
646  for (auto qp : index_range(xyz_vec))
647  {
648  mesh_to_preevaluated_side_values_map[elem_side_pair][qp] = counter;
649 
650  v.all_xyz[counter] = xyz_vec[qp];
651  v.elem_ids[counter] = elem_side_pair.first;
652  v.side_indices[counter] = elem_side_pair.second;
653  v.qps[counter] = qp;
654  v.sbd_ids[counter] = subdomain_id;
655  v.boundary_ids[counter] = boundary_id;
656 
657  v.phi_i_qp[counter].resize(phi.size());
658  for(auto i : index_range(phi))
659  v.phi_i_qp[counter][i] = phi[i][qp];
660 
662  {
663  const auto & qps_and_perturbs =
664  libmesh_map_find(side_all_xyz_perturb, elem_side_pair);
665  libmesh_error_msg_if(qp >= qps_and_perturbs.size(), "Error: Invalid qp");
666 
667  v.all_xyz_perturb[counter] = qps_and_perturbs[qp];
668  }
669  else
670  {
671  v.all_xyz_perturb[counter] = empty_perturbs;
672  }
673  counter++;
674  }
675  }
676  else if (side_type == 1)
677  {
678  auto elem_fe = con.get_element_fe(/*var=*/0, elem_ref.dim());
679  const std::vector<std::vector<Real>> & phi = elem_fe->get_phi();
680 
681  elem_fe->reinit(&elem_ref);
682 
683  for (auto qp : index_range(xyz_vec))
684  {
685  mesh_to_preevaluated_side_values_map[elem_side_pair][qp] = counter;
686 
687  v.all_xyz[counter] = xyz_vec[qp];
688  v.elem_ids[counter] = elem_side_pair.first;
689  v.side_indices[counter] = elem_side_pair.second;
690  v.qps[counter] = qp;
691  v.sbd_ids[counter] = subdomain_id;
692  v.boundary_ids[counter] = boundary_id;
693 
694  v.phi_i_qp[counter].resize(phi.size());
695  for(auto i : index_range(phi))
696  v.phi_i_qp[counter][i] = phi[i][qp];
697 
699  {
700  const auto & qps_and_perturbs =
701  libmesh_map_find(side_all_xyz_perturb, elem_side_pair);
702  libmesh_error_msg_if(qp >= qps_and_perturbs.size(), "Error: Invalid qp");
703 
704  v.all_xyz_perturb[counter] = qps_and_perturbs[qp];
705  }
706  else
707  {
708  v.all_xyz_perturb[counter] = empty_perturbs;
709  }
710  counter++;
711  }
712  }
713  else
714  libmesh_error_msg ("Unrecognized side_type: " << side_type);
715  }
716 
717  std::vector<RBParameters> mus {mu};
719 
721 }
722 
724  const std::unordered_map<dof_id_type, Point> & all_xyz,
725  const std::unordered_map<dof_id_type, boundary_id_type> & node_boundary_ids,
726  const System & /*sys*/)
727 {
729 
730  unsigned int n_points = all_xyz.size();
731 
733  v.all_xyz.resize(n_points);
734  v.node_ids.resize(n_points);
735  v.boundary_ids.resize(n_points);
736 
737  // Empty vector to be used when xyz perturbations are not required
738  std::vector<Point> empty_perturbs;
739 
740  unsigned int counter = 0;
741  for (const auto & [node_id, p] : all_xyz)
742  {
743  boundary_id_type boundary_id = libmesh_map_find(node_boundary_ids, node_id);
744 
745  mesh_to_preevaluated_node_values_map[node_id] = counter;
746 
747  v.all_xyz[counter] = p;
748  v.node_ids[counter] = node_id;
749  v.boundary_ids[counter] = boundary_id;
750 
751  counter++;
752  }
753 
754  std::vector<RBParameters> mus {mu};
756 
758 }
759 
761  dof_id_type elem_id,
762  unsigned int qp) const
763 {
764  const std::vector<unsigned int> & indices_at_qps =
765  libmesh_map_find(mesh_to_preevaluated_values_map, elem_id);
766 
767  libmesh_error_msg_if(qp >= indices_at_qps.size(), "Error: invalid qp");
768 
769  unsigned int index = indices_at_qps[qp];
770  libmesh_error_msg_if(preevaluated_values.size() != 1, "Error: we expect only one parameter index");
771  libmesh_error_msg_if(index >= preevaluated_values[0].size(), "Error: invalid index");
772 
773  return preevaluated_values[0][index][comp];
774 }
775 
777  dof_id_type elem_id,
778  unsigned int side_index,
779  unsigned int qp) const
780 {
781  const std::vector<unsigned int> & indices_at_qps =
782  libmesh_map_find(mesh_to_preevaluated_side_values_map, std::make_pair(elem_id,side_index));
783 
784  libmesh_error_msg_if(qp >= indices_at_qps.size(), "Error: invalid qp");
785 
786  unsigned int index = indices_at_qps[qp];
787  libmesh_error_msg_if(preevaluated_values.size() != 1, "Error: we expect only one parameter index");
788  libmesh_error_msg_if(index >= preevaluated_values[0].size(), "Error: invalid index");
789 
790  return preevaluated_values[0][index][comp];
791 }
792 
794  dof_id_type node_id) const
795 {
796  unsigned int index =
797  libmesh_map_find(mesh_to_preevaluated_node_values_map, node_id);
798 
799  libmesh_error_msg_if(preevaluated_values.size() != 1, "Error: we expect only one parameter index");
800  libmesh_error_msg_if(index >= preevaluated_values[0].size(), "Error: invalid index");
801 
802  return preevaluated_values[0][index][comp];
803 }
804 
806 {
807  // No-op by default, override in subclasses as needed
808 }
809 
811  subdomain_id_type sbd_id) const
812 {
813  return libmesh_map_find(libmesh_map_find(_parameter_independent_data, property_name), sbd_id);
814 }
815 
816 void RBParametrizedFunction::get_spatial_indices(std::vector<std::vector<unsigned int>> & /*spatial_indices*/,
817  const VectorizedEvalInput & /*v*/)
818 {
819  // No-op by default
820 }
821 
822 void RBParametrizedFunction::initialize_spatial_indices(const std::vector<std::vector<unsigned int>> & /*spatial_indices*/,
823  const VectorizedEvalInput & /*v*/)
824 {
825  // No-op by default
826 }
827 
829 {
830  // No-op by default
831 }
832 
833 const std::unordered_map<std::string, std::set<dof_id_type>> & RBParametrizedFunction::get_rb_property_map() const
834 {
835  return _rb_property_map;
836 }
837 
838 void RBParametrizedFunction::add_rb_property_map_entry(std::string & property_name, std::set<dof_id_type> & entity_ids)
839 {
840  bool insert_succeed = _rb_property_map.insert({property_name, entity_ids}).second;
841  libmesh_error_msg_if(!insert_succeed, "Entry already added, duplicate detected.");
842 }
843 
845  const Parallel::Communicator & /*comm*/,
846  std::unordered_map<std::string, std::set<dof_id_type>> & /*rb_property_map*/,
847  dof_id_type /*elem_id*/)
848 {
849  // No-op by default
850 }
851 
852 const std::set<boundary_id_type> & RBParametrizedFunction::get_parametrized_function_boundary_ids() const
853 {
855 }
856 
857 void RBParametrizedFunction::set_parametrized_function_boundary_ids(const std::set<boundary_id_type> & boundary_ids, bool is_nodal_boundary)
858 {
860  _is_nodal_boundary = is_nodal_boundary;
861 }
862 
864 {
866 }
867 
869 {
871 }
872 
873 }
virtual unsigned int get_n_components() const =0
Specify the number of components in this parametrized function.
virtual void get_spatial_indices(std::vector< std::vector< unsigned int >> &spatial_indices, const VectorizedEvalInput &v)
In some cases a parametrized function is defined based on array data that we index into based on the ...
std::vector< ElemType > elem_types
virtual void preevaluate_parametrized_function_on_mesh_sides(const RBParameters &mu, const std::map< std::pair< dof_id_type, unsigned int >, std::vector< Point >> &side_all_xyz, const std::map< std::pair< dof_id_type, unsigned int >, subdomain_id_type > &sbd_ids, const std::map< std::pair< dof_id_type, unsigned int >, boundary_id_type > &side_boundary_ids, const std::map< std::pair< dof_id_type, unsigned int >, unsigned int > &side_types, const std::map< std::pair< dof_id_type, unsigned int >, std::vector< std::vector< Point >> > &side_all_xyz_perturb, const System &sys)
Same as preevaluate_parametrized_function_on_mesh() except for mesh sides.
virtual void initialize_spatial_indices(const std::vector< std::vector< unsigned int >> &spatial_indices, const VectorizedEvalInput &v)
The Online stage counterpart of get_spatial_indices().
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
std::vector< Point > dxyzdeta_elem_center
void get_side_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge/face (2D/3D) finite element object for variable var for the largest dimension in th...
Definition: fem_context.h:317
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
Definition: fem_context.C:1683
std::vector< std::vector< Real > > JxW_all_qp
virtual void add_interpolation_data_to_rb_property_map(const Parallel::Communicator &comm, std::unordered_map< std::string, std::set< dof_id_type >> &rb_property_map, dof_id_type elem_id)
Virtual function that can be overridden in RBParametrizedFunction subclasses to store properties in t...
void set_parametrized_function_boundary_ids(const std::set< boundary_id_type > &boundary_ids, bool is_nodal_boundary)
std::unordered_map< dof_id_type, unsigned int > mesh_to_preevaluated_node_values_map
Indexing into preevaluated_values for the case where the preevaluated values were obtained from evalu...
const std::set< boundary_id_type > & get_parametrized_function_boundary_ids() const
For RBParametrizedFunctions defined on element sides or nodes, we get/set the boundary IDs that this ...
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
std::vector< unsigned int > qps
unsigned char get_elem_dim() const
Definition: fem_context.h:944
unsigned int dim
std::set< boundary_id_type > _parametrized_function_boundary_ids
In the case of an RBParametrizedFunction defined on element sides, this defines the set of boundary I...
std::vector< std::vector< Real > > phi_i_qp
std::map< dof_id_type, unsigned int > elem_id_to_local_index
The following containers are indexed by element id to avoid duplicated data.
std::unordered_map< std::string, std::set< dof_id_type > > _rb_property_map
Generic property map used to store data during mesh pre-evaluation.
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
virtual void side_vectorized_evaluate(const std::vector< RBParameters > &mus, const VectorizedEvalInput &v, std::vector< std::vector< std::vector< Number >>> &output)
Same as vectorized_evaluate() but on element sides.
std::vector< dof_id_type > elem_ids
The libMesh namespace provides an interface to certain functionality in the library.
const MeshBase & get_mesh() const
Definition: system.h:2358
Define a struct for the input to the "vectorized evaluate" functions below.
const std::unordered_map< std::string, std::set< dof_id_type > > & get_rb_property_map() const
Function that returns a reference to the rb_property_map stored in the RBParametrizedFunction.
virtual void preevaluate_parametrized_function_on_mesh(const RBParameters &mu, const std::unordered_map< dof_id_type, std::vector< Point >> &all_xyz, const std::unordered_map< dof_id_type, subdomain_id_type > &sbd_ids, const std::unordered_map< dof_id_type, std::vector< std::vector< Point >> > &all_xyz_perturb, const System &sys)
Store the result of vectorized_evaluate.
virtual Number lookup_preevaluated_value_on_mesh(unsigned int comp, dof_id_type elem_id, unsigned int qp) const
Look up the preevaluate values of the parametrized function for component comp, element elem_id...
std::vector< subdomain_id_type > sbd_ids
void add_rb_property_map_entry(std::string &property_name, std::set< dof_id_type > &entity_ids)
Function that adds a property to the RBParametrizedFunction rb_property_map.
virtual Number side_evaluate_comp(const RBParameters &mu, unsigned int comp, const Point &xyz, dof_id_type elem_id, unsigned int side_index, unsigned int qp, subdomain_id_type subdomain_id, boundary_id_type boundary_id, const std::vector< Point > &xyz_perturb, const std::vector< Real > &phi_i_qp)
Same as evaluate_comp() but for element sides.
std::vector< unsigned int > side_indices
int8_t boundary_id_type
Definition: id_types.h:51
std::unordered_map< std::string, std::set< dof_id_type > > rb_property_map
Generic map that can be used to store any list of ids (elements, nodes, elemsets, subdomains...
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
virtual Number lookup_preevaluated_side_value_on_mesh(unsigned int comp, dof_id_type elem_id, unsigned int side_index, unsigned int qp) const
Look up the preevaluated values of the parametrized function for component comp, element elem_id...
virtual Number node_evaluate_comp(const RBParameters &mu, unsigned int comp, const Point &xyz, dof_id_type node_id, boundary_id_type boundary_id)
Same as evaluate_comp() but for element nodes.
std::vector< Point > all_xyz
The members that define the inputs to the vectorized evaluate functions.
Order get_order() const
Definition: quadrature.h:249
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
const Elem * reference_elem() const
Definition: elem.C:570
virtual std::vector< Number > side_evaluate(const RBParameters &mu, const Point &xyz, dof_id_type elem_id, unsigned int side_index, unsigned int qp, subdomain_id_type subdomain_id, boundary_id_type boundary_id, const std::vector< Point > &xyz_perturb, const std::vector< Real > &phi_i_qp)
Same as evaluate() but for element sides.
std::map< std::string, std::map< subdomain_id_type, Number > > _parameter_independent_data
In some cases we need to store parameter-independent data which is related to this function but since...
bool requires_all_elem_qp_data
Boolean to indicate whether this parametrized function requires data from all qps on the current elem...
virtual void preevaluate_parametrized_function_cleanup()
Virtual function that performs cleanup after each "preevaluate parametrized function" evaluation...
This class is part of the rbOOmit framework.
Definition: rb_parameters.h:52
std::vector< std::vector< std::vector< Real > > > phi_i_all_qp
virtual Number evaluate_comp(const RBParameters &mu, unsigned int comp, const Point &xyz, dof_id_type elem_id, unsigned int qp, subdomain_id_type subdomain_id, const std::vector< Point > &xyz_perturb, const std::vector< Real > &phi_i_qp)
Evaluate the parametrized function at the specified point for parameter mu.
bool requires_all_elem_center_data
Boolean to indicate whether this parametrized function requires data from the center on the current e...
std::vector< Point > dxyzdxi_elem_center
std::map< std::pair< dof_id_type, unsigned int >, std::vector< unsigned int > > mesh_to_preevaluated_side_values_map
Similar to the above except this map stores the data on element sides.
bool requires_xyz_perturbations
Boolean to indicate whether this parametrized function requires xyz perturbations in order to evaluat...
virtual void preevaluate_parametrized_function_on_mesh_nodes(const RBParameters &mu, const std::unordered_map< dof_id_type, Point > &all_xyz, const std::unordered_map< dof_id_type, boundary_id_type > &node_boundary_ids, const System &sys)
Same as preevaluate_parametrized_function_on_mesh() except for mesh nodes.
void clear()
Clear all the members.
bool _is_nodal_boundary
In the case that _parametrized_function_boundary_ids is not empty, then this parametrized function is...
Number get_parameter_independent_data(const std::string &property_name, subdomain_id_type sbd_id) const
Get the value stored in _parameter_independent_data associated with region_name and property_name...
std::vector< std::vector< std::vector< Number > > > preevaluated_values
Storage for pre-evaluated values.
std::vector< boundary_id_type > boundary_ids
virtual unsigned short dim() const =0
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:639
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
std::vector< std::vector< Point > > all_xyz_perturb
virtual void node_vectorized_evaluate(const std::vector< RBParameters > &mus, const VectorizedEvalInput &v, std::vector< std::vector< std::vector< Number >>> &output)
Same as vectorized_evaluate() but on element nodes.
virtual Number lookup_preevaluated_node_value_on_mesh(unsigned int comp, dof_id_type node_id) const
Look up the preevaluate values of the parametrized function for component comp, node node_id...
virtual std::vector< Number > evaluate(const RBParameters &mu, const Point &xyz, dof_id_type elem_id, unsigned int qp, subdomain_id_type subdomain_id, const std::vector< Point > &xyz_perturb, const std::vector< Real > &phi_i_qp)
Evaluate the parametrized function at the specified point for parameter mu.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh...
Definition: fem_context.h:277
std::unordered_map< dof_id_type, std::vector< unsigned int > > mesh_to_preevaluated_values_map
Indexing into preevaluated_values for the case where the preevaluated values were obtained from evalu...
virtual void initialize_lookup_table()
If this parametrized function is defined based on a lookup table then we can call this function to in...
virtual ElemType type() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
Definition: fem_context.h:802
Point vertex_average() const
Definition: elem.C:688
const std::set< unsigned char > & elem_dimensions() const
Definition: fem_context.h:951
virtual std::vector< Number > node_evaluate(const RBParameters &mu, const Point &xyz, dof_id_type node_id, boundary_id_type boundary_id)
Same as evaluate() but for element nodes.
std::vector< dof_id_type > node_ids
uint8_t dof_id_type
Definition: id_types.h:67
virtual void vectorized_evaluate(const std::vector< RBParameters > &mus, const VectorizedEvalInput &v, std::vector< std::vector< std::vector< Number >>> &output)
Vectorized version of evaluate.