7  Two-dimensional Geometry Setup

7.1 Overview

This chapter introduces the foundation of the approach: creating accurate physical representations of 2D domains. You’ll learn how to build and visualize 2D geometries using RayTraceHeatTransfer.jl, establishing the geometric framework that the analytical methods will operate on. The chapter progresses systematically through increasingly complex geometries:

  • Rectangle: The fundamental building block, essential for later validation against analytical solutions
  • L-shape: Introduces non-convex geometries and demonstrates geometric complexity
  • Star shape: Showcases the flexibility of the approach with intricate boundaries

Each geometry demonstrates how to construct physical domain representations that serve as the foundation for radiative transfer calculations in subsequent chapters.

7.2 What You’ll Learn

  • Building accurate physical representations of 2D domains
  • Visualizing geometries with working Julia code
  • Understanding how geometric complexity affects domain representation
  • Preparing geometric foundations for exchange factor calculations and heat transfer solutions

7.3 Concepts

The graph equilibrium approach builds 2D domains from fundamental radiative transfer elements: control volumes and walls (boundaries). Understanding how these elements are constructed and connected forms the foundation for all subsequent radiative transfer calculations.

Domain Elements

  • Control volumes: Represent participating media regions with size given by 4βV (m²), where β is the extinction coefficient and V is the volume
  • Walls/boundaries: Represent surfaces with area A (m²) that can emit, absorb, and reflect radiation
  • Each element receives a unique incremental number for exchange factor indexing

Radiative Connectivity

Visibility between elements determines radiative exchange and will be calculated through ray tracing in later chapters. Since this method uses Monte Carlo ray tracing rather than angular discretization, it achieves accurate visibility knowledge without directional approximations, enabling analytical solutions that closely represent the true physical domain.

Geometric Construction

Domains are built from convex blocks - either triangles or quadrilaterals - that can be efficiently ray traced. The meshing and ray tracing algorithms handle both geometric primitives seamlessly. Blocks are combined by specifying which walls are ‘solid’ (opaque boundaries) versus ‘penetrable’ (allowing radiation passage between blocks). This modular approach enables complex geometries while maintaining computational efficiency.

Property Assignment

Material properties (known temperature/flux, emissivity, albedo) are assigned during geometry construction using nested inheritance - each mesh element inherits properties from its parent ‘superpiece’. This simplifies property specification compared to defining properties for individual sub-elements while maintaining flexibility through multiple superpieces with different properties.

Visualization and Access

The visualization system displays element numbers, allowing easy identification of specific walls and volumes for result analysis and validation.

7.4 Examples

Now we’ll build three increasingly complex 2D geometries using RayTraceHeatTransfer.jl, starting with the fundamental rectangle that will serve as our validation benchmark.

7.4.1 Rectangle Domain

The rectangle is the most basic geometry and provides an essential validation case since analytical solutions exist for rectangular enclosures. Let’s construct a simple rectangular domain:

using RayTraceHeatTransfer
using StaticArrays
using GeometryBasics

faces_1 = RayTraceHeatTransfer.PolyFace2D{Float64}[] # create a vector to hold parts

# build the first part
vertices1 = SVector(
    Point2(0.0, 0.0), # 2D vertices
    Point2(1.0, 0.0),
    Point2(1.0, 1.0),
    Point2(0.0, 1.0),
)
solidWall1 = SVector(true, true, true, true) # all walls are impenetrable
n_spectral_bins = 1 # number of spectral (wavelength) bins
kappa = 1.0 # local absorption coefficient (vector for spectral bins > 1)
sigma_s = 0.0 # local scattering coefficient (vector for spectral bins > 1)
face1 = RayTraceHeatTransfer.PolyFace2D{Float64}(vertices1, solidWall1, n_spectral_bins, kappa, sigma_s)

# set the physical properties and state
face1.T_in_w = [1000.0, 0.0, 0.0, 0.0] # known wall temperatures
face1.q_in_w = [0.0, 0.0, 0.0, 0.0] # wall fluxes not used when temperature is specified
face1.T_in_g = -1.0 # unknown gas temperature
face1.q_in_g = 0.0 # known gas flux (radiative equilibrium)
face1.epsilon = [1.0, 1.0, 1.0, 1.0] # wall emissivities

push!(faces_1, face1) # push the first part into the vector for meshing

Next, the domain is meshed into a desired resolution:

Ndim = 11 # splits in (x,y) directions
mesh1 = RayTraceHeatTransfer.RayTracingMeshOptim(faces_1, [(Ndim,Ndim)]);

Finally, the domain is displayed using Makie:

using CairoMakie
CairoMakie.activate!()
Makie.inline!(true)
fig = Figure(; size = (800, 600))
ax = Axis(fig[1, 1], aspect=AxisAspect(1))
RayTraceHeatTransfer.plotMesh2D(ax, mesh1) # plot the mesh
# save("rectangle_mesh.png", fig) # Save the figure
fig

2D quadrilateral mesh

7.4.2 L-shaped Domain

The L-shaped domain is the first non-convex geometry and provides a more complex enclosure, when compared to the rectangle. Let’s construct a simple L-shaped domain. We’ll build it from 3 rectangles:

using RayTraceHeatTransfer
using StaticArrays
using GeometryBasics

faces_2 = RayTraceHeatTransfer.PolyFace2D{Float64}[] # create a vector to hold parts

Rectangle 1

# build the first part
vertices1 = SVector(
    Point2(0.0, 0.0), # 2D vertices
    Point2(1.0, 0.0),
    Point2(1.0, 1.0),
    Point2(0.0, 1.0),
)
solidWall1 = SVector(true, true, false, true) # top wall is open
face1 = RayTraceHeatTransfer.PolyFace2D{Float64}(vertices1, solidWall1, n_spectral_bins, kappa, sigma_s)

# set the physical properties and state
face1.T_in_w = [1000.0, 0.0, 0.0, 0.0] # known wall temperatures
face1.q_in_w = [0.0, 0.0, 0.0, 0.0] # wall fluxes not used when temperature is specified
face1.T_in_g = -1.0 # unknown gas temperature
face1.q_in_g = 0.0 # known gas flux (radiative equilibrium)
face1.epsilon = [1.0, 1.0, 1.0, 1.0] # wall emissivities

push!(faces_2, face1) # push the first part into the vector for meshing

Rectangle 2

# build the first part
vertices2 = SVector(
    Point2(0.0, 1.0), # 2D vertices
    Point2(1.0, 1.0),
    Point2(1.0, 2.0),
    Point2(0.0, 2.0),
)
solidWall2 = SVector(false, false, true, true) # two walls are open
face2 = RayTraceHeatTransfer.PolyFace2D{Float64}(vertices2, solidWall2, n_spectral_bins, kappa, sigma_s)

# set the physical properties and state
face2.T_in_w = [0.0, 0.0, 0.0, 0.0] # known wall temperatures
face2.q_in_w = [0.0, 0.0, 0.0, 0.0] # wall fluxes not used when temperature is specified
face2.T_in_g = -1.0 # unknown gas temperature
face2.q_in_g = 0.0 # known gas flux (radiative equilibrium)
face2.epsilon = [1.0, 1.0, 1.0, 1.0] # wall emissivities

push!(faces_2, face2) # push the second part into the vector for meshing

Rectangle 3

# build the first part
vertices3 = SVector(
    Point2(1.0, 1.0), # 2D vertices
    Point2(2.0, 1.0),
    Point2(2.0, 2.0),
    Point2(1.0, 2.0),
)
solidWall3 = SVector(true, true, true, false) # two walls are open
face3 = RayTraceHeatTransfer.PolyFace2D{Float64}(vertices3, solidWall3, n_spectral_bins, kappa, sigma_s)

# set the physical properties and state
face3.T_in_w = [0.0, 0.0, 0.0, 0.0] # known wall temperatures
face3.q_in_w = [0.0, 0.0, 0.0, 0.0] # wall fluxes not used when temperature is specified
face3.T_in_g = -1.0 # unknown gas temperature
face3.q_in_g = 0.0 # known gas flux (radiative equilibrium)
face3.epsilon = [1.0, 1.0, 1.0, 1.0] # wall emissivities

push!(faces_2, face3) # push the second part into the vector for meshing

Next, the domain is meshed into a desired resolution:

Ndim = 11 # splits in 3 (x,y) directions
mesh2 = RayTraceHeatTransfer.RayTracingMeshOptim(faces_2, [(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim)]);

Finally, the domain is displayed using Makie:

fig = Figure(; size = (800, 600))
ax = Axis(fig[1, 1], aspect=AxisAspect(1))
RayTraceHeatTransfer.plotMesh2D(ax, mesh2) # plot the mesh
# save("L-shaped_mesh.png", fig) # Save the figure
fig

2D L-shaped mesh

7.4.3 Star-shaped Domain

The star-shaped domain is the first non-convex, complex enclosure we’ll mesh programmatically. We’ll build it from 10 triangles. First, let’s calculate the points of the star:

using RayTraceHeatTransfer
using GeometryBasics
using StaticArrays

# Inner pentagon radius and outer star point radius
inner_radius = 0.3
outer_radius = 1.0

# Generate 5 points for pentagon (72° between points)
inner_points = Vector{Point2{Float64}}()
for i in 1:5
    angle = 2π/5 * (i-1) - π/2  # Start from top (-π/2)
    x = inner_radius * cos(angle)
    y = inner_radius * sin(angle)
    push!(inner_points, Point2(x, y))
end

# Generate 5 outer star points
outer_points = Vector{Point2{Float64}}()
for i in 1:5
    angle = 2π/5 * (i-1) - π/2 + π/5  # Offset by half pentagon angle
    x = outer_radius * cos(angle)
    y = outer_radius * sin(angle)
    push!(outer_points, Point2(x, y))
end

Next, let’s create the faces:

# Create faces
faces_1 = PolyFace2D{Float64}[]

# Central pentagon (can be divided into triangles)
for i in 1:5
    vertices = SVector(
        Point2(0.0, 0.0),  # Center
        inner_points[i],
        inner_points[mod1(i+1, 5)]
    )
    solidWalls = SVector(false, false, false)
    face = PolyFace2D{Float64}(vertices, solidWalls, n_spectral_bins, kappa, sigma_s)
    face.T_in_w = [0.0, 0.0, 0.0]
    face.q_in_w = [0.0, 0.0, 0.0]
    face.T_in_g = -1.0
    face.q_in_g = 1000.0
    face.epsilon = [1.0, 1.0, 1.0]

    push!(faces_1, face)
end

# Star points (triangles)
for i in 1:5
    vertices = SVector(
        inner_points[i],
        outer_points[i],
        inner_points[mod1(i+1, 5)]
    )
    solidWalls = SVector(true, true, false)
    face = PolyFace2D{Float64}(vertices, solidWalls, n_spectral_bins, kappa, sigma_s)
    face.T_in_w = [0.0, 0.0, 0.0]
    face.q_in_w = [0.0, 0.0, 0.0]
    face.T_in_g = -1.0
    face.q_in_g = 1000.0
    face.epsilon = [1.0, 1.0, 1.0]

    push!(faces_1, face)
end

Finally, let’s mesh and plot the geometry:

Ndim = 11
mesh1 = RayTracingMeshOptim(faces_1, [(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim),
                                (Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim),(Ndim,Ndim)]);
using CairoMakie
CairoMakie.activate!()
Makie.inline!(true)
fig = Figure(; size = (800, 600))
ax = Axis(fig[1, 1], aspect=AxisAspect(1))
RayTraceHeatTransfer.plotMesh2D(ax, mesh1) # plot the mesh
# save("Star-shaped_mesh.png", fig) # Save the figure
fig

2D star-shaped mesh