Repository navigation
Description
Is this a new feature, an improvement, or a change to existing functionality?
Improvement
How would you describe the priority of this feature request
Low (would be nice)
Please provide a clear description of problem you would like to solve.
physicsnemo.mesh.fields.RankSpec records a parity for each field. "odd" marks a pseudotensor, which gains a factor of det M under an improper map. Examples are vorticity, angular velocity, and the curl of any vector field.
Mesh.transform, rotate and scale never read a schema. _transform_tensordict in physicsnemo/mesh/transformations/geometric.py:50-130 picks the law from the trailing shape of each tensor. A scalar is left alone, a vector gets v @ M.T, and a rank-2 tensor gets M T M^T. So every field follows the true-tensor law.
The docs in PR #2122 say so. Its new docs/api/mesh/fields.rst says these functions "select fields by shape and do not read a schema." The User Guide applies the det M factor to vorticity by hand. So this is a missing feature, not a bug.
Under a reflection (det M = -1) the result is wrong for odd fields. Here a 2D pseudoscalar keeps its sign under the mirror y -> -y:
import torch
from physicsnemo.mesh.fields import FieldSchema
from physicsnemo.mesh.primitives.basic import single_triangle_2d
mesh = single_triangle_2d.load()
mesh.point_data["omega"] = torch.ones(mesh.n_points) # 2D vorticity, a pseudoscalar
mesh.point_data["velocity"] = torch.tensor([[1.0, 1.0]]).repeat(mesh.n_points, 1)
schema = FieldSchema.parse({"omega": {"rank": 0, "parity": "odd"}, "velocity": {"rank": 1}})
schema.check(mesh.point_data, label="point data")
M = torch.diag(torch.tensor([1.0, -1.0])) # mirror y -> -y, det(M) = -1
mirrored = mesh.transform(M, transform_point_data=True)
print(mirrored.point_data["omega"].tolist()) # [1.0, 1.0, 1.0], the law needs [-1.0, -1.0, -1.0]
print(mirrored.point_data["velocity"][0].tolist()) # [1.0, -1.0], correctscale with a negative factor in 3D is also improper. scale(-1.0) maps a pseudovector [0, 0, 1] to [0, 0, -1], but the pseudovector law gives [0, 0, 1].
rotate is not affected. Its matrix comes from rotation_matrix, which is always proper with det R = +1. So the parity factor is 1, and the true-tensor law is already correct.
A possible design: transform, scale and the MeshTransform datapipe classes accept a FieldSchema for each of point_data, cell_data and global_data. Fields declared parity="odd" then get the extra det M factor. The schema could also select fields in place of the boolean mask. symmetric could stay a declaration only, because every linear map keeps a symmetric tensor symmetric.
Describe any alternatives you have considered
- Apply the factor by hand: leave the odd field out of the mask, then set
torch.linalg.det(M) * field @ M.T. The User Guide shows this. It is easy to forget, and it is spread across call sites. - Add a
paritymask argument besidetransform_point_data. This is smaller, but it duplicates whatFieldSchemaalready records. - Leave
transformas it is and document the limitation. PR Overhaul the PhysicsNeMo-Mesh documentation and tutorials #2122 does this.
Activity
bennibyx commentedon Nov 3, 2026
Hi @peterdsharpe,
I'd be interested in working on this improvement.
I looked through the issue and understand the need to apply the determinant-based parity correction to fields declared with
parity="odd"under improper transformations.Before I start implementing, I'd like to confirm the preferred API direction:
Would you recommend passing
FieldSchemaseparately for point, cell and global data, or is there a smaller interface you'd prefer for the initial implementation?I'm happy to work on the implementation and regression tests once the intended API is aligned.
Thanks!