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:
| Module | Purpose | Common Classes |
|---|---|---|
| Rodin:: | Mesh operations and geometry | Mesh, Polytope |
| Rodin:: | Finite element spaces and forms | P1, TrialFunction, TestFunction, Problem |
| Rodin:: | Linear system solvers | CG, SparseLU |
| Rodin:: | Input/output operations | File reading/writing |
| Rodin:: | Matrix assembly | Assemblers |
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_programYou 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= vertices1= 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:: and Rodin:: 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.