function rdb_ocean_get_kinetic_energy(c_handle, ke_out) result(status) &
bind(c, name="rdb_ocean_get_kinetic_energy")
!! Total kinetic energy (J, up to the Boussinesq reference-density
!! factor — matches `rdb_ocean_get_total_mass`'s convention of
!! leaving `rho0` out) over the physical interior:
!! `sum(0.5 * h_layer * (u_centre^2 + v_centre^2) * areaT)`, faces
!! averaged to centres — same formula as
!! `rdb_ocean_budgets::budget_total_ke`, computed inline (weighted by
!! the metrics' `areaT`, so it is right on spherical / supergrid /
!! tripolar grids too; `grid%dx*grid%dy` is only the fallback). Unlike
!! the raw-pointer getters above, this refreshes the host itself —
!! it hands back a NUMBER, not a pointer a caller could otherwise
!! defer syncing for.
type(c_ptr), intent(in), value :: c_handle
real(c_double), intent(out) :: ke_out
integer(c_int) :: status
type(ocean_handle_t), pointer :: h
real(wp) :: total, uc, vc
integer :: i, j, k, ng, nxp, nyp
logical :: use_area
ke_out = 0.0_c_double
status = resolve_ocean(c_handle, h)
if (status /= OCEAN_STATUS_OK) return
call ocean_handle_refresh_host(h)
use_area = handle_has_area(h)
ng = h%grid%nghost
nxp = h%grid%nx_phys
nyp = h%grid%ny_phys
total = 0.0_wp
associate (ms => h%state%multilayer)
do k = 1, ms%nz_ml
do j = 1, nyp
do i = 1, nxp
uc = 0.5_wp*(ms%u_face_x_layer(ng + i, ng + j, k) + &
ms%u_face_x_layer(ng + i + 1, ng + j, k))
vc = 0.5_wp*(ms%v_face_y_layer(ng + i, ng + j, k) + &
ms%v_face_y_layer(ng + i, ng + j + 1, k))
if (use_area) then
total = total + ms%h_layer(ng + i, ng + j, k)*0.5_wp*(uc*uc + vc*vc)* &
h%state%metrics%areaT(ng + i, ng + j)
else
total = total + ms%h_layer(ng + i, ng + j, k)*0.5_wp*(uc*uc + vc*vc)
end if
end do
end do
end do
end associate
if (use_area) then
ke_out = real(total, c_double)
else
ke_out = real(total*h%grid%dx*h%grid%dy, c_double)
end if
status = int(OCEAN_STATUS_OK, c_int)
end function rdb_ocean_get_kinetic_energy