tune(brain): uniform triangulation + bigger spacing for r_f=6mm fillets

- Switched to uniform triangulation (no density-adaptive refinement for
  initial pass — saves that for stress-informed iterations)
- s_min=45mm, s_max=55mm (was 12/35) — larger triangles fit 6mm fillets
- Boss keepout circles: 12 segments (was 32) — less boundary clutter
- Fillet must be >= 80% of r_f at every corner or pocket is skipped
- Result: 75 pockets, 481 NX entities, min fillet 4.85mm, mass 4066g
- adaptive_density=True param enables density refinement for future stress iterations
This commit is contained in:
2026-02-16 20:51:20 +00:00
parent 30981fa066
commit 239e2f01a9
3 changed files with 5797 additions and 1531 deletions

View File

@@ -37,8 +37,8 @@ DEFAULT_PARAMS = {
'p': 2.0,
'beta': 0.3,
'R_edge': 15.0,
's_min': 12.0,
's_max': 35.0,
's_min': 45.0,
's_max': 55.0,
't_min': 2.5,
't_0': 3.0,
'gamma': 1.0,

View File

@@ -84,7 +84,7 @@ def build_pslg(geometry, params):
# r_boss = r_hole + d_keep * hole_diameter / 2
hole_radius = diameter / 2.0
boss_radius = hole_radius + keepout_dist
keepout_boundary = sample_circle(hole['center'], boss_radius, num_points=32)
keepout_boundary = sample_circle(hole['center'], boss_radius, num_points=12)
else:
# Fallback for non-circular holes
keepout_boundary = offset_polygon(hole['boundary'], keepout_dist, inward=False)
@@ -169,36 +169,39 @@ def generate_triangulation(geometry, params, max_refinement_passes=3):
"""
pslg = build_pslg(geometry, params)
# Initial triangulation with global max area
s_max = params['s_max']
global_max_area = (np.sqrt(3) / 4.0) * s_max**2
# Triangle options: p=PSLG, q30=min angle 30°, a=area constraint, D=conforming Delaunay
result = tr.triangulate(pslg, f'pq30Da{global_max_area:.1f}')
# Iterative refinement based on density field
for iteration in range(max_refinement_passes):
verts = result['vertices']
tris = result['triangles']
# Use s_min as the uniform target spacing for initial/non-adaptive pass.
# Density-adaptive refinement only kicks in when stress results are
# available (future iterations). For now, uniform triangles everywhere.
s_target = params['s_min']
use_adaptive = params.get('adaptive_density', False)
areas = compute_triangle_areas(verts, tris)
centroids = compute_centroids(verts, tris)
# Target equilateral triangle area for the chosen spacing
target_area = (np.sqrt(3) / 4.0) * s_target**2
# Compute target area for each triangle based on density at centroid
target_areas = np.array([
(np.sqrt(3) / 4.0) * density_to_spacing(
evaluate_density(cx, cy, geometry, params), params
)**2
for cx, cy in centroids
])
# Triangle options: p=PSLG, q30=min angle 30°, a=area constraint, D=conforming
result = tr.triangulate(pslg, f'pq30Da{target_area:.1f}')
# Check if all triangles satisfy constraints (20% tolerance)
if np.all(areas <= target_areas * 1.2):
break
if use_adaptive:
# Iterative density-adaptive refinement (for stress-informed passes)
for iteration in range(max_refinement_passes):
verts = result['vertices']
tris = result['triangles']
# Set per-triangle max area and refine
result['triangle_max_area'] = target_areas
result = tr.triangulate(result, 'rpq30D')
areas = compute_triangle_areas(verts, tris)
centroids = compute_centroids(verts, tris)
target_areas = np.array([
(np.sqrt(3) / 4.0) * density_to_spacing(
evaluate_density(cx, cy, geometry, params), params
)**2
for cx, cy in centroids
])
if np.all(areas <= target_areas * 1.2):
break
result['triangle_max_area'] = target_areas
result = tr.triangulate(result, 'rpq30D')
min_triangle_area = params.get('min_triangle_area', 20.0)
result = filter_small_triangles(result, min_triangle_area)

File diff suppressed because it is too large Load Diff