dragonfly_sim.core.meshwarping

Attributes

_NO_PITCH

Classes

IDWarp_jax

IDW mesh deformation via the idwarp-jax package's kd-tree warper.

Module Contents

class dragonfly_sim.core.meshwarping.IDWarp_jax(mesh_obj, wall_meshtag_val: int = 2, anchor_meshtag_vals=(3, 4), extrusion_width: float = None, LdefFact: float = 1.0, err_tol: float = 1e-06, **idwarp_kwargs)

Bases: csdl_alpha.CustomExplicitOperation

IDW mesh deformation via the idwarp-jax package’s kd-tree warper.

Maps the replicated (M_global, dim) wall-node motions (evaluate(bdry_motions, cp_motion_inputs), usually shape_parameterization.WallFFD.wall_displacement) to rank-local volume node motions, in geometry-node order. Because the map is closed form rather than a PDE solve, this is a CustomExplicitOperation – CSDL calls compute and compute_jacvec_product.

Everything that is a property of the mesh rather than of the warp lives on the Mesh (mesh_manager): the wall and anchor node sets and their global numbering (Mesh.boundary_node_set), the tagged facets and their orientation, applying/resetting node motions, mesh quality, and the output files. The wall numbering here is the mesh’s wall node set’s.

The warp itself is idwarp_jax.build_volume_pts_func, a kd-tree IDWarp that batches its forward and reverse passes internally, so its memory stays bounded without any chunking here. err_tol is its pruning tolerance, relative to the IDW denominator; err_tol=0 disables pruning and gives the exact dense IDW sum. This class only turns the mesh’s wall and anchor node sets into the package’s surface and moves arrays between CSDL and it.

Parameters:
mesh_objmesh_manager.Mesh, with its boundary facets

tagged. Its output_suffix (Mesh.configure_output) names this class’s memory logs.

wall_meshtag_valtag of the moving (design) surface.
anchor_meshtag_valstags of the boundaries held at zero displacement.

Pass () to disable anchoring – but read the note above first.

extrusion_width2D only; width of the extruded boundary ribbon. None

derives it from the smallest boundary edge. See _resolve_extrusion_width for why the default is what it is – the choice trades IDW distance fidelity against the conditioning of Newell’s method, and neither limit is safe.

LdefFactIDWarp’s dimensionless deformation length:

Ldef = LdefFact * Ldef0, with Ldef0 the largest distance from the wall nodes’ centroid to a wall node. The package takes Ldef0 over every prescribed node, anchors included – the farfield radius – so the factor is rescaled before it is passed on.

err_tolkd-tree pruning tolerance, see above. Measured against

err_tol=0 on the 2D NACA0012 mesh: 1e-6 gives 1.2e-3 relative motion error at half the cost; 1e-10 prunes almost nothing (cost of the exact sum). On the 3D wing mesh 1e-6 keeps ~25% of the dense interactions, with a ~47 GiB host route cache summed over all ranks.

**idwarp_kwargsforwarded to build_volume_pts_func, over these

defaults:

  • normal_eps = warp_eps = 1e-30. The package defaults (1e-6) are dimensional: normal_eps is added to |2A|^2 of every face, which is ~1e-14 for a 2D ribbon quad and ~1e-10 for a small 3D wall face, and warp_eps floors every IDW distance at 1e-3. Either would wreck the warp.

  • symmetry_tolerance: 0 in 2D, where no ribbon node may be pinned to y = 0; 1e-6 in 3D, the tolerance the model tags symmetry facets with.

  • target_batch_interactions = 1_000_000 (package: 6e6). Each rank holds one batch of volume-point x source interactions in flight, and the reverse pass’s working arrays scale with it: on the 3D wing mesh at err_tol=1e-6 they add ~0.9 GiB/rank at 1e6 vs ~2.4 GiB/rank at 6e6, with identical results and no measurable slowdown.

  • progress = print_timings = False; this class reports its own timings.

Everything else (zeroCornerRotations, cornerAngle, useRotations, rotation_eps, bucket_size, route_cache, …) is the package’s default.

_assemble_motions(wall_motions, interior_3d)

(N_local, dim) mesh-node motions in geometry-node order.

Owned wall nodes take their rows of the replicated wall array, interior nodes the warp output; anchor nodes stay zero.

_assert_planar(motion_3d)

In 2D the out-of-plane (y) motion must vanish identically.

It does so exactly, per surface node: both nodal normals are in-plane, so the rotation is about y, so the y component of M (x_v - Xs0_i) + Xs_i - x_v is (+-w/2) + (-+w/2) = 0. A nonzero value here means the ribbon construction is wrong, not that a tolerance is tight.

_build_global_surface_topology()

Assemble the replicated prescribed surface as the package’s mesh dict.

self._mesh_dict holds points (the surface rows, self._Xs0) and faces (every face with type 1, so wall and anchor faces are all prescribed). Every rank ends up with byte-identical arrays, because every rank has to evaluate the IDW sum against the whole surface.

_build_warper()

Build this rank’s idwarp_jax warper over its own interior nodes.

The volume points are the interior nodes only: wall motions are prescribed and anchors are held at zero, so neither needs warping. A rank that owns no interior node gets one dummy point, because the package cannot preprocess an empty volume.

_check_surface_topology(n_flipped)

Fail loudly if the ranks assembled different surfaces.

Identical on every rank, byte for byte: otherwise ranks evaluate different surfaces and the gradients they reduce are incoherent. A content hash rather than a sum, so a permutation cannot slip through.

_expand(wall_vals)

(M_global, dim) wall values -> (n_surface_rows, 3), in every layer.

The anchor rows stay zero.

_fold(rows)

Adjoint of _expand: (n_surface_rows, 3) -> (M_global, dim).

The layers are summed because the forward writes the same wall motion into every layer. The anchor rows are dropped: they are sensitivities with respect to a constant.

_log_memory(note)
_project(wall_vals)

Zero the y component of wall nodes on the symmetry plane.

Takes and returns a full (M_global, dim) array. The package pins those nodes to y = 0 before warping, so this is the wall motion the warp actually assumed. A diagonal projection, hence its own adjoint. Identity in 2D, where no node is pinned.

_resolve_extrusion_width(node_coords, local_faces)

Pick the 2D ribbon width: a fraction of the shortest boundary edge.

Two effects pull in opposite directions, and the factor below is where they balance.

Smaller w is better for the IDW geometry: w enters the distance as sqrt(d_xy^2 + w^2/4), so a width comparable to the near-wall spacing would blunt exactly the weight concentration that makes the warp follow the surface. At w = 0.1 * h_min the nearest volume node sees its distance inflated by 0.125%, which is nothing.

Larger w is better for conditioning. Newell’s method in idwarp_jax’s normal.py sums cross products of absolute coordinates, whose magnitudes are set by the farfield, and those terms have to cancel down to twice the face area – which for a ribbon quad is only edge_length * w. The relative error in the resulting normal therefore goes as 1/w. Measured on the 2D mesh, as the error in reproducing a rigid surface translation:

w = 1e-5 -> 6.5e-08 w = 1e-2 -> 6.7e-11 w = 1e-4 -> 6.7e-09 w = 1e-1 -> 6.7e-12 w = 1e-3 -> 6.7e-10 w = 1e+0 -> 6.6e-13

0.1 * h_min puts that error around 1e-9 for an O(1) displacement, four orders below the smallest cell, at a distance distortion of 0.1%. (3D is unaffected: real surface faces have areas many orders larger than an extruded ribbon’s, so nothing cancels catastrophically.)

The minimum is taken over every prescribed boundary edge, so in practice it is set by the wall, where the edges are finest.

compute(input_vals, output_vals)

Forward warp. No collectives: every rank already has the whole surface.

compute_jacvec_product(input_vals, output_vals, d_inputs, d_outputs, mode)

Matrix-free VJP. Only reverse mode is supported.

Each rank seeds only the rows it owns – its own wall nodes for the identity block, its own interior nodes for the IDW block – so the per-rank gradients are disjoint contributions to one (M_global, dim) array and summing them is exact.

evaluate(bdry_motions: csdl_alpha.Variable, cp_motion_inputs: csdl_alpha.Variable)

Register the operation.

cp_motion_inputs carries no derivative – it is declared only to keep every rank’s reverse chain attached to the design variables, including ranks that own no wall nodes.

LdefFact
M_global
N_local
_cols = [0, 2]
_extrusion_width_arg = None
_idwarp_kwargs
_interior_local_idx
_n_interior
_n_surface_nodes
anchor_meshtag_vals = (3, 4)
anchors
dimensions
err_tol
mesh
ordered_callbacks = True
wall
wall_meshtag_val = 2
dragonfly_sim.core.meshwarping._NO_PITCH