You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Disclaimer: This architectural blueprint was co-authored using Gemini in collaboration with a core Firedrake developer.
Architecture Plan: Native Region Name Support in Firedrake
This development plan introduces native, human-readable region naming capabilities across Firedrake. By allowing users to seamlessly use string identifiers preserved from external mesh generators or explicitly assign custom semantic names to subdomains at runtime, it eliminates the cognitive overhead of tracking opaque numerical tags when defining boundary conditions, extracting submeshes, or specifying UFL variational forms.
Motivation: The Target User Experience
Firedrake users can use names natively within their mesh generators, which are automatically preserved and parsed when defining boundary conditions, submesh extractions, or integration domains in modern UFL measures.
fromfiredrakeimport*importnetgen.occasngocc# 1. Generate geometry & name boundaries natively in Netgenwp=ngocc.WorkPlane()
box=wp.Rectangle(1, 1).Face()
box.edges.Max(ngocc.Y).name="inlet"box.faces.name="fluid_domain"ngmesh=ngocc.OCCGeometry(box, dim=2).GenerateMesh(maxh=0.1)
# 2. Native Firedrake Mesh captures the names automaticallymesh=Mesh(ngmesh)
# 3. Explicitly map a single string name to multiple disjoint entity IDs# 4. Clean, human-readable API usageV=FunctionSpace(mesh, "CG", 1)
# DirichletBC automatically maps "inlet" to the correct face IDbc=DirichletBC(V, 0.0, "inlet")
# SubMesh automatically maps "fluid_domain" using the correct keyword argumentsubmesh=SubMesh(mesh, subdomain_id="fluid_domain")
# Form language integration: UFL Measures accept string region names directlydx=Measure("dx", domain=mesh, subdomain_id="fluid_domain")
f=Function(V)
L=f*dx
Phase 1: Implementing the Naming & Registration Engine
Mesh.rename_subdomain serves as the sole gateway for mutating the region_names store. It handles both scalar IDs and iterables, treating the underlying values as flat list structures.
Expected Storage Structure
# Each integer dimension key maps to a dictionary of string names pointing to integer lists
{
3: {"fluid_domain": [1, 2]},
2: {"inlet": [10], "internal_obstacles": [12, 14, 15, 42]},
1: {},
0: {}
}
1. Naming and Registration API
# Inside firedrake/mesh.py -> Class MeshGeometry / Meshdefrename_subdomain(self, dim, subdomain_id, subdomain_name):
""" Bind a custom string name to an integer subdomain ID (or multiple IDs) for a given dimension. """ifnothasattr(self.topology, "region_names"):
self.topology.region_names= {}
mesh_dim=self.topological_dimensionifnot (0<=dim<=mesh_dim):
raiseValueError(f"Invalid entity dimension {dim} for mesh of dimension {mesh_dim}")
ifdimnotinself.topology.region_names:
self.topology.region_names[dim] = {}
ifsubdomain_namenotinself.topology.region_names[dim]:
self.topology.region_names[dim][subdomain_name] = []
# Dynamically extend or append depending on whether an iterable container is providedifisinstance(subdomain_id, (list, tuple, set)):
self.topology.region_names[dim][subdomain_name].extend(subdomain_id)
else:
self.topology.region_names[dim][subdomain_name].append(subdomain_id)
2. Streamlined File Parsers
# Inside firedrake/mesh.pydefextract_netgen_region_names(ngmesh, mesh_dim):
"""Query a live Netgen mesh object using its native region name API."""extracted= []
get_names_fn=getattr(ngmesh, "GetRegionNames", getattr(ngmesh, "getRegionNames", None))
ifnotget_names_fn:
returnextracted# Sweep sequentially through all available dimensionsfortarget_diminrange(mesh_dim+1):
names=get_names_fn(dim=target_dim)
fori, nameinenumerate(names, 1):
ifname:
extracted.append((target_dim, i, name))
returnextracteddefparse_gmsh_physical_names(filename):
"""Parses the $PhysicalNames block from a Gmsh .msh file."""extracted= []
try:
withopen(filename, 'r') asf:
in_physical_names=Falseforlineinf:
line=line.strip()
ifline=="$PhysicalNames":
in_physical_names=Truecontinueelifline=="$EndPhysicalNames":
breakifin_physical_names:
parts=line.split()
iflen(parts) >=3:
dim=int(parts[0])
tag=int(parts[1])
name=parts[2].strip('"')
extracted.append((dim, tag, name))
exceptIOError:
passreturnextracted
Because names point directly to integer arrays, parse_subdomain_id automatically flattens lookups when evaluating collections containing mixed strings and integers. It includes an explicit pass-through guard for standard fallback strings like "everywhere".
# Inside firedrake/mesh.py -> Class MeshGeometry / Meshdefparse_subdomain_id(self, dim, subdomain_id):
""" Translate a string region name (or collection of names) into the corresponding integer subdomain ID(s) for a given entity dimension. Always flattens named lists. """meta=getattr(self.topology, "region_names", {})
mesh_dim=self.topological_dimensionifnot (0<=dim<=mesh_dim):
raiseValueError(f"Invalid entity dimension {dim} for mesh of dimension {mesh_dim}")
dim_dict=meta.get(dim, {})
defresolve_single(sd):
ifisinstance(sd, str):
ifsdin ("on_boundary", "everywhere"):
return [sd]
ifsdindim_dict:
returndim_dict[sd]
else:
available=list(dim_dict.keys())
raiseValueError(
f"Subdomain region '{sd}' not found in dimension {dim}. "f"Available names: {available}"
)
return [sd]
# Process iterables and flatten underlying integer lists seamlesslyifisinstance(subdomain_id, (list, tuple, set)):
resolved= []
forsdinsubdomain_id:
resolved.extend(resolve_single(sd))
returntype(subdomain_id)(resolved)
else:
ifisinstance(subdomain_id, str):
ifsubdomain_idin ("on_boundary", "everywhere"):
returnsubdomain_idreturnresolve_single(subdomain_id)
returnsubdomain_id
Phase 3: High-Level API Integration (DirichletBC & SubMesh)
Both high-level APIs route their internal checks completely through the new centralized parser method on the mesh object using integer definitions.
To support string region names passed into ufl.Measure, we hook into the form compilation pipeline right as an individual integral block enters TSFC. We determine the entity dimension from the UFL integral type and swap out string subdomains for their actual integer IDs.
# Inside firedrake/tsfc/driver.py -> compile_integraldefcompile_integral(integral, coordinate_mapping, ...):
"""Compiles a single UFL integral using TSFC."""# 1. Extract the underlying Firedrake mesh object and current metadatamesh=integral.ufl_domain()
subdomain_id=integral.subdomain_id()
integral_type=integral.integral_type()
# 2. Derive the correct topological dimension based on the integral typeifintegral_type=="cell":
dim=mesh.topological_dimensionelifintegral_typein ("exterior_facet", "interior_facet"):
dim=mesh.topological_dimension-1elifintegral_type=="vertex":
dim=0else:
dim=mesh.topological_dimension# 3. Resolve potential string names into their corresponding numerical IDsnew_subdomain_id=mesh.parse_subdomain_id(dim, subdomain_id)
# 4. Reconstruct the immutable UFL integral if structural updates occurredifnew_subdomain_id!=subdomain_id:
integral=integral.reconstruct(subdomain_id=new_subdomain_id)
# ... [Proceed with standard TSFC translation and code generation] ...
Phase 5: Verification and Edge Cases
Sequential Appends: Ensure that executing rename_subdomain multiple times for the same name and dimension correctly appends to the internal value list instead of replacing it.
String Pass-Through Integrity: Verify that hardcoded strings like "on_boundary" and "everywhere" evaluate properly both as standalone arguments and when combined within collections.
Compiler Isolation: Ensure that updating the subdomain_id on the UFL expression level leaves the remainder of the active variational form definition mathematically unchanged.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Architecture Plan: Native Region Name Support in Firedrake
This development plan introduces native, human-readable region naming capabilities across Firedrake. By allowing users to seamlessly use string identifiers preserved from external mesh generators or explicitly assign custom semantic names to subdomains at runtime, it eliminates the cognitive overhead of tracking opaque numerical tags when defining boundary conditions, extracting submeshes, or specifying UFL variational forms.
Motivation: The Target User Experience
Firedrake users can use names natively within their mesh generators, which are automatically preserved and parsed when defining boundary conditions, submesh extractions, or integration domains in modern UFL measures.
Phase 1: Implementing the Naming & Registration Engine
Mesh.rename_subdomainserves as the sole gateway for mutating theregion_namesstore. It handles both scalar IDs and iterables, treating the underlying values as flat list structures.Expected Storage Structure
1. Naming and Registration API
2. Streamlined File Parsers
3. Mesh Constructor Integration
Phase 2: Implementing
Mesh.parse_subdomain_idBecause names point directly to integer arrays,
parse_subdomain_idautomatically flattens lookups when evaluating collections containing mixed strings and integers. It includes an explicit pass-through guard for standard fallback strings like"everywhere".Phase 3: High-Level API Integration (
DirichletBC&SubMesh)Both high-level APIs route their internal checks completely through the new centralized parser method on the mesh object using integer definitions.
Phase 4: Form Compiler Integration (
TSFC Driver)To support string region names passed into
ufl.Measure, we hook into the form compilation pipeline right as an individual integral block enters TSFC. We determine the entity dimension from the UFL integral type and swap out string subdomains for their actual integer IDs.Phase 5: Verification and Edge Cases
rename_subdomainmultiple times for the same name and dimension correctly appends to the internal value list instead of replacing it."on_boundary"and"everywhere"evaluate properly both as standalone arguments and when combined within collections.subdomain_idon the UFL expression level leaves the remainder of the active variational form definition mathematically unchanged.Beta Was this translation helpful? Give feedback.
All reactions