libMesh
Loading...
Searching...
No Matches
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
33namespace 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
50 JxW_all_qp.clear();
51 phi_i_all_qp.clear();
52 dxyzdxi_elem_center.clear();
54 qrule_orders.clear();
55
56 rb_property_map.clear();
57}
58
60:
61requires_xyz_perturbations(false),
62requires_all_elem_qp_data(false),
63requires_all_elem_center_data(false),
64is_lookup_table(false),
65fd_delta(1.e-6),
66_is_nodal_boundary(false)
67{}
68
70
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
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
107Number
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
121std::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
136std::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
153std::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
165void 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
249void 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
335void 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
816void RBParametrizedFunction::get_spatial_indices(std::vector<std::vector<unsigned int>> & /*spatial_indices*/,
817 const VectorizedEvalInput & /*v*/)
818{
819 // No-op by default
820}
821
822void 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
832
833const std::unordered_map<std::string, std::set<dof_id_type>> & RBParametrizedFunction::get_rb_property_map() const
834{
835 return _rb_property_map;
836}
837
838void 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
856
857void 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
867
872
873}
unsigned int dim
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual unsigned short dim() const =0
const Elem * reference_elem() const
Definition elem.C:568
virtual ElemType type() const =0
Point vertex_average() const
Definition elem.C:669
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
This class provides all data required for a physics package (e.g.
Definition fem_context.h:63
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...
const std::set< unsigned char > & elem_dimensions() const
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
unsigned char get_elem_dim() const
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.
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
Order get_order() const
Definition quadrature.h:249
This class is part of the rbOOmit framework.
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.
void set_parametrized_function_boundary_ids(const std::set< boundary_id_type > &boundary_ids, bool is_nodal_boundary)
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.
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 ...
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...
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 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::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.
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().
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 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::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...
virtual void preevaluate_parametrized_function_cleanup()
Virtual function that performs cleanup after each "preevaluate parametrized function" evaluation.
std::vector< std::vector< std::vector< Number > > > preevaluated_values
Storage for pre-evaluated values.
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,...
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 _is_nodal_boundary
In the case that _parametrized_function_boundary_ids is not empty, then this parametrized function is...
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,...
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.
std::unordered_map< std::string, std::set< dof_id_type > > _rb_property_map
Generic property map used to store data during mesh pre-evaluation.
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.
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...
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.
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.
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::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...
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_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 vectorized_evaluate(const std::vector< RBParameters > &mus, const VectorizedEvalInput &v, std::vector< std::vector< std::vector< Number > > > &output)
Vectorized version of evaluate.
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 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_xyz_perturbations
Boolean to indicate whether this parametrized function requires xyz perturbations in order to evaluat...
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.
virtual unsigned int get_n_components() const =0
Specify the number of components in this parametrized function.
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.
bool requires_all_elem_center_data
Boolean to indicate whether this parametrized function requires data from the center on the current e...
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.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
const MeshBase & get_mesh() const
Definition system.h:2401
The libMesh namespace provides an interface to certain functionality in the library.
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:153
int8_t boundary_id_type
Definition id_types.h:51
uint8_t dof_id_type
Definition id_types.h:67
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:176
Define a struct for the input to the "vectorized evaluate" functions below.
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::vector< std::vector< Point > > all_xyz_perturb
void clear()
Clear all the members.
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....
std::vector< unsigned int > qps
std::vector< std::vector< std::vector< Real > > > phi_i_all_qp
std::vector< dof_id_type > elem_ids
std::vector< boundary_id_type > boundary_ids
std::vector< Point > all_xyz
The members that define the inputs to the vectorized evaluate functions.
std::vector< subdomain_id_type > sbd_ids
std::vector< std::vector< Real > > JxW_all_qp
std::vector< std::vector< Real > > phi_i_qp
std::vector< unsigned int > side_indices
std::vector< dof_id_type > node_ids