Ordinate 2D Marketplace Community Open the studio

Marketplace › From Ordinate 2D

Energy audit of the open scene

Script by Ordinate 2D ✓ · Dynamics

Open any scene, then run it: kinetic and potential energy of every moving body, their sum, and how much was lost.

Use in Ordinate 2DOpens in the Script tab: it runs when you press Run. Free, no account needed.
"""Energy audit of the open scene: where does the energy go?

Open any scene first (an example, or your own), then run this script: it does not clear the scene. Every 0.05 s it adds up
the kinetic energy (½mv² + ½Iω²) and the gravitational potential energy (m g y) of every moving body and records them with
their sum. A sum that stays flat means nothing is lost; a falling sum is friction, inelastic hits and damping at work.
(Energy stored in springs is not counted: a scene with springs shows it as an exchange.)

Runs in the studio (Script tab → Run) and locally:  python3 energy_audit.py
"""
import studio as s

EVERY = 0.05     # s between rows
DURATION = 10    # s
next_row = 0.0
start = None


def totals():
    g = -s.gravity[1]
    ke = pe = 0.0
    for b in s.bodies():
        if b.static:
            continue
        ke += 0.5 * b.mass * (b.vx ** 2 + b.vy ** 2) + 0.5 * (b.inertia or 0.0) * b.omega ** 2
        pe += b.mass * g * b.y
    return ke, pe


moving = [b for b in s.bodies() if not b.static]
if moving:
    print(f"Auditing {len(moving)} moving bod{'y' if len(moving) == 1 else 'ies'} for {DURATION} s: a row every {EVERY} s in the Script sheet "
          f"(Table tab), the totals here every 2 s.")
else:
    print("This scene has no moving bodies. Open a scene first (an example, or build one), then run the audit on it.")
next_print = 0.0


@s.on_step
def audit(t, dt):
    global next_row, start, next_print
    if t + 1e-9 < next_row:
        return
    next_row += EVERY
    ke, pe = totals()
    if start is None:
        start = ke + pe
    s.record(t=t, KE=ke, PE=pe, E=ke + pe, lost=start - (ke + pe))
    if t + 1e-9 >= next_print:
        next_print += 2.0
        print(f"t = {t:4.1f} s: KE {ke:8.2f} J, PE {pe:8.2f} J, total {ke + pe:8.2f} J")


@s.on_end
def report():
    ke, pe = totals()
    if start is not None and moving:
        lost = start - (ke + pe)
        print(f"total energy: {start:.2f} J at the start, {ke + pe:.2f} J now: {lost:.2f} J lost "
              f"({100 * lost / start if start else 0:.1f} %) to friction, collisions and damping")


s.run(DURATION)

table energy any scene