General concepts » First Steps with Rodin

Understanding the basics and creating your first Rodin program.

Now that you have Rodin installed, let's explore the basic concepts and create your first program.

Project Structure

A typical Rodin project consists of:

  • CMakeLists.txt - Build configuration
  • Source files (.cpp) - Your C++ code using Rodin
  • Mesh files - Geometry data (optional, can generate programmatically)

Basic CMakeLists.txt

Here's a minimal CMakeLists.txt for a Rodin project:

cmake_minimum_required(VERSION 3.16)
project(MyRodinProject CXX)

# Set C++20 standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find Rodin package
find_package(Rodin REQUIRED)

# Create executable
add_executable(my_program main.cpp)

# Link against Rodin libraries
target_link_libraries(my_program PRIVATE
  Rodin::Geometry
  Rodin::Variational
  Rodin::Solver
)

Rodin Modules

Rodin is organized into several modules, each providing specific functionality:

ModulePurposeCommon Classes
Rodin::GeometryMesh operations and geometryMesh, Polytope
Rodin::VariationalFinite element spaces and formsP1, TrialFunction, TestFunction, Problem
Rodin::SolverLinear system solversCG, SparseLU
Rodin::IOInput/output operationsFile reading/writing
Rodin::AssemblyMatrix assemblyAssemblers

Hello Rodin

Let's create a simple program that creates a mesh and prints information about it.

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

using namespace Rodin;
using namespace Rodin::Geometry;

int main() {
  // Create a 2D uniform triangular mesh
  Mesh mesh;
  mesh = mesh.UniformGrid(Polytope::Type::Triangle, {8, 8});

  // Print mesh information
  std::cout << "Created a mesh with:" << std::endl;
  std::cout << "  Vertices: " << mesh.getVertexCount() << std::endl;
  std::cout << "  Cells: " << mesh.getCellCount() << std::endl;
  std::cout << "  Dimension: " << mesh.getDimension() << std::endl;

  return 0;
}

Save this as main.cpp and build it:

cmake .
make
./my_program

You should see output like:

Created a mesh with:
  Vertices: 81
  Cells: 128
  Dimension: 2

Core Concepts

Working with Meshes

A Mesh represents the discretization of your domain. You can create meshes in several ways:

Generate a uniform grid:**

Mesh mesh;
// 2D triangular mesh with 16x16 grid
mesh = mesh.UniformGrid(Polytope::Type::Triangle, {16, 16});

// 3D tetrahedral mesh with 8x8x8 grid
mesh = mesh.UniformGrid(Polytope::Type::Tetrahedron, {8, 8, 8});

Load from file:**

Mesh mesh;
mesh.load("domain.mesh");  // MEDIT format

Computing Connectivity

Before performing certain operations, you may need to compute connectivity information:

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

// Compute face-to-cell connectivity (needed for boundary operations)
mesh.getConnectivity().compute(1, 2);

The compute(d1, d2) method computes connectivity from polytopes of dimension d1 to polytopes of dimension d2. For a 2D mesh:

  • 0 = vertices
  • 1 = edges (faces)
  • 2 = triangles (cells)

Finite Element Spaces

Finite element spaces define the type of basis functions used for approximation:

#include <Rodin/Variational.h>

using namespace Rodin::Variational;

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

// P1 (linear) finite element space
P1 Vh(mesh);

std::cout << "FE space has " << Vh.getSize() << " degrees of freedom" << std::endl;

Trial and Test Functions

To formulate problems, we use trial and test functions:

P1 Vh(mesh);

// The unknown function we're solving for
TrialFunction u(Vh);

// The test function for weak formulation
TestFunction v(Vh);

These are used to express variational formulations in a natural way.

Using Namespaces

To avoid typing Rodin::Geometry:: and Rodin::Variational:: repeatedly, most Rodin programs start with:

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

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

Complete Example

Here's a complete program that demonstrates the basic workflow:

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

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

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

  // 2. Compute connectivity (needed for boundary operations)
  mesh.getConnectivity().compute(1, 2);

  // 3. Create finite element space
  P1 Vh(mesh);

  // 4. Create trial and test functions
  TrialFunction u(Vh);
  TestFunction v(Vh);

  // 5. Print information
  std::cout << "Setup complete!" << std::endl;
  std::cout << "  Mesh cells: " << mesh.getCellCount() << std::endl;
  std::cout << "  DOFs: " << Vh.getSize() << std::endl;

  return 0;
}

What's Next?

You now understand the basic building blocks of a Rodin program. In the next section, Your First Problem: Solving the Poisson Equation, we'll put these concepts together to solve a complete PDE problem: the Poisson equation.