Skip to content

🚀[FEA]: Let Mesh.transform apply RankSpec parity to pseudotensor fields under reflections #2136

Open

Description

@peterdsharpe

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], correct

scale 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 parity mask argument beside transform_point_data. This is smaller, but it duplicates what FieldSchema already records.
  • Leave transform as it is and document the limitation. PR Overhaul the PhysicsNeMo-Mesh documentation and tutorials #2122 does this.

Activity

  1. bennibyx commented on Nov 3, 2026

    @bennibyx

    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 FieldSchema separately 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions