1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
// Copyright (C) 2006-2022 Anders Logg and Garth N. Wells
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "Topology.h"
#include <algorithm>
#include <basix/mdspan.hpp>
#include <concepts>
#include <cstdint>
#include <dolfinx/common/IndexMap.h>
#include <dolfinx/common/MPI.h>
#include <dolfinx/common/sort.h>
#include <dolfinx/fem/CoordinateElement.h>
#include <dolfinx/fem/ElementDofLayout.h>
#include <dolfinx/fem/dofmapbuilder.h>
#include <dolfinx/graph/AdjacencyList.h>
#include <dolfinx/graph/partition.h>
#include <functional>
#include <memory>
#include <span>
#include <utility>
#include <vector>
namespace dolfinx::mesh
{
/// @brief Geometry stores the geometry imposed on a mesh.
template <std::floating_point T>
class Geometry
{
public:
/// @brief Value type
using value_type = T;
/// @brief Constructor of object that holds mesh geometry data.
///
/// The geometry 'degree-of-freedom map' is a logically rectangular
/// array (row-major) where each row corresponds to a cell, and the
/// columns are the indices in the coordinate array of the cell
/// coordinate degree-of-freedom. For each cell type there is a
/// geometry degree-of-freedom map.
///
/// @param[in] index_map Index map associated with the geometry
/// degrees-of-freedom.
/// @param[in] dofmaps The geometry (point) dofmaps for each cell type
/// in the mesh. For a cell of a given type, the dofmap gives the
/// position in the point array of each local geometry node of the
/// cell. Each cell type has its own dofmap. Each dofmap uses
/// row-major storage.
/// @param[in] elements Elements that describe cell geometry maps.
/// `element[i]` is the coordinate element associated with
/// `dofmaps[i]`.
/// @param[in] x Point coordinates. The shape is `(num_points, 3)` and
/// the storage is row-major.
/// @param[in] dim The geometric dimension (`0 < dim <= 3`).
/// @param[in] input_global_indices 'Global' input index of each
/// point, commonly from a mesh input file.
template <typename U, typename V, typename W>
requires std::is_convertible_v<std::remove_cvref_t<U>,
std::vector<std::vector<std::int32_t>>>
and std::is_convertible_v<std::remove_cvref_t<V>,
std::vector<T>>
and std::is_convertible_v<std::remove_cvref_t<W>,
std::vector<std::int64_t>>
Geometry(
std::shared_ptr<const common::IndexMap> index_map, U&& dofmaps,
const std::vector<fem::CoordinateElement<
typename std::remove_reference_t<typename V::value_type>>>& elements,
V&& x, int dim, W&& input_global_indices)
: _dim(dim), _dofmaps(std::forward<U>(dofmaps)),
_index_map(std::move(index_map)), _cmaps(elements),
_x(std::forward<V>(x)),
_input_global_indices(std::forward<W>(input_global_indices))
{
assert(_x.size() % 3 == 0);
if (_x.size() / 3 != _input_global_indices.size())
throw std::runtime_error("Geometry size mismatch.");
if (_dofmaps.size() != _cmaps.size())
{
throw std::runtime_error("Geometry number of dofmaps not equal to the "
"number of coordinate elements.");
}
// TODO: check that elements dim == number of dofmap columns
}
/// Copy constructor
Geometry(const Geometry&) = default;
/// Move constructor
Geometry(Geometry&&) = default;
/// Destructor
~Geometry() = default;
/// Copy assignment
Geometry& operator=(const Geometry&) = delete;
/// Move assignment
Geometry& operator=(Geometry&&) = default;
/// @brief Return dimension of the Euclidean coordinate system.
int dim() const { return _dim; }
/// @brief DofMap for the geometry.
/// @return A 2D array with shape `(num_cells, dofs_per_cell)`.
md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>> dofmap() const
{
if (_dofmaps.size() != 1)
throw std::runtime_error("Multiple dofmaps");
return this->dofmap(0);
}
/// @brief Degree-of-freedom map associated with the `i`th coordinate
/// map element in the geometry.
/// @param[in] i Index of the requested degree-of-freedom map. The
/// degree-of-freedom map corresponds to the geometry element
/// `cmaps()[i]`.
/// @return A dofmap array, with shape `(num_cells, dofs_per_cell)`.
md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>
dofmap(std::size_t i) const
{
std::size_t ndofs = _cmaps.at(i).dim();
return md::mdspan<const std::int32_t, md::dextents<std::size_t, 2>>(
_dofmaps.at(i).data(), _dofmaps.at(i).size() / ndofs, ndofs);
}
/// @brief Index map for the geometry 'degrees-of-freedom'.
/// @return Index map for the geometry dofs.
std::shared_ptr<const common::IndexMap> index_map() const
{
return _index_map;
}
/// @brief Access geometry degrees-of-freedom data (const version).
///
/// @return The flattened row-major geometry data, where the shape is
/// `(num_points, 3)`.
std::span<const value_type> x() const { return _x; }
/// @brief Access geometry degrees-of-freedom data (non-const
/// version).
///
/// @return The flattened row-major geometry data, where the shape is
/// `(num_points, 3)`.
std::span<value_type> x() { return _x; }
/// @brief The elements that describes the geometry map.
///
/// The coordinate element `cmaps()[i]` corresponds to the
/// degree-of-freedom map `dofmap(i)`.
///
/// @return The coordinate/geometry elements.
const std::vector<fem::CoordinateElement<value_type>>& cmaps() const
{
return _cmaps;
}
/// @brief The element that describes the geometry map.
///
/// @return The coordinate/geometry element
const fem::CoordinateElement<value_type>& cmap() const
{
if (_cmaps.size() > 1)
throw std::runtime_error("Multiple cmaps.");
return _cmaps.front();
}
/// @brief Global user indices.
const std::vector<std::int64_t>& input_global_indices() const
{
return _input_global_indices;
}
private:
// Geometric dimension
int _dim;
// Map per cell for extracting coordinate data for each cmap
std::vector<std::vector<std::int32_t>> _dofmaps;
// IndexMap for geometry 'dofmap'
std::shared_ptr<const common::IndexMap> _index_map;
// The coordinate elements
std::vector<fem::CoordinateElement<value_type>> _cmaps;
// Coordinates for all points stored as a contiguous array (row-major,
// column size = 3)
std::vector<value_type> _x;
// Global indices as provided on Geometry creation
std::vector<std::int64_t> _input_global_indices;
};
/// @cond
/// Template type deduction
template <typename U, typename V, typename W>
Geometry(std::shared_ptr<const common::IndexMap>, U&&,
const std::vector<fem::CoordinateElement<
typename std::remove_reference_t<typename V::value_type>>>&,
V&&, int, W&&)
-> Geometry<typename std::remove_cvref_t<typename V::value_type>>;
/// @endcond
/// @brief Build Geometry from input data.
///
/// This function should be called after the mesh topology is built and
/// 'node' coordinate data has been distributed to the processes where
/// it is required.
///
/// @param[in] topology Mesh topology.
/// @param[in] elements List of elements that defines the geometry map for
/// each cell type.
/// @param[in] nodes Geometry node global indices for cells on this
/// process. @pre Must be sorted.
/// @param[in] xdofs Geometry degree-of-freedom map (using global
/// indices) for cells on this process. `nodes` is a sorted and unique
/// list of the indices in `xdofs`.
/// @param[in] x The node coordinates (row-major, with shape
/// `(num_nodes, dim)`. The global index of each node is `i +
/// rank_offset`, where `i` is the local row index in `x` and
/// `rank_offset` is the sum of `x` rows on all processed with a lower
/// rank than the caller.
/// @param[in] dim Geometric dimension (1, 2, or 3).
/// @param[in] reorder_fn Function for re-ordering the degree-of-freedom
/// map associated with the geometry data.
/// @note Experimental new interface for multiple cmap/dofmap
/// @return A mesh geometry.
template <typename U>
Geometry<typename std::remove_reference_t<typename U::value_type>>
create_geometry(const Topology& topology,
const std::vector<fem::CoordinateElement<
std::remove_reference_t<typename U::value_type>>>& elements,
std::span<const std::int64_t> nodes,
std::span<const std::int64_t> xdofs, const U& x, int dim,
const std::function<std::vector<int>(
const graph::AdjacencyList<std::int32_t>&)>& reorder_fn
= nullptr)
{
spdlog::info("Create Geometry (multiple)");
assert(std::ranges::is_sorted(nodes));
using T = typename std::remove_reference_t<typename U::value_type>;
// Check elements match cell types in topology
const int tdim = topology.dim();
const std::size_t num_cell_types = topology.entity_types(tdim).size();
if (elements.size() != num_cell_types)
throw std::runtime_error("Mismatch between topology and geometry.");
std::vector<fem::ElementDofLayout> dof_layouts;
dof_layouts.reserve(elements.size());
for (auto& el : elements)
dof_layouts.push_back(el.create_dof_layout());
spdlog::info("Got {} dof layouts", dof_layouts.size());
// Build 'geometry' dofmap on the topology
auto [_dof_index_map, bs, dofmaps]
= fem::build_dofmap_data(topology.index_maps(topology.dim())[0]->comm(),
topology, dof_layouts, reorder_fn);
auto dof_index_map
= std::make_shared<common::IndexMap>(std::move(_dof_index_map));
// If the mesh has higher order geometry, permute the dofmap
if (elements.front().needs_dof_permutations())
{
const std::int32_t num_cells
= topology.connectivity(topology.dim(), 0)->num_nodes();
const std::vector<std::uint32_t>& cell_info
= topology.get_cell_permutation_info();
int d = elements.front().dim();
for (std::int32_t cell = 0; cell < num_cells; ++cell)
{
std::span dofs(dofmaps.front().data() + cell * d, d);
elements.front().permute_inv(dofs, cell_info[cell]);
}
}
spdlog::info("Calling compute_local_to_global");
// Compute local-to-global map from local indices in dofmap to the
// corresponding global indices in cells, and pass to function to
// compute local (dof) to local (position in coords) map from (i)
// local-to-global for dofs and (ii) local-to-global for entries in
// coords
spdlog::info("xdofs.size = {}", xdofs.size());
std::vector<std::int32_t> all_dofmaps;
std::stringstream s;
for (auto q : dofmaps)
{
s << q.size() << " ";
all_dofmaps.insert(all_dofmaps.end(), q.begin(), q.end());
}
spdlog::info("dofmap sizes = {}", s.str());
spdlog::info("all_dofmaps.size = {}", all_dofmaps.size());
spdlog::info("nodes.size = {}", nodes.size());
const std::vector<std::int32_t> l2l = graph::build::compute_local_to_local(
graph::build::compute_local_to_global(xdofs, all_dofmaps), nodes);
// Allocate space for input global indices and copy data
std::vector<std::int64_t> igi(nodes.size());
std::ranges::transform(l2l, igi.begin(),
[&nodes](auto index) { return nodes[index]; });
// Build coordinate dof array, copying coordinates to correct position
assert(x.size() % dim == 0);
const std::size_t shape0 = x.size() / dim;
const std::size_t shape1 = dim;
std::vector<T> xg(3 * shape0, 0);
for (std::size_t i = 0; i < shape0; ++i)
{
std::copy_n(std::next(x.begin(), shape1 * l2l[i]), shape1,
std::next(xg.begin(), 3 * i));
}
spdlog::info("Creating geometry with {} dofmaps", dof_layouts.size());
return Geometry(dof_index_map, std::move(dofmaps), elements, std::move(xg),
dim, std::move(igi));
}
} // namespace dolfinx::mesh
|