OpenPFC  0.1.4
Phase Field Crystal simulation framework
Loading...
Searching...
No Matches
strong_types.hpp File Reference

Strong type aliases for geometric quantities. More...

#include <array>
#include <openpfc/core/types.hpp>
#include <type_traits>
Include dependency graph for strong_types.hpp:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  pfc::GridSize
 Grid dimensions (number of grid points per dimension) More...
 
struct  pfc::LocalOffset
 Local subdomain offset in local coordinate system. More...
 
struct  pfc::GlobalOffset
 Global subdomain offset in global coordinate system. More...
 
struct  pfc::IndexBounds
 Index space bounds (min and max indices) More...
 
struct  pfc::GridSpacing
 Physical spacing between grid points. More...
 
struct  pfc::PhysicalOrigin
 Physical origin of coordinate system. More...
 
struct  pfc::PhysicalCoords
 Physical coordinates in space. More...
 
struct  pfc::PhysicalBounds
 Physical space bounds (min and max coordinates) More...
 

Detailed Description

Strong type aliases for geometric quantities.

This header provides lightweight strong type wrappers for geometric quantities used throughout OpenPFC. These types improve code clarity and type safety by distinguishing between different kinds of 3D arrays (size vs spacing vs offset).

Design Philosophy

OpenPFC uses strong types to make code self-documenting and type-safe:

Before (primitive obsession):

Int3 size = {64, 64, 64};
Int3 offset = {0, 0, 0};
Real3 spacing = {1.0, 1.0, 1.0};
auto world = create(size, offset, spacing); // Which is which?
auto bad = create(offset, size, spacing); // ❌ Compiles but wrong!
auto create(const World< T > &world, const heffte::box3d< int > &box)
Construct a new World object from an existing one and a box.
Definition decomposition.hpp:62

After (strong types):

GridSize size({64, 64, 64});
LocalOffset offset({0, 0, 0});
GridSpacing spacing({1.0, 1.0, 1.0});
auto world = create(size, offset, spacing); // ✅ Clear intent
auto bad = create(offset, size, spacing); // ❌ Won't compile!

Zero-Cost Abstraction

All strong types are zero-cost - they compile away completely:

static_assert(sizeof(GridSize) == sizeof(Int3));
static_assert(std::is_trivially_copyable_v<GridSize>);

Assembly output is identical to using raw Int3 or Real3 types.

Backward Compatibility

Strong types use implicit conversions for seamless backward compatibility:

// Old code still works
Int3 size = {64, 64, 64};
auto world = create(size, ...); // ✅ Works
// New code uses strong types
GridSize size({64, 64, 64});
auto world = create(size, ...); // ✅ Also works
// Can mix and match
auto world2 = create(GridSize({32, 32, 32}), {0, 0, 0}, ...); // ✅ Works

Available Types

Discrete (index) space:

  • GridSize - Grid dimensions (number of points per dimension)
  • LocalOffset - Subdomain offset in local coordinate system
  • GlobalOffset - Subdomain offset in global coordinate system
  • IndexBounds - Min/max indices for a region

Physical (coordinate) space:

  • GridSpacing - Physical spacing between grid points
  • PhysicalOrigin - Physical origin of coordinate system
  • PhysicalCoords - Physical position in space
  • PhysicalBounds - Physical min/max coordinates for a region

Usage Examples

Basic Construction

// From raw arrays
Int3 raw_size = {64, 64, 64};
GridSize size(raw_size);
// Direct brace initialization
GridSize size2({128, 128, 128});
// Implicit conversion back to raw type
Int3 extracted = size;

Function Parameters

// Self-documenting function signatures
void setup(GridSize size, GridSpacing spacing, PhysicalOrigin origin);
// Compiler catches argument order mistakes
setup(size, spacing, origin); // ✅ Correct
// setup(spacing, size, origin); // ❌ Won't compile!

Bounds Types

// Index space bounds
IndexBounds idx_bounds({0, 0, 0}, {63, 63, 63});
Int3 lower = idx_bounds.lower;
Int3 upper = idx_bounds.upper;
// Physical space bounds
PhysicalBounds phys_bounds({-10.0, -10.0, -10.0}, {10.0, 10.0, 10.0});
Real3 lower_phys = phys_bounds.lower;
Real3 upper_phys = phys_bounds.upper;

When to Use

Use strong types for:

  • Function parameters (improves clarity)
  • Public APIs (self-documenting)
  • Struct members (semantic meaning)

Raw types are fine for:

  • Local variables in implementation
  • Tight loops (no conversion overhead anyway)
  • Internal helper functions

Performance Notes

Strong types have zero runtime overhead:

  • No heap allocation
  • No virtual functions
  • Same size as underlying types
  • Trivially copyable
  • Standard layout
  • Optimizes away completely
See also
core/types.hpp for raw type definitions (Int3, Real3)
core/world.hpp for usage in World construction
Author
OpenPFC Development Team
Date
2025-11-24