4#include <boost/signals2/connection.hpp>
6#include <Eigen/Eigenvalues>
9#include <deal.II/base/point.h>
10#include <deal.II/distributed/tria_base.h>
11#include <deal.II/base/quadrature_lib.h>
12#include <deal.II/dofs/dof_handler.h>
13#include <deal.II/dofs/dof_tools.h>
14#include <deal.II/fe/fe_dgq.h>
15#include <deal.II/fe/fe_interface_values.h>
16#include <deal.II/fe/fe_q.h>
17#include <deal.II/fe/fe_values.h>
18#include <deal.II/grid/grid_tools.h>
19#include <deal.II/grid/grid_tools_geometry.h>
20#include <deal.II/lac/affine_constraints.h>
21#include <deal.II/lac/dynamic_sparsity_pattern.h>
22#include <deal.II/lac/full_matrix.h>
23#include <deal.II/lac/sparse_direct.h>
24#include <deal.II/lac/sparse_matrix.h>
25#include <deal.II/lac/sparsity_pattern.h>
26#include <deal.II/lac/vector.h>
27#include <deal.II/meshworker/mesh_loop.h>
28#include <deal.II/numerics/fe_field_function.h>
103 std::array<dealii::Vector<NumberType>, dim * dim>
values;
105 std::vector<dealii::Tensor<2, dim, NumberType>>
samples;
150 const dealii::Point<dim> &)
163 template <
int dim,
typename NumberType>
struct EoMResult {
165 std::optional<ReconstructedEoMPotential<dim, NumberType>>
potential;
170 using namespace dealii;
173 dealii::Point<dim>
get_origin(
const dealii::DoFHandler<dim> &dof_handler,
174 typename dealii::DoFHandler<dim>::cell_iterator &EoM_cell)
176 using CellIterator =
typename dealii::DoFHandler<dim>::cell_iterator;
178 auto l1_norm = [](
const Point<dim> &p) {
180 for (
uint d = 0; d < dim; ++d)
181 norm += std::abs(p[d]);
185 std::vector<Point<dim>> candidates;
187 auto iterate_cell = [&](
const CellIterator &cell) {
188 std::array<Point<dim>, GeometryInfo<dim>::vertices_per_cell> vertices;
189 for (
uint i = 0; i < GeometryInfo<dim>::vertices_per_cell; ++i)
190 vertices[i] = cell->vertex(i);
192 auto min_it = std::min_element(vertices.begin(), vertices.end(),
193 [&](
const auto &p1,
const auto &p2) { return l1_norm(p1) < l1_norm(p2); });
194 candidates.push_back(*min_it);
197 for (
const auto &cell : dof_handler.active_cell_iterators())
200 auto min_it = std::min_element(candidates.begin(), candidates.end(),
201 [&](
const auto &p1,
const auto &p2) { return l1_norm(p1) < l1_norm(p2); });
203 Point<dim> origin = *min_it;
204 EoM_cell = GridTools::find_active_cell_around_point(dof_handler, origin);
218 typename dealii::DoFHandler<dim>::active_cell_iterator
220 const typename dealii::DoFHandler<dim>::active_cell_iterator &source_cell)
222 return typename dealii::DoFHandler<dim>::active_cell_iterator(
223 &target_dof_handler.get_triangulation(), source_cell->level(), source_cell->index(), &target_dof_handler);
226 template <
int dim>
double l1_distance(
const dealii::Point<dim> &a,
const dealii::Point<dim> &b)
228 double distance = 0.;
229 for (
uint d = 0; d < dim; ++d)
230 distance += std::abs(a[d] - b[d]);
236 const double configured_length)
238 if (!std::isfinite(configured_length) || configured_length < -1. ||
239 (configured_length < 0. && configured_length != -1.))
240 throw std::invalid_argument(
241 "EoM_smoothing_length must be -1 (automatic), zero, or a positive physical length.");
244 return configured_length;
255 template <
int dim,
typename NumberType>
256 dealii::types::global_dof_index
257 select_gauge_dof(
const dealii::AffineConstraints<NumberType> &constraints,
const dealii::Point<dim> &origin,
258 const std::map<dealii::types::global_dof_index, dealii::Point<dim>> &support_points)
260 dealii::types::global_dof_index best_dof = numbers::invalid_dof_index;
261 double best_distance = std::numeric_limits<double>::max();
262 for (
const auto &dof_and_point : support_points) {
263 const auto dof = dof_and_point.first;
264 if (constraints.is_constrained(dof))
continue;
266 const double distance =
l1_distance(dof_and_point.second, origin);
267 if (distance < best_distance) {
268 best_distance = distance;
273 if (best_dof == numbers::invalid_dof_index)
274 throw std::runtime_error(
"EoM potential reconstruction could not find an unconstrained gauge DoF.");
281 return std::make_unique<FE_Q<dim>>(2);
286 return std::make_unique<FE_Q<dim>>(order);
316 static constexpr int face_dim = dim > 0 ? dim - 1 : 0;
339 bool describes(
const dealii::Triangulation<dim> &tria,
const dealii::DoFHandler<dim> &solution_dof_handler,
340 const double smoothing,
const uint potential_fe_degree)
const
350 const dealii::DoFHandler<dim> &solution_dof_handler,
const double smoothing)
359 connection = tria.signals.any_change.connect([
this]() {
stale =
true; });
384 if (!slot) slot = std::make_unique<Entry>();
392 template <
int dim,
typename EoMValue> dealii::Tensor<1, dim>
eom_to_tensor(
const EoMValue &
eom)
394 dealii::Tensor<1, dim> out;
395 for (
uint d = 0; d < dim; ++d)
402 dealii::Tensor<1, dim, NumberType>
value;
406 template <
int dim,
typename VectorType,
typename GradientFUN>
407 std::vector<DG0GradientModel<dim, typename VectorType::value_type>>
409 const dealii::Mapping<dim> &mapping,
const GradientFUN &get_gradient)
411 using NumberType =
typename VectorType::value_type;
412 using Iterator =
typename dealii::DoFHandler<dim>::active_cell_iterator;
414 const uint n_cells = solution_dof_handler.get_triangulation().n_active_cells();
415 std::vector<Iterator> cells(n_cells);
416 std::vector<DG0GradientModel<dim, NumberType>> models(n_cells);
418 dealii::QMidpoint<dim> midpoint;
419 dealii::FEValues<dim> values(mapping, solution_dof_handler.get_fe(), midpoint,
420 dealii::update_values | dealii::update_quadrature_points);
421 std::vector<dealii::Vector<NumberType>> solution_values(
422 1, dealii::Vector<NumberType>(solution_dof_handler.get_fe().n_components()));
423 for (
const auto &cell : solution_dof_handler.active_cell_iterators()) {
424 const uint index = cell->active_cell_index();
427 values.get_function_values(sol, solution_values);
428 models[index].center = values.quadrature_point(0);
429 models[index].value =
eom_to_tensor<dim>(get_gradient(models[index].center, solution_values[0]));
432 const uint target_patch_size = std::min<uint>(n_cells, 2 * (dim + 1));
433 for (
uint cell_index = 0; cell_index < n_cells; ++cell_index) {
434 std::vector<uint> patch{cell_index};
435 std::vector<uint> frontier{cell_index};
436 std::vector<bool> seen(n_cells,
false);
437 seen[cell_index] =
true;
439 while (patch.size() < target_patch_size && !frontier.empty()) {
440 std::vector<uint> next_frontier;
441 for (
const uint index : frontier) {
442 std::vector<Iterator> neighbors;
443 dealii::GridTools::get_active_neighbors<dealii::DoFHandler<dim>>(cells[index], neighbors);
444 for (
const auto &neighbor : neighbors) {
445 const uint neighbor_index = neighbor->active_cell_index();
446 if (seen[neighbor_index])
continue;
447 seen[neighbor_index] =
true;
448 patch.push_back(neighbor_index);
449 next_frontier.push_back(neighbor_index);
452 frontier = std::move(next_frontier);
455 if (patch.size() < dim + 1)
continue;
456 const double scale = std::max(cells[cell_index]->diameter(), std::numeric_limits<double>::epsilon());
457 Eigen::MatrixXd design(patch.size(), dim + 1);
458 for (
uint row = 0; row < patch.size(); ++row) {
460 for (
uint d = 0; d < dim; ++d)
461 design(row, d + 1) = (models[patch[row]].center[d] - models[cell_index].center[d]) / scale;
464 Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr(design);
465 if (qr.rank() < dim + 1)
continue;
466 for (
uint component = 0; component < dim; ++component) {
467 Eigen::VectorXd samples(patch.size());
468 for (
uint row = 0; row < patch.size(); ++row)
469 samples[row] = models[patch[row]].value[component];
470 const Eigen::VectorXd coefficients = qr.solve(samples);
471 for (
uint d = 0; d < dim; ++d)
472 models[cell_index].jacobian[component][d] = coefficients[d + 1] / scale;
479 template <
int dim,
typename NumberType>
481 const dealii::Point<dim> &point)
483 auto value = model.
value;
484 for (
uint component = 0; component < dim; ++component)
485 for (
uint d = 0; d < dim; ++d)
486 value[component] += model.
jacobian[component][d] * (point[d] - model.
center[d]);
490 template <
int dim,
typename NumberType>
493 dealii::Tensor<2, dim, NumberType> hessian;
494 for (
uint d = 0; d < dim; ++d)
495 for (
uint e = 0; e < dim; ++e)
510 template <
int dim,
typename NumberType>
512 const dealii::Tensor<2, dim, NumberType> &right,
513 const dealii::Tensor<1, dim> &coordinate_scale)
515 Eigen::Matrix<double, dim, dim> jump;
516 Eigen::Matrix<double, dim, dim> average;
517 for (
uint d = 0; d < dim; ++d)
518 for (
uint e = 0; e < dim; ++e) {
519 jump(d, e) = coordinate_scale[d] *
static_cast<double>(right[d][e] - left[d][e]) * coordinate_scale[e];
521 0.5 * coordinate_scale[d] *
static_cast<double>(right[d][e] + left[d][e]) * coordinate_scale[e];
524 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<double, dim, dim>> eigensolver(jump);
525 if (eigensolver.info() != Eigen::Success)
return {};
528 double leading_value = 0.;
529 double remainder_square = 0.;
530 for (
uint i = 0; i < dim; ++i) {
531 const double value = std::abs(eigensolver.eigenvalues()[i]);
532 if (value > leading_value) {
533 remainder_square += leading_value * leading_value;
534 leading_value = value;
537 remainder_square += value * value;
542 const double epsilon = std::numeric_limits<double>::epsilon();
543 result.
relative_jump = jump.norm() / std::max(average.norm(), epsilon);
545 std::clamp(1. - std::sqrt(remainder_square) / std::max(leading_value, epsilon), 0., 1.);
547 for (
uint d = 0; d < dim; ++d)
548 result.
normal[d] = eigensolver.eigenvectors()(d, leading) / std::max(coordinate_scale[d], epsilon);
549 const double normal_norm = result.
normal.norm();
550 if (normal_norm > 0.) result.
normal /= normal_norm;
554 template <
int dim,
typename NumberType>
557 const dealii::DoFHandler<dim> &solution_dof_handler,
558 const double jump_threshold)
560 using Iterator =
typename dealii::DoFHandler<dim>::active_cell_iterator;
561 const uint n_cells = models.size();
562 std::vector<Iterator> cells(n_cells);
563 std::vector<dealii::Tensor<1, dim>> coordinate_scales(n_cells);
566 recovery.sample_centers.resize(n_cells);
567 recovery.samples.resize(n_cells);
568 recovery.neighbors.resize(n_cells);
569 recovery.face_weights.resize(n_cells);
570 recovery.kink_faces_by_cell.resize(n_cells);
572 for (
const auto &cell : solution_dof_handler.active_cell_iterators()) {
573 const uint index = cell->active_cell_index();
575 recovery.sample_centers[index] = models[index].center;
577 for (
uint d = 0; d < dim; ++d) {
578 double lower = std::numeric_limits<double>::max();
579 double upper = -std::numeric_limits<double>::max();
580 for (
uint vertex = 0; vertex < cell->n_vertices(); ++vertex) {
581 lower = std::min(lower, cell->vertex(vertex)[d]);
582 upper = std::max(upper, cell->vertex(vertex)[d]);
584 coordinate_scales[index][d] = std::max(upper - lower, std::numeric_limits<double>::epsilon());
588 for (
uint index = 0; index < n_cells; ++index) {
589 std::vector<Iterator> neighbors;
590 dealii::GridTools::get_active_neighbors<dealii::DoFHandler<dim>>(cells[index], neighbors);
591 for (
const auto &neighbor : neighbors) {
592 const uint neighbor_index = neighbor->active_cell_index();
593 dealii::Tensor<1, dim> scale;
594 for (
uint d = 0; d < dim; ++d)
595 scale[d] = 0.5 * (coordinate_scales[index][d] + coordinate_scales[neighbor_index][d]);
596 const auto compatibility =
598 const double strength =
599 compatibility.is_rank_one ? compatibility.relative_jump * compatibility.rank_one_confidence : 0.;
600 const double ratio = strength / std::max(jump_threshold, std::numeric_limits<double>::epsilon());
601 const double face_weight = jump_threshold <= 0. ? (strength > 0. ? 0. : 1.) : 1. / (1. + std::pow(ratio, 6.));
602 recovery.neighbors[index].push_back(neighbor_index);
603 recovery.face_weights[index].push_back(face_weight);
604 if (index < neighbor_index && face_weight < 1. - 1e-8) {
605 const auto separation = recovery.sample_centers[neighbor_index] - recovery.sample_centers[index];
606 const double width = separation.norm();
608 const uint face_index = recovery.kink_face_centers.size();
609 recovery.kink_face_centers.push_back(
610 0.5 * (recovery.sample_centers[index] + recovery.sample_centers[neighbor_index]));
611 recovery.kink_face_normals.push_back(compatibility.normal);
612 recovery.kink_face_widths.push_back(width);
613 recovery.kink_face_strengths.push_back(1. - face_weight);
614 recovery.kink_faces_by_cell[index].push_back(face_index);
615 recovery.kink_faces_by_cell[neighbor_index].push_back(face_index);
628 template <
int dim,
typename NumberType>
630 const uint root_cell,
const dealii::Point<dim> &point)
635 if (root_cell >= recovery.kink_faces_by_cell.size())
return result;
636 for (
const uint face : recovery.kink_faces_by_cell[root_cell]) {
637 const double width = recovery.kink_face_widths[face];
638 const double distance = point.distance(recovery.kink_face_centers[face]);
639 const double radius = 1.5 * width;
640 const double weight =
641 recovery.kink_face_strengths[face] * std::exp(-0.5 * distance * distance / (radius * radius));
642 if (weight > result.
weight) {
644 result.
normal = recovery.kink_face_normals[face];
657 template <
int dim,
typename NumberType>
660 const dealii::DoFHandler<dim> &solution_dof_handler)
664 recovery.finite_element = std::make_unique<dealii::FE_Q<dim>>(1);
665 recovery.dof_handler = std::make_unique<dealii::DoFHandler<dim>>(solution_dof_handler.get_triangulation());
666 recovery.dof_handler->distribute_dofs(*recovery.finite_element);
668 const auto n_dofs = recovery.dof_handler->n_dofs();
669 std::vector<std::vector<uint>> incident_cells(n_dofs);
670 std::vector<dealii::types::global_dof_index> local_dof_indices(recovery.finite_element->n_dofs_per_cell());
671 for (
const auto &cell : recovery.dof_handler->active_cell_iterators()) {
672 cell->get_dof_indices(local_dof_indices);
673 for (
const auto dof : local_dof_indices)
674 incident_cells[dof].push_back(cell->active_cell_index());
677 for (
auto &values : recovery.values)
678 values.reinit(n_dofs);
680 for (dealii::types::global_dof_index dof = 0; dof < n_dofs; ++dof) {
681 const auto &cells = incident_cells[dof];
682 if (cells.empty())
continue;
684 for (
uint derivative_direction = 0; derivative_direction < dim; ++derivative_direction) {
685 dealii::Tensor<1, dim, NumberType> mean_trace;
687 for (
const auto cell_index : cells) {
689 dealii::Tensor<1, dim, NumberType> trace;
690 for (
uint component = 0; component < dim; ++component)
691 trace[component] = hessian[component][derivative_direction];
694 mean_trace /=
static_cast<double>(cells.size());
695 for (
uint component = 0; component < dim; ++component)
696 recovery.values[component * dim + derivative_direction][dof] = mean_trace[component];
700 dealii::AffineConstraints<NumberType> constraints;
701 dealii::DoFTools::make_hanging_node_constraints(*recovery.dof_handler, constraints);
703 for (
auto &values : recovery.values)
704 constraints.distribute(values);
708 template <
int dim,
typename NumberType>
709 std::optional<dealii::Tensor<2, dim, NumberType>>
711 const dealii::Point<dim> &point)
715 if (root_cell >= recovery.neighbors.size())
return std::nullopt;
717 struct ConnectedCell {
722 std::vector<ConnectedCell> connected_cells{{root_cell, 0, 1.}};
723 std::deque<uint> frontier{0};
724 constexpr uint max_depth = 3;
725 while (!frontier.empty()) {
726 const uint connected_index = frontier.front();
727 frontier.pop_front();
728 const auto current = connected_cells[connected_index];
729 if (current.depth == max_depth)
continue;
730 for (
uint edge = 0; edge < recovery.neighbors[current.index].size(); ++edge) {
731 const uint neighbor = recovery.neighbors[current.index][edge];
732 const double candidate = current.connectivity * recovery.face_weights[current.index][edge];
733 const auto existing = std::find_if(connected_cells.begin(), connected_cells.end(),
734 [neighbor](
const auto &cell) { return cell.index == neighbor; });
735 if (existing != connected_cells.end()) {
736 if (candidate <= existing->connectivity + 1e-12)
continue;
737 existing->depth = current.depth + 1;
738 existing->connectivity = candidate;
739 frontier.push_back(std::distance(connected_cells.begin(), existing));
740 }
else if (candidate >= 1e-6) {
741 connected_cells.push_back({neighbor, current.depth + 1, candidate});
742 frontier.push_back(connected_cells.size() - 1);
747 double nearest_distance = std::numeric_limits<double>::max();
748 for (
const uint neighbor : recovery.neighbors[root_cell])
750 std::min(nearest_distance, recovery.sample_centers[root_cell].distance(recovery.sample_centers[neighbor]));
751 if (!std::isfinite(nearest_distance))
return std::nullopt;
752 const double fit_radius = 1.5 * nearest_distance;
753 dealii::Tensor<2, dim, NumberType> hessian;
754 double weight_sum = 0.;
755 for (
const auto &cell : connected_cells) {
756 const double distance = point.distance(recovery.sample_centers[cell.index]);
757 const double spatial_weight = std::exp(-0.5 * distance * distance / (fit_radius * fit_radius));
758 const double weight = cell.connectivity * spatial_weight;
759 if (weight < 1e-8)
continue;
760 hessian += weight * recovery.samples[cell.index];
761 weight_sum += weight;
763 if (!(weight_sum > 0.))
return std::nullopt;
764 hessian /= weight_sum;
770 const dealii::FiniteElement<dim> &potential_fe,
771 const dealii::Quadrature<dim> &quadrature,
772 const dealii::Quadrature<dim - 1> &face_quadrature)
773 :
solution_fe_values(mapping, solution_fe, quadrature, update_values | update_quadrature_points),
775 update_gradients | update_JxW_values | update_quadrature_points),
777 update_values | update_quadrature_points),
779 update_values | update_gradients | update_JxW_values |
780 update_quadrature_points | update_normal_vectors)
782 solution_values.resize(quadrature.size(), Vector<NumberType>(solution_fe.n_components()));
806 Vector<NumberType>(n_components));
808 Vector<NumberType>(n_components));
826 template <
class Iterator>
void reinit_cell(
const Iterator &cell,
const uint dofs_per_cell)
838 const uint n_interface_dofs = fe_interface_values.n_current_interface_dofs();
841 data.
matrix.reinit(n_interface_dofs, n_interface_dofs);
842 data.rhs.reinit(n_interface_dofs);
843 data.dof_indices = fe_interface_values.get_interface_dof_indices();
853 template <
int dim,
typename VectorType,
typename EoMFUN>
855 const dealii::DoFHandler<dim> &potential_dof_handler,
856 const dealii::FiniteElement<dim> &potential_fe,
const dealii::Mapping<dim> &mapping,
857 const EoMFUN &get_EoM,
const dealii::Quadrature<dim> &quadrature,
858 const dealii::Quadrature<dim - 1> &face_quadrature,
859 const dealii::AffineConstraints<typename VectorType::value_type> &constraints,
860 dealii::SparseMatrix<typename VectorType::value_type> &matrix,
861 dealii::Vector<typename VectorType::value_type> &rhs,
862 const double smoothing_length,
const bool assemble_matrix)
864 using NumberType =
typename VectorType::value_type;
865 using Iterator =
typename dealii::DoFHandler<dim>::active_cell_iterator;
869 const FEValuesExtractors::Scalar scalar(0);
870 const bool recover_dg0_gradient = solution_dof_handler.get_fe().degree == 0;
871 const auto dg0_gradient_models = recover_dg0_gradient
873 : std::vector<DG0GradientModel<dim, NumberType>>{};
875 const auto evaluate_gradient = [&](
const Iterator &cell,
const dealii::Point<dim> &point,
876 const dealii::Vector<NumberType> &values) {
877 if (recover_dg0_gradient)
882 const auto cell_worker = [&](
const Iterator &potential_cell, Scratch &scratch, Copy ©) {
883 const auto solution_cell =
matching_dof_cell(solution_dof_handler, potential_cell);
884 const uint dofs_per_cell = potential_fe.n_dofs_per_cell();
886 scratch.solution_fe_values.reinit(solution_cell);
887 scratch.potential_fe_values.reinit(potential_cell);
888 scratch.solution_fe_values.get_function_values(sol, scratch.solution_values);
890 copy.reinit_cell(potential_cell, dofs_per_cell);
892 for (
const auto q : scratch.potential_fe_values.quadrature_point_indices()) {
893 const auto eom = evaluate_gradient(solution_cell, scratch.potential_fe_values.quadrature_point(q),
894 scratch.solution_values[q]);
896 for (
uint i = 0; i < dofs_per_cell; ++i) {
897 const auto grad_i = scratch.potential_fe_values.shape_grad(i, q);
898 copy.cell_rhs(i) += scratch.potential_fe_values.JxW(q) * scalar_product(
eom, grad_i);
900 if (!assemble_matrix)
continue;
901 for (
uint j = 0; j < dofs_per_cell; ++j)
902 copy.cell_matrix(i, j) += scratch.potential_fe_values.JxW(q) *
903 scalar_product(scratch.potential_fe_values.shape_grad(j, q), grad_i);
907 const auto boundary_worker = []([[maybe_unused]]
const Iterator &cell, [[maybe_unused]]
const uint &face_no,
908 [[maybe_unused]] Scratch &scratch, [[maybe_unused]] Copy ©) {};
911 const auto copier = [&](
const Copy ©) {
912 if (assemble_matrix) {
913 constraints.distribute_local_to_global(copy.cell_matrix, copy.cell_rhs, copy.cell_dof_indices, matrix, rhs);
914 for (
const auto &face : copy.face_data)
915 constraints.distribute_local_to_global(face.matrix, face.rhs, face.dof_indices, matrix, rhs);
917 constraints.distribute_local_to_global(copy.cell_rhs, copy.cell_dof_indices, rhs);
918 for (
const auto &face : copy.face_data)
919 constraints.distribute_local_to_global(face.rhs, face.dof_indices, rhs);
922 const auto face_worker = [&](
const Iterator &potential_cell,
const uint &face_no,
const uint &subface_no,
923 const Iterator &potential_neighbor,
const uint &neighbor_face_no,
924 const uint &neighbor_subface_no, Scratch &scratch, Copy ©) {
925 const auto solution_cell =
matching_dof_cell(solution_dof_handler, potential_cell);
926 const auto solution_neighbor =
matching_dof_cell(solution_dof_handler, potential_neighbor);
928 scratch.solution_fe_interface_values.reinit(solution_cell, face_no, subface_no, solution_neighbor,
929 neighbor_face_no, neighbor_subface_no);
930 scratch.potential_fe_interface_values.reinit(potential_cell, face_no, subface_no, potential_neighbor,
931 neighbor_face_no, neighbor_subface_no);
933 const auto &solution_fe_interface_values = scratch.solution_fe_interface_values;
934 const auto &potential_fe_interface_values = scratch.potential_fe_interface_values;
936 const uint n_interface_dofs = potential_fe_interface_values.n_current_interface_dofs();
937 auto &face_data = copy.new_face_data(potential_fe_interface_values);
938 const auto potential_view = potential_fe_interface_values[scalar];
939 const auto &solution_fe_values_s = solution_fe_interface_values.get_fe_face_values(0);
940 const auto &solution_fe_values_n = solution_fe_interface_values.get_fe_face_values(1);
941 const auto &q_points = potential_fe_interface_values.get_quadrature_points();
943 auto &solution_values_s = scratch.solution_interface_values[0];
944 auto &solution_values_n = scratch.solution_interface_values[1];
945 solution_fe_values_s.get_function_values(sol, solution_values_s);
946 solution_fe_values_n.get_function_values(sol, solution_values_n);
948 const double h_face = std::min(solution_cell->diameter(), solution_neighbor->diameter());
951 const uint degree = potential_fe.degree;
952 const double tau = 10. * (degree + 1.) * (degree + 1.) / h_face;
953 const double gradient_jump_weight = smoothing_length * smoothing_length / h_normal;
955 for (
const auto q : potential_fe_interface_values.quadrature_point_indices()) {
956 const auto normal = potential_fe_interface_values.normal_vector(q);
957 const auto eom_s = evaluate_gradient(solution_cell, q_points[q], solution_values_s[q]);
958 const auto eom_n = evaluate_gradient(solution_neighbor, q_points[q], solution_values_n[q]);
959 const auto average_eom = 0.5 * (eom_s + eom_n);
960 const double rhs_flux = scalar_product(average_eom, normal);
962 for (
uint i = 0; i < n_interface_dofs; ++i) {
963 const double jump_i = potential_view.jump_in_values(i, q);
964 const Tensor<1, dim> average_grad_i = potential_view.average_of_gradients(i, q);
965 const double normal_gradient_jump_i = scalar_product(potential_view.jump_in_gradients(i, q), normal);
967 face_data.rhs(i) += -potential_fe_interface_values.JxW(q) * rhs_flux * jump_i;
969 if (!assemble_matrix)
continue;
970 for (
uint j = 0; j < n_interface_dofs; ++j) {
971 const double jump_j = potential_view.jump_in_values(j, q);
972 const Tensor<1, dim> average_grad_j = potential_view.average_of_gradients(j, q);
973 const double normal_gradient_jump_j = scalar_product(potential_view.jump_in_gradients(j, q), normal);
975 face_data.matrix(i, j) +=
976 potential_fe_interface_values.JxW(q) *
977 (-scalar_product(average_grad_j, normal) * jump_i - scalar_product(average_grad_i, normal) * jump_j +
978 tau * jump_j * jump_i + gradient_jump_weight * normal_gradient_jump_i * normal_gradient_jump_j);
984 Scratch scratch(mapping, solution_dof_handler.get_fe(), potential_fe, quadrature, face_quadrature);
986 const MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells | MeshWorker::assemble_boundary_faces |
987 MeshWorker::assemble_own_interior_faces_once;
996 MeshWorker::mesh_loop(potential_dof_handler.begin_active(), potential_dof_handler.end(), cell_worker, copier,
997 scratch, copy, flags, boundary_worker, face_worker);
1002 double value = std::numeric_limits<double>::max();
1013 const std::vector<double> &local_values,
1014 const dealii::Point<dim> &point)
1017 for (
uint i = 0; i < potential_fe.n_dofs_per_cell(); ++i) {
1018 evaluation.
value += local_values[i] * potential_fe.shape_value(i, point);
1019 evaluation.
gradient += local_values[i] * potential_fe.shape_grad(i, point);
1020 evaluation.
hessian += local_values[i] * potential_fe.shape_grad_grad(i, point);
1027 const dealii::Tensor<1, dim> &projected_gradient,
1028 const std::array<bool, dim> &fixed, dealii::Tensor<1, dim> &direction)
1030 std::array<uint, dim> free_indices{};
1032 for (
uint d = 0; d < dim; ++d)
1033 if (!fixed[d]) free_indices[n_free++] = d;
1035 if (n_free == 0)
return false;
1037 Eigen::MatrixXd reduced_hessian(n_free, n_free);
1038 Eigen::VectorXd reduced_gradient(n_free);
1039 double hessian_norm = 0.;
1040 for (
uint i = 0; i < n_free; ++i) {
1041 reduced_gradient[i] = projected_gradient[free_indices[i]];
1042 for (
uint j = 0; j < n_free; ++j) {
1043 reduced_hessian(i, j) = hessian[free_indices[i]][free_indices[j]];
1044 hessian_norm = std::max(hessian_norm, std::abs(reduced_hessian(i, j)));
1047 if (!reduced_hessian.allFinite() || !reduced_gradient.allFinite())
return false;
1049 const double positivity_threshold = 100. * std::numeric_limits<double>::epsilon() * std::max(1., hessian_norm);
1050 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(reduced_hessian);
1051 if (solver.info() != Eigen::Success || !solver.eigenvalues().allFinite() ||
1052 !(solver.eigenvalues().minCoeff() > positivity_threshold))
1055 const Eigen::VectorXd reduced_direction =
1056 -solver.eigenvectors() *
1057 (solver.eigenvectors().transpose() * reduced_gradient).cwiseQuotient(solver.eigenvalues());
1058 if (!reduced_direction.allFinite())
return false;
1061 for (
uint i = 0; i < n_free; ++i)
1062 direction[free_indices[i]] = reduced_direction[i];
1066 template <
int dim,
typename NumberType>
1067 PotentialMinimum<dim, NumberType>
1069 const dealii::FiniteElement<dim> &potential_fe,
const dealii::Mapping<dim> &mapping,
1070 const dealii::Vector<NumberType> &potential,
const dealii::Quadrature<dim> &quadrature,
1072 const std::optional<dealii::Point<dim>> &initial_unit_point = std::nullopt)
1074 std::vector<dealii::types::global_dof_index> local_dof_indices(potential_fe.n_dofs_per_cell());
1075 cell->get_dof_indices(local_dof_indices);
1077 std::vector<double> local_values(potential_fe.n_dofs_per_cell());
1078 for (
uint i = 0; i < potential_fe.n_dofs_per_cell(); ++i)
1079 local_values[i] = (
double)potential[local_dof_indices[i]];
1081 dealii::Point<dim> sampled_point = quadrature.point(0);
1083 const auto consider_seed = [&](
const dealii::Point<dim> &candidate) {
1085 if (candidate_evaluation.value < sampled_evaluation.value) {
1086 sampled_point = candidate;
1087 sampled_evaluation = candidate_evaluation;
1091 for (
const auto &support_point : potential_fe.get_unit_support_points())
1092 consider_seed(support_point);
1093 for (
const auto &quadrature_point : quadrature.get_points())
1094 consider_seed(quadrature_point);
1096 const double gradient_tolerance = std::max(
config.abs_tol, 100. * std::numeric_limits<double>::epsilon());
1098 const auto refine_from_seed = [&](dealii::Point<dim> point) {
1100 for (
uint iteration = 0; iteration <
config.max_iter; ++iteration) {
1101 std::array<bool, dim> fixed{};
1102 dealii::Tensor<1, dim> projected_gradient;
1103 double projected_gradient_norm = 0.;
1104 for (
uint d = 0; d < dim; ++d) {
1105 const bool fixed_at_lower = point[d] <=
config.bound_tolerance && evaluation.gradient[d] > 0.;
1106 const bool fixed_at_upper = point[d] >= 1. -
config.bound_tolerance && evaluation.gradient[d] < 0.;
1107 fixed[d] = fixed_at_lower || fixed_at_upper;
1108 projected_gradient[d] = fixed[d] ? 0. : evaluation.gradient[d];
1109 const double extent = std::max(cell->extent_in_direction(d), std::numeric_limits<double>::epsilon());
1110 projected_gradient_norm = std::max(projected_gradient_norm, std::abs(projected_gradient[d]) / extent);
1112 if (projected_gradient_norm <= gradient_tolerance)
break;
1114 dealii::Tensor<1, dim> direction;
1116 double directional_derivative = dealii::scalar_product(evaluation.gradient, direction);
1117 if (!used_newton || !(directional_derivative < 0.) || !std::isfinite(directional_derivative)) {
1118 direction = -projected_gradient;
1119 directional_derivative = dealii::scalar_product(evaluation.gradient, direction);
1120 used_newton =
false;
1122 if (!(directional_derivative < 0.) || !std::isfinite(directional_derivative))
break;
1124 const auto try_step = [&](
const dealii::Tensor<1, dim> &search_direction,
const double search_derivative,
1125 dealii::Point<dim> &accepted_point,
1128 for (
uint d = 0; d < dim; ++d) {
1129 if (search_direction[d] > 0.)
1130 alpha = std::min(alpha, (1. - point[d]) / search_direction[d]);
1131 else if (search_direction[d] < 0.)
1132 alpha = std::min(alpha, -point[d] / search_direction[d]);
1134 if (!(alpha > 0.) || !std::isfinite(alpha))
return false;
1136 for (
uint backtrack = 0; backtrack <
config.max_backtracks; ++backtrack) {
1137 auto candidate = point;
1138 for (
uint d = 0; d < dim; ++d)
1139 candidate[d] = std::clamp(point[d] + alpha * search_direction[d], 0., 1.);
1141 if (candidate_evaluation.value <=
1142 evaluation.value +
config.armijo_coefficient * alpha * search_derivative) {
1143 accepted_point = candidate;
1144 accepted_evaluation = candidate_evaluation;
1152 dealii::Point<dim> next_point;
1154 bool accepted = try_step(direction, directional_derivative, next_point, next_evaluation);
1155 if (!accepted && used_newton) {
1156 direction = -projected_gradient;
1157 directional_derivative = dealii::scalar_product(evaluation.gradient, direction);
1158 if (directional_derivative < 0.)
1159 accepted = try_step(direction, directional_derivative, next_point, next_evaluation);
1161 if (!accepted || !(next_evaluation.
value < evaluation.value))
break;
1164 evaluation = next_evaluation;
1166 return std::pair{point, evaluation};
1169 auto [point, evaluation] = refine_from_seed(sampled_point);
1170 if (initial_unit_point) {
1171 auto warm_start = *initial_unit_point;
1173 for (
uint d = 0; d < dim; ++d) {
1174 valid = valid && std::isfinite(warm_start[d]) && warm_start[d] >= -
config.bound_tolerance &&
1175 warm_start[d] <= 1. +
config.bound_tolerance;
1176 warm_start[d] = std::clamp(warm_start[d], 0., 1.);
1179 auto [warm_point, warm_evaluation] = refine_from_seed(warm_start);
1180 if (warm_evaluation.value < evaluation.value) {
1182 evaluation = warm_evaluation;
1187 return {.point = mapping.transform_unit_to_real_cell(cell, point), .value = evaluation.value};
1190 template <
int dim,
typename NumberType>
1191 PotentialMinimum<dim, NumberType>
1193 const dealii::FiniteElement<dim> &potential_fe,
const dealii::Mapping<dim> &mapping,
1194 const dealii::Vector<NumberType> &potential,
const dealii::Quadrature<dim> &quadrature,
1196 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt,
1197 const std::map<dealii::types::global_dof_index, dealii::Point<dim>> *cached_support_points =
1202 const auto own_support_points = cached_support_points !=
nullptr
1203 ? std::map<dealii::types::global_dof_index, dealii::Point<dim>>{}
1204 : DoFTools::map_dofs_to_support_points(mapping, potential_dof_handler);
1205 const auto &support_points = cached_support_points !=
nullptr ? *cached_support_points : own_support_points;
1206 for (
const auto &dof_and_point : support_points) {
1207 const auto dof = dof_and_point.first;
1208 if ((
double)potential[dof] < minimum.
value) {
1209 minimum.
value = (double)potential[dof];
1210 minimum.
point = dof_and_point.second;
1214 FEValues<dim> potential_fe_values(mapping, potential_fe, quadrature, update_values | update_quadrature_points);
1215 std::vector<NumberType> potential_values(quadrature.size());
1216 for (
const auto &cell : potential_dof_handler.active_cell_iterators()) {
1217 potential_fe_values.reinit(cell);
1218 potential_fe_values.get_function_values(potential, potential_values);
1220 for (
const auto q : potential_fe_values.quadrature_point_indices()) {
1221 if ((
double)potential_values[q] < minimum.
value) {
1222 minimum.
value = (double)potential_values[q];
1223 minimum.
point = potential_fe_values.quadrature_point(q);
1228 const auto minimum_cell =
1229 GridTools::find_active_cell_around_point(mapping, potential_dof_handler, minimum.
point).first;
1230 using CellIterator =
typename dealii::DoFHandler<dim>::active_cell_iterator;
1231 struct CandidateCell {
1233 std::optional<dealii::Point<dim>> initial_unit_point;
1235 std::vector<CandidateCell> candidate_cells;
1236 const auto add_candidate = [&](
const CellIterator &cell,
1237 const std::optional<dealii::Point<dim>> &initial_unit_point = std::nullopt) {
1238 const auto candidate = std::find_if(candidate_cells.begin(), candidate_cells.end(),
1239 [&](
const auto &entry) { return entry.cell == cell; });
1240 if (candidate == candidate_cells.end())
1241 candidate_cells.push_back({cell, initial_unit_point});
1242 else if (initial_unit_point)
1243 candidate->initial_unit_point = initial_unit_point;
1245 const auto add_cell_and_neighbors = [&](
const CellIterator &cell) {
1246 add_candidate(cell);
1247 std::vector<CellIterator> active_neighbors;
1248 GridTools::get_active_neighbors<dealii::DoFHandler<dim>>(cell, active_neighbors);
1249 for (
const auto &neighbor : active_neighbors)
1250 add_candidate(neighbor);
1253 add_cell_and_neighbors(minimum_cell);
1254 if (initial_guess) {
1256 for (
uint d = 0; d < dim; ++d)
1257 finite = finite && std::isfinite((*initial_guess)[d]);
1258 const auto bounding_box = GridTools::compute_bounding_box(potential_dof_handler.get_triangulation());
1259 if (finite && bounding_box.point_inside(*initial_guess)) {
1261 const auto initial_cell =
1262 GridTools::find_active_cell_around_point(mapping, potential_dof_handler, *initial_guess).first;
1263 if (initial_cell != potential_dof_handler.end()) {
1264 add_cell_and_neighbors(initial_cell);
1265 add_candidate(initial_cell, mapping.transform_real_to_unit_cell(initial_cell, *initial_guess));
1267 }
catch (
const dealii::ExceptionBase &) {
1272 for (
const auto &candidate : candidate_cells) {
1273 const auto &cell = candidate.cell;
1275 config, candidate.initial_unit_point);
1276 if (refined.value < minimum.
value) minimum = refined;
1282 template <
int dim,
typename VectorType,
typename GradientFUN>
1284 solve_potential(
const VectorType &sol,
const dealii::DoFHandler<dim> &solution_dof_handler,
1285 const dealii::Mapping<dim> &mapping,
const GradientFUN &get_gradient,
1289 using NumberType =
typename VectorType::value_type;
1292 ++solve_stats.solves;
1295 auto origin_cell = solution_dof_handler.begin_active();
1296 const auto origin =
get_origin(solution_dof_handler, origin_cell);
1312 const auto &potential_triangulation =
serial_mirror(solution_dof_handler.get_triangulation());
1317 auto &system = (cache !=
nullptr ? *cache : uncached).entry(kind);
1322 const bool rebuild =
1323 !system.describes(potential_triangulation, solution_dof_handler, smoothing_length, potential_order);
1326 system.dof_handler = std::make_shared<DoFHandler<dim>>(potential_triangulation);
1327 system.dof_handler->distribute_dofs(*system.finite_element);
1329 system.support_points = DoFTools::map_dofs_to_support_points(mapping, *system.dof_handler);
1331 system.constraints.clear();
1332 DoFTools::make_hanging_node_constraints(*system.dof_handler, system.constraints);
1333 const auto gauge_dof =
select_gauge_dof(system.constraints, origin, system.support_points);
1334 system.constraints.add_line(gauge_dof);
1335 system.constraints.set_inhomogeneity(gauge_dof, 0.);
1336 system.constraints.close();
1338 DynamicSparsityPattern dsp(system.dof_handler->n_dofs());
1339 DoFTools::make_flux_sparsity_pattern(*system.dof_handler, dsp, system.constraints,
1341 system.matrix.clear();
1342 system.sparsity_pattern.copy_from(dsp);
1343 system.matrix.reinit(system.sparsity_pattern);
1345 const uint quadrature_order = std::max<uint>(
1346 std::max<uint>(solution_dof_handler.get_fe().degree, system.finite_element->degree) + 2, 2);
1347 system.quadrature = QGauss<dim>(quadrature_order);
1348 system.face_quadrature = QGauss<dim - 1>(quadrature_order);
1351 Vector<NumberType> rhs(system.dof_handler->n_dofs());
1353 get_gradient, system.quadrature, system.face_quadrature, system.constraints,
1354 system.matrix, rhs, smoothing_length, rebuild);
1357 system.factorization.initialize(system.matrix);
1358 system.note_built_for(potential_triangulation, solution_dof_handler, smoothing_length);
1361 Vector<NumberType> potential(system.dof_handler->n_dofs());
1362 system.factorization.vmult(potential, rhs);
1363 system.constraints.distribute(potential);
1365 return {.finite_element = system.finite_element,
1366 .dof_handler = system.dof_handler,
1367 .values = std::move(potential)};
1370 template <
int dim,
typename VectorType,
typename EoMFUN>
1373 const dealii::DoFHandler<dim> &solution_dof_handler,
const dealii::Mapping<dim> &mapping,
1375 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt,
1384 const uint quadrature_order =
1385 std::max<uint>(std::max<uint>(solution_dof_handler.get_fe().degree, potential.finite_element->degree) + 2, 2);
1386 const std::optional<QGauss<dim>> own_quadrature =
1387 cache !=
nullptr ? std::nullopt : std::optional<QGauss<dim>>(quadrature_order);
1391 *potential.dof_handler, *potential.finite_element, mapping, potential.values,
1392 system !=
nullptr ? system->quadrature : *own_quadrature,
config, initial_guess,
1393 system !=
nullptr ? &system->support_points :
nullptr);
1394 EoM_cell = GridTools::find_active_cell_around_point(solution_dof_handler, minimum.point);
1395 return {.minimum = minimum.point,
1396 .finite_element = std::move(potential.finite_element),
1397 .dof_handler = std::move(potential.dof_handler),
1398 .values = std::move(potential.values)};
1409 template <
int dim,
typename VectorType,
typename GradientFUN>
1410 ReconstructedRawPotential<dim, typename VectorType::value_type>
1412 const dealii::Mapping<dim> &mapping,
const GradientFUN &get_gradient,
1416 static_assert(dim > 0,
"A raw spatial potential cannot be reconstructed in zero dimensions.");
1420 if (
config.raw_potential_recover_mass_hessian && dof_handler.get_fe().degree == 0) {
1423 if (
config.raw_potential_mass_hessian_jump_threshold >= 0.)
1425 config.raw_potential_mass_hessian_jump_threshold);
1436 template <
int dim,
typename NumberType>
1437 RawPotentialEvaluation<dim, NumberType>
1439 const dealii::Mapping<dim> &mapping,
const dealii::Point<dim> &point)
1441 const auto cell = dealii::GridTools::find_active_cell_around_point(mapping, *potential.
dof_handler, point).first;
1442 const auto unit_point = mapping.transform_real_to_unit_cell(cell, point);
1443 dealii::FEValues<dim> fe_values(mapping, *potential.
finite_element, unit_point,
1444 dealii::update_values | dealii::update_gradients | dealii::update_hessians);
1445 fe_values.reinit(cell);
1447 std::vector<NumberType> values(1);
1448 std::vector<dealii::Tensor<1, dim, NumberType>> gradients(1);
1449 std::vector<dealii::Tensor<2, dim, NumberType>> potential_hessians(1);
1450 fe_values.get_function_values(potential.
values, values);
1451 fe_values.get_function_gradients(potential.
values, gradients);
1452 fe_values.get_function_hessians(potential.
values, potential_hessians);
1453 auto mass_hessian = potential_hessians[0];
1457 dealii::FEValues<dim> hessian_fe_values(mapping, *recovery.finite_element, unit_point, dealii::update_values);
1458 hessian_fe_values.reinit(hessian_cell);
1459 for (
uint d = 0; d < dim; ++d)
1460 for (
uint e = 0; e < dim; ++e) {
1461 std::vector<NumberType> component_values(1);
1462 hessian_fe_values.get_function_values(recovery.values[d * dim + e], component_values);
1463 mass_hessian[d][e] = component_values[0];
1465 mass_hessian = 0.5 * (mass_hessian + transpose(mass_hessian));
1466 if (
const auto eom_side_hessian =
1469 const auto correction = *eom_side_hessian - mass_hessian;
1470 const double normal_correction = blend.normal * correction * blend.normal;
1471 mass_hessian += blend.weight * normal_correction * outer_product(blend.normal, blend.normal);
1474 return {.value = values[0],
1475 .gradient = gradients[0],
1476 .potential_hessian = potential_hessians[0],
1477 .mass_hessian = mass_hessian};
1484 template <
int dim,
typename VectorType,
typename EoMFUN,
typename EoMPFUN>
1485 EoMResult<dim, typename VectorType::value_type>
1487 const dealii::DoFHandler<dim> &dof_handler,
const dealii::Mapping<dim> &mapping,
1489 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt,
1497 auto EoM = potential.minimum;
1499 dealii::Vector<typename VectorType::value_type> values(dof_handler.get_fe().n_components());
1500 dealii::Functions::FEFieldFunction<dim, VectorType> fe_function(dof_handler, sol, mapping);
1501 fe_function.set_active_cell(EoM_cell);
1502 fe_function.vector_value(EoM, values);
1504 EoM = EoM_postprocess(EoM, values);
1505 EoM_cell = dealii::GridTools::find_active_cell_around_point(dof_handler, EoM);
1507 return {.point = EoM, .potential = std::move(potential)};
1511 template <
int dim,
typename VectorType,
typename EoMFUN,
typename EoMPFUN>
1513 typename dealii::DoFHandler<dim>::cell_iterator &EoM_cell,
const VectorType &sol,
1514 const dealii::DoFHandler<dim> &dof_handler,
const dealii::Mapping<dim> &mapping,
const EoMFUN &get_EoM,
1515 const EoMPFUN &EoM_postprocess = [](
const auto &p, [[maybe_unused]]
const auto &values) {
return p; },
1519 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt,
1520 internal::PotentialSystemCache<dim, typename VectorType::value_type> *cache =
nullptr)
1523 Config::EoMConfig(EoM_abs_tol, max_iter, EoM_smoothing_length), initial_guess,
1531 template <
int dim,
typename VectorType,
typename EoMFUN,
typename EoMPFUN>
1532 dealii::Point<dim>
get_EoM_point(
typename dealii::DoFHandler<dim>::cell_iterator &EoM_cell,
const VectorType &sol,
1533 const dealii::DoFHandler<dim> &dof_handler,
const dealii::Mapping<dim> &mapping,
1534 const EoMFUN &get_EoM,
const EoMPFUN &EoM_postprocess,
1536 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt,
1541 initial_guess, cache)
1546 template <
int dim,
typename VectorType,
typename EoMFUN,
typename EoMPFUN>
1548 typename dealii::DoFHandler<dim>::cell_iterator &EoM_cell,
const VectorType &sol,
1549 const dealii::DoFHandler<dim> &dof_handler,
const dealii::Mapping<dim> &mapping,
const EoMFUN &get_EoM,
1550 const EoMPFUN &EoM_postprocess = [](
const auto &p, [[maybe_unused]]
const auto &values) {
return p; },
1554 const std::optional<dealii::Point<dim>> &initial_guess = std::nullopt)
1556 return get_EoM_point(EoM_cell, sol, dof_handler, mapping, get_EoM, EoM_postprocess,
1557 Config::EoMConfig(EoM_abs_tol, max_iter, EoM_smoothing_length), initial_guess);
Adds the wall time of its scope to a double, in seconds.
Definition output_timings.hh:97
The mesh-dependent half of solve_potential, retained across calls.
Definition eom.hh:315
std::array< std::unique_ptr< Entry >, n_potential_kinds > entries
Definition eom.hh:389
static constexpr int face_dim
Definition eom.hh:316
Entry & entry(const PotentialKind kind)
The entry for one kind of potential, created empty on first use.
Definition eom.hh:381
constexpr double minimum_rank_one_confidence
Definition eom.hh:507
EoMSideHessianBlend< dim > eom_side_hessian_blend(const ReconstructedRawPotential< dim, NumberType > &potential, const uint root_cell, const dealii::Point< dim > &point)
Definition eom.hh:629
dealii::Tensor< 1, dim, NumberType > evaluate_dg0_gradient_model(const DG0GradientModel< dim, NumberType > &model, const dealii::Point< dim > &point)
Definition eom.hh:480
void prepare_eom_side_hessian_recovery(ReconstructedRawPotential< dim, NumberType > &potential, const std::vector< DG0GradientModel< dim, NumberType > > &models, const dealii::DoFHandler< dim > &solution_dof_handler, const double jump_threshold)
Definition eom.hh:555
std::vector< DG0GradientModel< dim, typename VectorType::value_type > > recover_dg0_gradient_models(const VectorType &sol, const dealii::DoFHandler< dim > &solution_dof_handler, const dealii::Mapping< dim > &mapping, const GradientFUN &get_gradient)
Definition eom.hh:408
std::unique_ptr< dealii::FiniteElement< dim > > make_eom_potential_fe()
Definition eom.hh:279
std::unique_ptr< dealii::FiniteElement< dim > > make_potential_fe(const PotentialKind kind, const uint raw_order)
Definition eom.hh:294
PotentialSolveStats & potential_solve_stats()
Definition eom.hh:70
constexpr uint n_potential_kinds
Definition eom.hh:291
void recover_continuous_hessian_field(ReconstructedRawPotential< dim, NumberType > &potential, const std::vector< DG0GradientModel< dim, NumberType > > &models, const dealii::DoFHandler< dim > &solution_dof_handler)
Definition eom.hh:658
double minimum_face_normal_cell_width(const dealii::DoFHandler< dim > &dof_handler)
The smallest face-normal width anywhere on the mesh.
Definition cell_geometry.hh:51
PotentialMinimum< dim, NumberType > refine_cell_minimum(const typename dealii::DoFHandler< dim >::active_cell_iterator &cell, const dealii::FiniteElement< dim > &potential_fe, const dealii::Mapping< dim > &mapping, const dealii::Vector< NumberType > &potential, const dealii::Quadrature< dim > &quadrature, const Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_unit_point=std::nullopt)
Definition eom.hh:1068
dealii::types::global_dof_index select_gauge_dof(const dealii::AffineConstraints< NumberType > &constraints, const dealii::Point< dim > &origin, const std::map< dealii::types::global_dof_index, dealii::Point< dim > > &support_points)
Definition eom.hh:257
ReconstructedEoMPotential< dim, typename VectorType::value_type > reconstruct_potential(typename dealii::DoFHandler< dim >::cell_iterator &EoM_cell, const VectorType &sol, const dealii::DoFHandler< dim > &solution_dof_handler, const dealii::Mapping< dim > &mapping, const EoMFUN &get_EoM, const Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_guess=std::nullopt, PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Definition eom.hh:1372
LocalPotentialEvaluation< dim > evaluate_local_potential(const dealii::FiniteElement< dim > &potential_fe, const std::vector< double > &local_values, const dealii::Point< dim > &point)
Definition eom.hh:1012
dealii::Point< dim > get_origin(const dealii::DoFHandler< dim > &dof_handler, typename dealii::DoFHandler< dim >::cell_iterator &EoM_cell)
Definition eom.hh:173
std::optional< dealii::Tensor< 2, dim, NumberType > > evaluate_eom_side_hessian(const ReconstructedRawPotential< dim, NumberType > &potential, const uint root_cell, const dealii::Point< dim > &point)
Definition eom.hh:710
Config::EoMConfig resolve_eom_config(const dealii::DoFHandler< dim > &dof_handler, Config::EoMConfig config)
Definition eom.hh:248
ReconstructedRawPotential< dim, typename VectorType::value_type > solve_potential(const VectorType &sol, const dealii::DoFHandler< dim > &solution_dof_handler, const dealii::Mapping< dim > &mapping, const GradientFUN &get_gradient, const Config::EoMConfig &config, const PotentialKind kind, PotentialSystemCache< dim, typename VectorType::value_type > *cache)
Definition eom.hh:1284
PotentialMinimum< dim, NumberType > find_potential_minimum(const dealii::DoFHandler< dim > &potential_dof_handler, const dealii::FiniteElement< dim > &potential_fe, const dealii::Mapping< dim > &mapping, const dealii::Vector< NumberType > &potential, const dealii::Quadrature< dim > &quadrature, const Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_guess=std::nullopt, const std::map< dealii::types::global_dof_index, dealii::Point< dim > > *cached_support_points=nullptr)
Definition eom.hh:1192
dealii::Tensor< 1, dim > eom_to_tensor(const EoMValue &eom)
Definition eom.hh:392
PotentialKind
Which of the two potentials a reconstruction is for; they are cached separately.
Definition eom.hh:290
std::unique_ptr< dealii::FiniteElement< dim > > make_raw_potential_fe(const uint order=2)
Definition eom.hh:284
void assemble_potential_system(const VectorType &sol, const dealii::DoFHandler< dim > &solution_dof_handler, const dealii::DoFHandler< dim > &potential_dof_handler, const dealii::FiniteElement< dim > &potential_fe, const dealii::Mapping< dim > &mapping, const EoMFUN &get_EoM, const dealii::Quadrature< dim > &quadrature, const dealii::Quadrature< dim - 1 > &face_quadrature, const dealii::AffineConstraints< typename VectorType::value_type > &constraints, dealii::SparseMatrix< typename VectorType::value_type > &matrix, dealii::Vector< typename VectorType::value_type > &rhs, const double smoothing_length, const bool assemble_matrix)
Definition eom.hh:854
double face_normal_cell_width(const CellIterator &cell, const uint face_no)
The extent of a cell along the normal of one of its faces.
Definition cell_geometry.hh:23
dealii::Tensor< 2, dim, NumberType > symmetric_jacobian(const DG0GradientModel< dim, NumberType > &model)
Definition eom.hh:491
dealii::DoFHandler< dim >::active_cell_iterator matching_dof_cell(const dealii::DoFHandler< dim > &target_dof_handler, const typename dealii::DoFHandler< dim >::active_cell_iterator &source_cell)
The cell at the same position in another DoFHandler.
Definition eom.hh:219
double resolve_potential_smoothing_length(const dealii::DoFHandler< dim > &dof_handler, const double configured_length)
Definition eom.hh:235
HessianJumpCompatibility< dim > hessian_jump_compatibility(const dealii::Tensor< 2, dim, NumberType > &left, const dealii::Tensor< 2, dim, NumberType > &right, const dealii::Tensor< 1, dim > &coordinate_scale)
Definition eom.hh:511
double l1_distance(const dealii::Point< dim > &a, const dealii::Point< dim > &b)
Definition eom.hh:226
bool projected_newton_direction(const dealii::Tensor< 2, dim > &hessian, const dealii::Tensor< 1, dim > &projected_gradient, const std::array< bool, dim > &fixed, dealii::Tensor< 1, dim > &direction)
Definition eom.hh:1026
Definition complex_math.hh:10
ReconstructedRawPotential< dim, typename VectorType::value_type > reconstruct_raw_potential(const VectorType &sol, const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const GradientFUN &get_gradient, const Config::EoMConfig &config, internal::PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Reconstruct a scalar raw potential without locating its minimum.
Definition eom.hh:1411
@ config
/discretization/threads.
UnusedPotentialEvaluation evaluate_raw_potential(const UnusedPotential &, const dealii::Mapping< dim > &, const dealii::Point< dim > &)
Definition eom.hh:149
unsigned int uint
Definition utils.hh:24
const dealii::Triangulation< dim > & serial_mirror(const dealii::Triangulation< dim > &source)
A process-local, serial mirror of a (possibly partitioned) triangulation.
Definition serial_mirror.hh:47
EoMResult< dim, typename VectorType::value_type > get_EoM_point_with_potential(typename dealii::DoFHandler< dim >::cell_iterator &EoM_cell, const VectorType &sol, const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const EoMFUN &get_EoM, const EoMPFUN &EoM_postprocess, const Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_guess=std::nullopt, internal::PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Reconstruct a potential whose gradient approximates the model EoM vector field and return a sampled a...
Definition eom.hh:1486
dealii::Point< dim > get_EoM_point(typename dealii::DoFHandler< dim >::cell_iterator &EoM_cell, const VectorType &sol, const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const EoMFUN &get_EoM, const EoMPFUN &EoM_postprocess, const Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_guess=std::nullopt, internal::PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Reconstruct a potential whose gradient approximates the model EoM vector field and return a sampled a...
Definition eom.hh:1532
Definition eom_config.hh:10
static constexpr unsigned int default_max_iter
Definition eom_config.hh:12
static constexpr double default_smoothing_length
Definition eom_config.hh:13
static constexpr double default_abs_tol
Definition eom_config.hh:11
Result of finding an EoM point, optionally including its reconstructed potential.
Definition eom.hh:163
std::optional< ReconstructedEoMPotential< dim, NumberType > > potential
Definition eom.hh:165
dealii::Point< dim > point
Definition eom.hh:164
Reconstructed potential data and the explicitly separate Hessian used for mass extraction.
Definition eom.hh:127
dealii::Tensor< 2, dim, NumberType > potential_hessian
Definition eom.hh:130
NumberType value
Definition eom.hh:128
dealii::Tensor< 1, dim, NumberType > gradient
Definition eom.hh:129
dealii::Tensor< 2, dim, NumberType > mass_hessian
Definition eom.hh:131
dealii::Point< 0 > minimum
Definition eom.hh:156
dealii::Vector< NumberType > values
Definition eom.hh:157
An owning scalar potential reconstructed from a model EoM vector field.
Definition eom.hh:83
dealii::Point< dim > minimum
Definition eom.hh:84
dealii::Vector< NumberType > values
Definition eom.hh:87
std::shared_ptr< dealii::DoFHandler< dim > > dof_handler
Definition eom.hh:86
std::shared_ptr< dealii::FiniteElement< dim > > finite_element
Definition eom.hh:85
dealii::Vector< NumberType > values
Definition eom.hh:123
std::unique_ptr< RecoveredMassHessian< dim, NumberType > > recovered_mass_hessian
Definition eom.hh:119
std::shared_ptr< dealii::DoFHandler< dim > > dof_handler
Definition eom.hh:117
std::shared_ptr< dealii::FiniteElement< dim > > finite_element
Definition eom.hh:116
dealii::Vector< NumberType > values
Definition eom.hh:118
A scalar potential reconstructed from a model-provided raw gradient.
Definition eom.hh:100
std::vector< std::vector< double > > face_weights
Definition eom.hh:107
std::unique_ptr< dealii::FiniteElement< dim > > finite_element
Definition eom.hh:101
std::vector< std::vector< uint > > kink_faces_by_cell
Definition eom.hh:108
std::vector< dealii::Tensor< 1, dim > > kink_face_normals
Definition eom.hh:110
std::vector< double > kink_face_widths
Definition eom.hh:111
std::vector< std::vector< uint > > neighbors
Definition eom.hh:106
std::vector< dealii::Point< dim > > kink_face_centers
Definition eom.hh:109
std::array< dealii::Vector< NumberType >, dim *dim > values
Definition eom.hh:103
std::vector< dealii::Point< dim > > sample_centers
Definition eom.hh:104
std::vector< dealii::Tensor< 2, dim, NumberType > > samples
Definition eom.hh:105
std::vector< double > kink_face_strengths
Definition eom.hh:112
std::unique_ptr< dealii::DoFHandler< dim > > dof_handler
Definition eom.hh:102
Evaluating an unread potential: the same three slots, all inert.
Definition eom.hh:144
UnusedPotential gradient
Definition eom.hh:145
UnusedPotential hessian
Definition eom.hh:145
UnusedPotential value
Definition eom.hh:145
Stand-in for a raw potential that a model has declared it does not read.
Definition eom.hh:140
dealii::Tensor< 2, dim, NumberType > jacobian
Definition eom.hh:403
dealii::Tensor< 1, dim, NumberType > value
Definition eom.hh:402
dealii::Point< dim > center
Definition eom.hh:401
double weight
Definition eom.hh:623
dealii::Tensor< 1, dim > normal
Definition eom.hh:624
bool is_rank_one
Definition eom.hh:503
double rank_one_confidence
Definition eom.hh:502
dealii::Tensor< 1, dim > normal
Definition eom.hh:504
double relative_jump
Definition eom.hh:501
double value
Definition eom.hh:1006
dealii::Tensor< 1, dim > gradient
Definition eom.hh:1007
dealii::Tensor< 2, dim > hessian
Definition eom.hh:1008
Vector< NumberType > rhs
Definition eom.hh:822
FullMatrix< NumberType > matrix
Definition eom.hh:821
std::vector< types::global_dof_index > dof_indices
Definition eom.hh:823
Vector< NumberType > cell_rhs
Definition eom.hh:848
std::vector< FaceCopy > face_data
Definition eom.hh:850
FaceCopy & new_face_data(const FEInterfaceValues< dim > &fe_interface_values)
Definition eom.hh:836
void reinit_cell(const Iterator &cell, const uint dofs_per_cell)
Definition eom.hh:826
FullMatrix< NumberType > cell_matrix
Definition eom.hh:847
std::vector< types::global_dof_index > cell_dof_indices
Definition eom.hh:849
std::array< std::vector< Vector< NumberType > >, 2 > solution_interface_values
Definition eom.hh:816
PotentialAssemblyScratch(const PotentialAssemblyScratch &scratch)
Definition eom.hh:787
FEValues< dim > potential_fe_values
Definition eom.hh:812
FEInterfaceValues< dim > potential_fe_interface_values
Definition eom.hh:814
std::vector< Vector< NumberType > > solution_values
Definition eom.hh:815
FEValues< dim > solution_fe_values
Definition eom.hh:811
FEInterfaceValues< dim > solution_fe_interface_values
Definition eom.hh:813
PotentialAssemblyScratch(const dealii::Mapping< dim > &mapping, const dealii::FiniteElement< dim > &solution_fe, const dealii::FiniteElement< dim > &potential_fe, const dealii::Quadrature< dim > &quadrature, const dealii::Quadrature< dim - 1 > &face_quadrature)
Definition eom.hh:769
dealii::Point< dim > point
Definition eom.hh:1001
double value
Definition eom.hh:1002
How much of an output frame went into reconstructing potentials.
Definition eom.hh:65
double seconds
Definition eom.hh:67
std::size_t solves
Definition eom.hh:66
double smoothing_length
Definition eom.hh:370
dealii::Quadrature< face_dim > face_quadrature
Definition eom.hh:327
const void * solution_dofs
Definition eom.hh:365
dealii::Quadrature< dim > quadrature
Definition eom.hh:326
bool describes(const dealii::Triangulation< dim > &tria, const dealii::DoFHandler< dim > &solution_dof_handler, const double smoothing, const uint potential_fe_degree) const
Whether this entry still describes the system that would be built now.
Definition eom.hh:339
std::size_t n_active_cells
Definition eom.hh:366
std::shared_ptr< dealii::DoFHandler< dim > > dof_handler
Definition eom.hh:321
std::size_t n_vertices
Definition eom.hh:368
bool stale
Definition eom.hh:372
std::shared_ptr< dealii::FiniteElement< dim > > finite_element
Definition eom.hh:320
uint solution_fe_degree
Definition eom.hh:369
const void * triangulation
Definition eom.hh:364
dealii::AffineConstraints< NumberType > constraints
Definition eom.hh:322
dealii::SparseDirectUMFPACK factorization
Definition eom.hh:325
std::map< dealii::types::global_dof_index, dealii::Point< dim > > support_points
Where each potential DoF sits; needed by the gauge choice and by every minimum search.
Definition eom.hh:329
dealii::SparsityPattern sparsity_pattern
Definition eom.hh:323
dealii::SparseMatrix< NumberType > matrix
Definition eom.hh:324
boost::signals2::scoped_connection connection
Definition eom.hh:371
void note_built_for(const dealii::Triangulation< dim > &tria, const dealii::DoFHandler< dim > &solution_dof_handler, const double smoothing)
Definition eom.hh:349
uint n_levels
Definition eom.hh:367