Skip to content

Geometries merged using CSG do not handle periodic points properly. #2071

Description

@echen5503

When applying periodic boundary conditions to Constructive Solid Geometry (CSG) objects (CSGUnion, CSGDifference, CSGIntersection), the mapped points frequently fall entirely outside the computational domain or land in the middle of it.

This occurs because the CSG classes do not calculate their periodic mappings based on the new, combined shape's bounding box. Instead, they strictly delegate the mapping to the parent geometries (e.g., calling self.geom1.periodic_point(x)). If the CSG operation alters the bounding box along the periodic axis, the parent geometry uses its original, now-incorrect bbox to calculate the shift.

Expected Behavior:
Periodic boundaries should wrap exactly from one side of the new CSG domain to the other.

  • For Union: Gluing a $1 \times 1$ square and a $1 \times 1$ square into a $2 \times 1$ rectangle should map $x=0$ to $x=2$.
  • For Difference: Subtracting half of a $2 \times 1$ rectangle to leave a $1 \times 1$ square should map $x=0$ to $x=1$.

Actual Behavior:
Because it delegates to the parents:

  • For Union: It asks the left parent to map $x=0$. The parent thinks its right wall is at $x=1$, so it dumps the point in the middle of the new fluid domain.
  • For Difference: It asks the parent to map $x=0$. The parent thinks its right wall is at $x=2$, so it throws the point into empty space outside the new domain.

Steps to Reproduce:
The script below demonstrates both failures visually and numerically on the current main branch.

import deepxde as dde
import numpy as np

def demonstrate_csg_periodic_flaws():
    print("=== DeepXDE CSG periodic_point Flaw ===")

    # ---------------------------------------------------------
    # TEST 1: CSGDifference (Maps outside the domain)
    # ---------------------------------------------------------
    print("\n--- 1. Testing CSGDifference ---")
    geom1 = dde.geometry.Rectangle([0, 0], [2, 1])
    geom2 = dde.geometry.Rectangle([1, 0], [2, 1])
    
    # Leaves a new square domain from x=0 to x=1
    diff_geom = dde.geometry.CSGDifference(geom1, geom2)
    test_point_diff = np.array([[0.0, 0.5]])
    
    mapped_diff = diff_geom.periodic_point(test_point_diff, component=0)
    
    print(f"New Domain:      x in [0, 1]")
    print(f"Original point:  {test_point_diff.tolist()}")
    print(f"Expected mapped: [[1.0, 0.5]]")
    print(f"ACTUAL mapped:   {mapped_diff.tolist()}")
    if mapped_diff[0, 0] == 2.0:
        print("-> BUG: Mapped to parent's bbox (x=2), landing OUTSIDE the domain.")

    # ---------------------------------------------------------
    # TEST 2: CSGUnion (Maps into the middle of the domain)
    # ---------------------------------------------------------
    print("\n--- 2. Testing CSGUnion ---")
    geom3 = dde.geometry.Rectangle([0, 0], [1, 1])
    geom4 = dde.geometry.Rectangle([1, 0], [2, 1])
    
    # Creates a wide rectangle domain from x=0 to x=2
    union_geom = dde.geometry.CSGUnion(geom3, geom4)
    test_point_union = np.array([[0.0, 0.5]])
    
    mapped_union = union_geom.periodic_point(test_point_union, component=0)
    
    print(f"New Domain:      x in [0, 2]")
    print(f"Original point:  {test_point_union.tolist()}")
    print(f"Expected mapped: [[2.0, 0.5]]")
    print(f"ACTUAL mapped:   {mapped_union.tolist()}")
    if mapped_union[0, 0] == 1.0:
        print("-> BUG: Mapped to parent's bbox (x=1), landing in the MIDDLE of the domain.")

if __name__ == "__main__":
    demonstrate_csg_periodic_flaws()

Suggested Resolution:
The periodic_point method in CSG geometries likely needs to be decoupled from self.geom1 and self.geom2. Instead, the CSG classes should compute the boundary shift using their own combined self.bbox, similar to how base geometries like Rectangle handle it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions