Skip to content

Conversation

@vtushar06
Copy link
Contributor

@vtushar06 vtushar06 commented Jun 26, 2025

Description

What is this PR

  • Bug fix
  • Addition of a new feature
  • Other

Why is this PR needed?
This PR introduces a new utility function, compute_kinetic_energy, for decomposing kinetic energy into translational and rotational components for each individual. This is based on the enhancement request raised in Issue #228 by @niksirbi. It improves the analytical capabilities of the movement library and sets the foundation for further physics-based behavioral analysis.


What does this PR do?

• Adds the function compute_kinetic_energy() to movement/kinematics/kinetic_energy.py
• Supports optional keypoint selection and custom mass dictionaries
• Handles both "xy" and "space" dimension naming conventions in the velocity input
• Returns an xarray with dimensions (time, individuals, energy) and coordinates ["translational", "rotational"]
• Adds a test case test_kinetic_energy_basic() to validate functionality
• Ensures full compatibility with pre-commit hooks and project linting standards (ruff, mypy)


References

Fixes: #228

How has this PR been tested?

✅ Added a dedicated unit test in tests/test_unit/test_kinematics/test_kinetic_energy.py
✅ Used simulated velocity input to verify correctness of translational and rotational KE outputs
✅ Ran all tests with pytest, all passed
✅ Passed all pre-commit checks including docstring rules, formatting, type checks, and linting


Is this a breaking change?

  • No
  • Yes (please explain):
    N/A

If this PR breaks any existing functionality, please explain how and why.

  • No

Does this PR require an update to the documentation?

  • No
  • Yes (please explain):
    Function includes a detailed docstring explaining usage, input format, and output structure.

Checklist:

  • The code has been tested locally
  • Tests have been added to cover all new functionality
  • The documentation has been updated to reflect any changes
  • The code has been formatted with pre-commit

@codecov
Copy link

codecov bot commented Jun 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7e83580) to head (b3a4f32).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #623   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           32        33    +1     
  Lines         1860      1889   +29     
=========================================
+ Hits          1860      1889   +29     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@niksirbi niksirbi self-requested a review June 26, 2025 08:52
Copy link
Member

@niksirbi niksirbi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vtushar06!

I've left some high-level comments for you to consider. After you've dealt with these we can do another round of review to deal with the details.

This will take some more work and time, but it's an exciting feature I'm looking forward to seeing released.

@vtushar06 vtushar06 force-pushed the feature/kinetic-energy-decomposition branch from 8861543 to 69e2da2 Compare July 4, 2025 01:03
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jul 4, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
9.5% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@vtushar06
Copy link
Contributor Author

Hi @niksirbi,
I’ve incorporated all the suggested changes, including:
• Accepting position as input and computing velocity internally
• Using validate_dims_coords for dimension validation
• Adding comprehensive examples to the docstring
• Writing robust tests using valid_poses_dataset and a custom spinning_dataset fixture
• Verifying both translational and rotational components across motion types
• Covering weighted kinetic energy cases
• Handling edge cases like insufficient keypoints

Please have a look at the latest changes when you get a chance. Happy to refine further if needed. Thanks!

@vtushar06
Copy link
Contributor Author

Hii @niksirbi,
can you please review out the changes i made in accordance to your comments.
thanks
@vtushar06

@niksirbi
Copy link
Member

Thanks for the reminder @vtushar06 and for keeping the momentum going.
I've been quite busy with other responsibilities, but I will try to do another round of review for this PR this week.

PR #634 has to wait till the 2nd half of August I'm afraid. The whole of the core maintainer team will be pre-occupied with teaching till then.

@niksirbi niksirbi mentioned this pull request Jul 30, 2025
7 tasks
@vtushar06
Copy link
Contributor Author

Thanks @niksirbi! Appreciate the update. I’ll be ready for any further feedback whenever you get a chance.

Copy link
Member

@niksirbi niksirbi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience @vtushar06.

I finally had a chance to go through it properly and also discussed it with @sfmig. Our conversations led us to a few overall points, which I’ll summarise here. These differ somewhat from what was outlined in the original issue #228 — but that’s often the case. Having a concrete implementation to look at helped us think more clearly about the problem, so thank you for providing that.

In addition to these main points, I’ve also left some inline comments on specific parts of the code, with concrete suggestions for implementation.

  1. Correction to translational kinetic energy. There’s an error in how translational kinetic energy is computed. At the moment you use K_trans = 0.5 * compute_norm(v_cm) ** 2 but this expression also needs to be multiplied by the individual’s total mass (weights.sum). See below for the correct math. The error stems from a mistake in the formula I had originally supplied in issue #228 — apologies for that. I realised the error while running tests: I was getting a non-zero "rotational" kinetic energy on valid_poses_dataset (without any modifications), which shouldn’t be happening. With this correction, the tests can also be simplified (see my inline comments).
  2. Renaming the "rotational" component. Sofía and I agreed that “rotational” isn’t quite the right term here. Since the animals aren’t rigid bodies, inter-keypoint distances aren’t fixed, and motions such as scaling and shearing also contribute to the non-translational kinetic energy. I suggest renaming this component from rotational to internal, meaning any motion relative to the individual’s centre of mass.
  3. Default behaviour of compute_kinetic_energy. Since many users may not need the decomposition, it might make sense for compute_kinetic_energy to return the total kinetic energy by default. We could add a boolean argument decompose=False so that, if set to True, the function returns both translational and internal components.

Finally, because the maths here tripped me up at first (and could do the same for users), I recommend adding the explicit formulas in a "Notes" section of the docstring. I’ve drafted a version you can adapt, which I’ve included below. Note that I use T to denote kinetic energy in the formulas, as is common in Physics.

Click to show source code
    Notes
    -----
    Considering a given individual at time point :math:`t` as a system of
    keypoint particles, its total kinetic energy :math:`T_{total}` is given by:

    .. math::  T_{total} = \sum_{i} \frac{1}{2} m_i \| \mathbf{v}_i(t) \|^2

    where :math:`m_i` is the mass of the :math:`i`-th keypoint and
    :math:`\mathbf{v}_i(t)` is its velocity at time :math:`t`.

    From Samuel König's second theorem, we can decompose :math:`T_{total}`
    into:

    - Translational kinetic energy: the kinetic energy of the individual's
      total mass :math:`M` moving with the centre of mass velocity;
    - Internal kinetic energy: the kinetic energy of the keypoints moving
      relative to the individual's centre of mass.

    We compute translational kinetic energy :math:`T_{trans}` as follows:

    .. math:: T_{trans} = \frac{1}{2} M \| \mathbf{v}_{cm}(t) \|^2

    where :math:`M = \sum_{i} m_i` is the total mass of the individual
    and :math:`\mathbf{v}_{cm}(t) = \frac{1}{M} \sum_{i} m_i \mathbf{v}_i(t)`
    is the velocity of the centre of mass at time :math:`t`
    (computed as the weighted mean of keypoint velocities).

    Internal kinetic energy :math:`T_{int}` is derived as the difference
    between the total and translational components:

    .. math:: T_{int} = T_{total} - T_{trans}
image

@vtushar06
Copy link
Contributor Author

Hi @niksirbi, I have updated my pr as per yours guidance and requirement, can you please have a look on this.
Thanks.

Copy link
Member

@niksirbi niksirbi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vtushar06!

Only some minor changes remain to be made, so I'l already approve this PR.

Basically you need to edit the movement/kinematics/__init__.py file and make the suggested changes in the docstring examples (see my inline comments).

Other than these, it's good to go.

@niksirbi niksirbi force-pushed the feature/kinetic-energy-decomposition branch from d4fd6c7 to b3a4f32 Compare August 5, 2025 13:19
@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 5, 2025

@niksirbi
Copy link
Member

niksirbi commented Aug 5, 2025

It all looks good now 🎉

I've rebased to the main branch and will merge this as soon as the CI checks are done. This feature will be part of our next release.

@niksirbi niksirbi enabled auto-merge August 5, 2025 13:20
@niksirbi niksirbi added this pull request to the merge queue Aug 5, 2025
Merged via the queue into neuroinformatics-unit:main with commit 04c5948 Aug 5, 2025
18 checks passed
@vtushar06
Copy link
Contributor Author

That's fantastic news 🎉🎉! Thank you so much, @niksirbi.

I really appreciate you taking the time to guide me through this. Your detailed feedback and patience were incredibly helpful, and the feature is much better because of it. I learned a ton in the process.

I'm so excited to see this included in the next release! Looking forward to finding another issue to contribute to soon. 🎉

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kinetic Energy Decomposition

2 participants