General concepts » Meshes » Advanced Utilities

Advanced mesh operations and transformations.

Rodin provides advanced utilities for mesh manipulation, transformation, and analysis.

Introduction

Beyond basic mesh creation and queries, Rodin offers powerful utilities for mesh transformation, domain decomposition, and advanced operations.

Mesh Transformations

Scaling

Scale all mesh coordinates by a factor:

#include <Rodin/Geometry.h>

using namespace Rodin;
using namespace Rodin::Geometry;

int main() {
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16});

  // Scale mesh by factor of 2.0
  mesh.scale(2.0);

  // Domain is now [0, 2] x [0, 2]
  std::cout << "Volume after scaling: " << mesh.getVolume() << std::endl;

  return 0;
}

Displacement

Displace mesh nodes using a vector-valued function:

#include <Rodin/Geometry.h>
#include <Rodin/Variational.h>

using namespace Rodin;
using namespace Rodin::Geometry;
using namespace Rodin::Variational;

int main() {
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16});

  P1 Vh(mesh);

  // Define displacement field
  VectorFunction displacement(
    [](const Point& p) {
      Math::Vector<Real> u(2);
      u(0) = 0.1 * std::sin(2 * M_PI * p.x());
      u(1) = 0.1 * std::sin(2 * M_PI * p.y());
      return u;
    }, mesh.getSpaceDimension());

  // Displace mesh
  mesh.displace(displacement);

  return 0;
}

SubMeshes

Create submeshes representing subregions:

Creating SubMeshes

#include <Rodin/Geometry.h>

using namespace Rodin;
using namespace Rodin::Geometry;

int main() {
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16});

  // Create submesh from attribute 1
  SubMesh submesh = mesh.trace(1);

  std::cout << "Parent mesh cells: " << mesh.getCellCount() << std::endl;
  std::cout << "SubMesh cells: " << submesh.getCellCount() << std::endl;

  // Access parent mesh
  const Mesh& parent = submesh.getParent();

  return 0;
}

Boundary SubMesh

mesh.getConnectivity().compute(1, 2);

// Create submesh of boundary
SubMesh boundary = mesh.skin();

std::cout << "Boundary elements: " << boundary.getCellCount() << std::endl;

Connected Component Labeling

Find connected components in the mesh:

mesh.getConnectivity().compute(2, 2);

// Find connected components
auto ccl = mesh.ccl(
  [](const Polytope& p1, const Polytope& p2) {
    // Define adjacency criterion
    return true;  // All adjacent cells are connected
  });

std::cout << "Found " << ccl.getCount() 
          << " connected components" << std::endl;

for (const auto& component : ccl) {
  std::cout << "Component with " << component.size() 
            << " cells" << std::endl;
}

Domain Decomposition

Partition meshes for parallel computing:

Balanced Partitioning

#include <Rodin/Geometry.h>

using namespace Rodin;
using namespace Rodin::Geometry;

int main() {
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {64, 64});

  // Partition into 4 subdomains
  size_t numParts = 4;
  BalancedCompactPartitioner partitioner;
  auto shards = partitioner.partition(mesh, numParts);

  for (size_t i = 0; i < shards.size(); ++i) {
    std::cout << "Shard " << i << " has " 
              << shards[i].getCellCount() << " cells" << std::endl;
  }

  return 0;
}

Greedy Partitioning

GreedyPartitioner partitioner;
auto shards = partitioner.partition(mesh, numParts);

Coordinate Operations

Setting Coordinates

// Set coordinates of vertex 0
mesh.setVertexCoordinates(0, 1.5, 0);  // x-coordinate
mesh.setVertexCoordinates(0, 2.5, 1);  // y-coordinate

// Or set all at once
Math::SpatialPoint coords(2);
coords << 1.5, 2.5;
mesh.setVertexCoordinates(0, coords);

Cache Management

Clear cached data:

// Flush cached transformations and derived data
mesh.flush();

This is useful after modifying mesh coordinates to ensure transformations are recomputed.

Complete Example

#include <Rodin/Geometry.h>

using namespace Rodin;
using namespace Rodin::Geometry;

int main() {
  // Create mesh
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {32, 32});

  std::cout << "Original mesh:" << std::endl;
  std::cout << "  Volume: " << mesh.getVolume() << std::endl;

  // Scale mesh
  mesh.scale(2.0);
  std::cout << "\nAfter scaling by 2.0:" << std::endl;
  std::cout << "  Volume: " << mesh.getVolume() << std::endl;

  // Create boundary submesh
  mesh.getConnectivity().compute(1, 2);
  SubMesh boundary = mesh.skin();
  std::cout << "\nBoundary:" << std::endl;
  std::cout << "  Elements: " << boundary.getCellCount() << std::endl;
  std::cout << "  Perimeter: " << boundary.getMeasure(1) << std::endl;

  // Find connected components
  mesh.getConnectivity().compute(2, 2);
  auto ccl = mesh.ccl(
    [](const Polytope& p1, const Polytope& p2) { return true; });
  std::cout << "\nConnected components: " << ccl.getCount() << std::endl;

  // Partition mesh
  BalancedCompactPartitioner partitioner;
  auto shards = partitioner.partition(mesh, 4);
  std::cout << "\nPartitioned into " << shards.size() 
            << " shards" << std::endl;
  for (size_t i = 0; i < shards.size(); ++i) {
    std::cout << "  Shard " << i << ": " 
              << shards[i].getCellCount() << " cells" << std::endl;
  }

  return 0;
}

Advanced Topics

Custom Builder

Use the builder for complete control:

auto builder = Mesh::Build();
builder.initialize(2)
       .reserve(2, 100)  // Reserve space for 100 cells
       .nodes(50);       // 50 vertices

// Add vertices and cells...

Mesh mesh = builder.finalize();

Transformations

Access and modify polytope transformations:

const auto& trans = mesh.getPolytopeTransformation(2, 0);  // Cell 0
// Use transformation for geometric computations

See Also