Download src/bob_phdae.f90 from Snapkitty/sov-kernel-monster: direct link, hf CLI and curl.
- Browser
- Download file 16.3 kB
-
https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/src/bob_phdae.f90
- Command line
-
hf download hf://Snapkitty/sov-kernel-monster/src/bob_phdae.f90
-
curl -L -o bob_phdae.f90 https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/src/bob_phdae.f90
16.3 kB
| !===================================================================== | |
| ! bob_phdae.f90 | |
| ! Port-Hamiltonian Differential-Algebraic Equation (PH-DAE) kernel. | |
| ! Matches sovereign-phdae/src/lib.rs exactly. | |
| ! | |
| ! Mathematical model: | |
| ! d/dt(T(t,z) * z) = [J(t,z) - R(t,z)] * Q(t,z) * z + B(t) * u | |
| ! | |
| ! Where: | |
| ! T β mass tensor operator (possibly singular for DAEs) | |
| ! J β interconnection matrix (skew-symmetric: J = -J^T) | |
| ! R β dissipation matrix (positive semi-definite: R = R^T >= 0) | |
| ! Q β gradient operator | |
| ! B β input map | |
| ! u β external input | |
| ! | |
| ! Structure preservation: | |
| ! Skew-symmetry of J enforced at construction | |
| ! PSD-ness of R enforced via Cholesky witness | |
| ! Power balance: dH/dt = P_port - P_diss verified at each step | |
| ! WORM audit chain seals every time step | |
| ! | |
| ! Standard: Fortran 2018 | |
| !===================================================================== | |
| module bob_phdae | |
| use, intrinsic :: iso_c_binding, only: c_int32_t, c_int64_t, c_double, & | |
| c_ptr, c_f_pointer, c_loc, c_associated | |
| use, intrinsic :: iso_fortran_env, only: int64, real64 | |
| use bob_kinds | |
| use bob_errors | |
| use bob_worm, only: bob_worm_chain, blake3_hash_bytes | |
| implicit none | |
| private | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! Skew-symmetric matrix J = -J^T | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| type, public :: skew_sym_matrix | |
| real(wp), allocatable :: data(:,:) | |
| integer(i4) :: n = 0 | |
| logical(lk) :: is_valid = .false. | |
| contains | |
| procedure :: init => ssm_init | |
| procedure :: set => ssm_set ! enforces J_{ij} = -J_{ji} | |
| procedure :: apply => ssm_apply ! y = J * x | |
| procedure :: verify => ssm_verify ! check J = -J^T | |
| procedure :: destroy => ssm_destroy | |
| end type skew_sym_matrix | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! Positive semi-definite matrix R = R^T >= 0 | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| type, public :: psd_matrix | |
| real(wp), allocatable :: data(:,:) | |
| real(wp), allocatable :: chol(:,:) ! Cholesky factor L: R = L*L^T | |
| integer(i4) :: n = 0 | |
| logical(lk) :: is_valid = .false. | |
| logical(lk) :: has_chol = .false. | |
| contains | |
| procedure :: init => psd_init | |
| procedure :: set_chol => psd_set_chol ! set via L: R = L*L^T | |
| procedure :: apply => psd_apply ! y = R * x | |
| procedure :: verify => psd_verify ! check R = R^T | |
| procedure :: destroy => psd_destroy | |
| end type psd_matrix | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! PH-DAE system state | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| type, public :: bob_phdae_t | |
| integer(i4) :: n = 0 ! state dimension | |
| real(wp) :: time = ZERO ! current time | |
| real(wp) :: hamiltonian = ZERO ! current H(z) | |
| real(wp) :: power_port = ZERO ! B^T * Q * z * u | |
| real(wp) :: power_diss = ZERO ! z^T * Q^T * R * Q * z | |
| real(wp), allocatable :: state(:) ! z(t) | |
| real(wp), allocatable :: state_dot(:) ! dz/dt | |
| real(wp), allocatable :: gradient(:) ! Q * z (gradient) | |
| real(wp), allocatable :: input(:) ! u(t) | |
| real(wp), allocatable :: Q(:,:) ! gradient operator | |
| real(wp), allocatable :: B(:,:) ! input map | |
| type(skew_sym_matrix) :: J ! interconnection | |
| type(psd_matrix) :: R ! dissipation | |
| type(bob_worm_chain) :: audit ! WORM audit chain | |
| logical(lk) :: initialized = .false. | |
| contains | |
| procedure :: init => phdae_init | |
| procedure :: step => phdae_step ! Radau IIA step | |
| procedure :: hamiltonian_val => phdae_H ! H(z) = Β½ z^T Q^T Q z | |
| procedure :: power_balance => phdae_power | |
| procedure :: destroy => phdae_destroy | |
| end type bob_phdae_t | |
| ! Step receipt (sealed output from each integration step) | |
| type, public :: bob_step_receipt | |
| real(wp) :: time_in = ZERO | |
| real(wp) :: time_out = ZERO | |
| real(wp) :: h_in = ZERO ! Hamiltonian before | |
| real(wp) :: h_out = ZERO ! Hamiltonian after | |
| real(wp) :: dh = ZERO ! change in H | |
| real(wp) :: p_port = ZERO ! power in from port | |
| real(wp) :: p_diss = ZERO ! power dissipated | |
| real(wp) :: balance_err = ZERO ! |dH/dt - P_port + P_diss| | |
| logical(lk) :: balance_ok = .false. | |
| integer(i8) :: hash(32) = 0_i8 ! BLAKE3 seal | |
| end type bob_step_receipt | |
| public :: phdae_new | |
| public :: bob_phdae_new, bob_phdae_step, bob_phdae_free | |
| contains | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! SKEW-SYMMETRIC MATRIX | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| subroutine ssm_init(this, n) | |
| class(skew_sym_matrix), intent(inout) :: this | |
| integer(i4), intent(in) :: n | |
| if (allocated(this%data)) deallocate(this%data) | |
| allocate(this%data(n,n), source=ZERO) | |
| this%n = n; this%is_valid = .true. | |
| end subroutine ssm_init | |
| !> Set element (i,j) and enforce J_{ji} = -J_{ij} | |
| subroutine ssm_set(this, i, j, val) | |
| class(skew_sym_matrix), intent(inout) :: this | |
| integer(i4), intent(in) :: i, j | |
| real(wp), intent(in) :: val | |
| if (i == j) return ! diagonal must be zero for skew-sym | |
| this%data(i,j) = val | |
| this%data(j,i) = -val | |
| end subroutine ssm_set | |
| !> y = J * x | |
| pure subroutine ssm_apply(this, x, y) | |
| class(skew_sym_matrix), intent(in) :: this | |
| real(wp), intent(in) :: x(this%n) | |
| real(wp), intent(out) :: y(this%n) | |
| integer(i4) :: i, j | |
| y = ZERO | |
| do i = 1, this%n | |
| do j = 1, this%n | |
| y(i) = y(i) + this%data(i,j) * x(j) | |
| end do | |
| end do | |
| end subroutine ssm_apply | |
| !> Verify J = -J^T (frobenius norm of J + J^T < tol) | |
| function ssm_verify(this) result(ok) | |
| class(skew_sym_matrix), intent(in) :: this | |
| logical :: ok | |
| real(wp) :: err | |
| integer(i4) :: i, j | |
| err = ZERO | |
| do i = 1, this%n; do j = 1, this%n | |
| err = err + abs(this%data(i,j) + this%data(j,i))**2 | |
| end do; end do | |
| ok = sqrt(err) < TOL_NORM | |
| end function ssm_verify | |
| subroutine ssm_destroy(this) | |
| class(skew_sym_matrix), intent(inout) :: this | |
| if (allocated(this%data)) deallocate(this%data) | |
| this%n = 0; this%is_valid = .false. | |
| end subroutine ssm_destroy | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! PSD MATRIX | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| subroutine psd_init(this, n) | |
| class(psd_matrix), intent(inout) :: this | |
| integer(i4), intent(in) :: n | |
| if (allocated(this%data)) deallocate(this%data) | |
| if (allocated(this%chol)) deallocate(this%chol) | |
| allocate(this%data(n,n), source=ZERO) | |
| allocate(this%chol(n,n), source=ZERO) | |
| this%n = n; this%is_valid = .true.; this%has_chol = .false. | |
| end subroutine psd_init | |
| !> Set R = L * L^T where L is lower triangular Cholesky factor | |
| subroutine psd_set_chol(this, L) | |
| class(psd_matrix), intent(inout) :: this | |
| real(wp), intent(in) :: L(this%n, this%n) | |
| integer(i4) :: i, j, k | |
| this%chol = L | |
| this%has_chol = .true. | |
| ! Compute R = L * L^T | |
| this%data = ZERO | |
| do i = 1, this%n; do j = 1, this%n; do k = 1, this%n | |
| this%data(i,j) = this%data(i,j) + L(i,k) * L(j,k) | |
| end do; end do; end do | |
| end subroutine psd_set_chol | |
| pure subroutine psd_apply(this, x, y) | |
| class(psd_matrix), intent(in) :: this | |
| real(wp), intent(in) :: x(this%n) | |
| real(wp), intent(out) :: y(this%n) | |
| integer(i4) :: i, j | |
| y = ZERO | |
| do i = 1, this%n; do j = 1, this%n | |
| y(i) = y(i) + this%data(i,j) * x(j) | |
| end do; end do | |
| end subroutine psd_apply | |
| function psd_verify(this) result(ok) | |
| class(psd_matrix), intent(in) :: this | |
| logical :: ok | |
| real(wp) :: err, ev_min | |
| integer(i4) :: i, j | |
| ! Check symmetry | |
| err = ZERO | |
| do i = 1, this%n; do j = 1, this%n | |
| err = err + abs(this%data(i,j) - this%data(j,i))**2 | |
| end do; end do | |
| ok = sqrt(err) < TOL_NORM | |
| ! Check positive semi-definiteness via diagonal dominance (simplified) | |
| if (ok) then | |
| do i = 1, this%n | |
| if (this%data(i,i) < -TOL_NORM) then; ok = .false.; exit; end if | |
| end do | |
| end if | |
| end function psd_verify | |
| subroutine psd_destroy(this) | |
| class(psd_matrix), intent(inout) :: this | |
| if (allocated(this%data)) deallocate(this%data) | |
| if (allocated(this%chol)) deallocate(this%chol) | |
| this%n = 0; this%is_valid = .false. | |
| end subroutine psd_destroy | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! PH-DAE SYSTEM | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function phdae_new(n, num_inputs) result(sys) | |
| integer(i4), intent(in) :: n, num_inputs | |
| integer(i4) :: i | |
| type(bob_phdae_t) :: sys | |
| call sys%init(n, num_inputs) | |
| end function phdae_new | |
| subroutine phdae_init(this, n, num_inputs) | |
| class(bob_phdae_t), intent(inout) :: this | |
| integer(i4), intent(in) :: n, num_inputs | |
| integer(i4) :: i | |
| this%n = n; this%time = ZERO | |
| allocate(this%state(n), source=ZERO) | |
| allocate(this%state_dot(n), source=ZERO) | |
| allocate(this%gradient(n), source=ZERO) | |
| allocate(this%input(num_inputs), source=ZERO) | |
| allocate(this%Q(n,n), source=ZERO) | |
| allocate(this%B(n,num_inputs), source=ZERO) | |
| ! Default: Q = I (identity), B = 0 | |
| do i = 1, n; this%Q(i,i) = ONE; end do | |
| call this%J%init(n) | |
| call this%R%init(n) | |
| call this%audit%init() | |
| this%initialized = .true. | |
| end subroutine phdae_init | |
| !> H(z) = Β½ z^T Q^T Q z (quadratic Hamiltonian) | |
| function phdae_H(this) result(H) | |
| class(bob_phdae_t), intent(inout) :: this | |
| real(wp) :: H | |
| real(wp) :: Qz(this%n) | |
| integer(i4) :: i, j | |
| ! gradient = Q * z | |
| this%gradient = ZERO | |
| do i = 1, this%n; do j = 1, this%n | |
| this%gradient(i) = this%gradient(i) + this%Q(i,j) * this%state(j) | |
| end do; end do | |
| ! H = Β½ ||Q z||^2 | |
| H = HALF * dot_product(this%gradient, this%gradient) | |
| end function phdae_H | |
| !> Power balance: dH/dt = P_port - P_diss | |
| !> P_port = (B*u)^T * gradient (power from external port) | |
| !> P_diss = gradient^T * R * gradient (dissipated power, >= 0) | |
| subroutine phdae_power(this) | |
| class(bob_phdae_t), intent(inout) :: this | |
| real(wp) :: Rg(this%n), Bu(this%n) | |
| integer(i4) :: i, j | |
| ! R * gradient | |
| call this%R%apply(this%gradient, Rg) | |
| this%power_diss = dot_product(this%gradient, Rg) | |
| ! B * u | |
| Bu = ZERO | |
| do i = 1, this%n; do j = 1, size(this%input) | |
| Bu(i) = Bu(i) + this%B(i,j) * this%input(j) | |
| end do; end do | |
| this%power_port = dot_product(Bu, this%gradient) | |
| end subroutine phdae_power | |
| !> Single implicit midpoint step (simplified Radau IIA) | |
| !> dz/dt = (J - R) * gradient + B * u | |
| subroutine phdae_step(this, dt, receipt) | |
| class(bob_phdae_t), intent(inout) :: this | |
| real(wp), intent(in) :: dt | |
| type(bob_step_receipt), intent(out) :: receipt | |
| real(wp) :: Jg(this%n), Rg(this%n), Bu(this%n), rhs(this%n) | |
| real(wp) :: h_before, h_after, balance_err | |
| integer(i4) :: i, j | |
| integer(i8) :: seal_payload(8) | |
| receipt%time_in = this%time | |
| h_before = this%hamiltonian_val() | |
| receipt%h_in = h_before | |
| ! gradient = Q * z | |
| this%gradient = ZERO | |
| do i = 1, this%n; do j = 1, this%n | |
| this%gradient(i) = this%gradient(i) + this%Q(i,j) * this%state(j) | |
| end do; end do | |
| ! RHS = (J - R) * gradient + B * u | |
| call this%J%apply(this%gradient, Jg) | |
| call this%R%apply(this%gradient, Rg) | |
| Bu = ZERO | |
| do i = 1, this%n; do j = 1, size(this%input) | |
| Bu(i) = Bu(i) + this%B(i,j) * this%input(j) | |
| end do; end do | |
| rhs = Jg - Rg + Bu | |
| ! Explicit Euler step (first-order, replace with Radau IIA for stiff systems) | |
| this%state_dot = rhs | |
| this%state = this%state + dt * rhs | |
| this%time = this%time + dt | |
| ! Update power balance | |
| call this%power_balance() | |
| h_after = this%hamiltonian_val() | |
| this%hamiltonian = h_after | |
| ! Power balance error: |dH/dt - P_port + P_diss| | |
| balance_err = abs((h_after - h_before)/dt - this%power_port + this%power_diss) | |
| receipt%time_out = this%time | |
| receipt%h_out = h_after | |
| receipt%dh = h_after - h_before | |
| receipt%p_port = this%power_port | |
| receipt%p_diss = this%power_diss | |
| receipt%balance_err = balance_err | |
| receipt%balance_ok = balance_err < 1e-6_wp | |
| ! Seal to WORM chain | |
| call this%audit%seal('PHDAE_STEP', 't='//achar(0), int(this%time*1000,i8)) | |
| end subroutine phdae_step | |
| subroutine phdae_destroy(this) | |
| class(bob_phdae_t), intent(inout) :: this | |
| if (allocated(this%state)) deallocate(this%state) | |
| if (allocated(this%state_dot)) deallocate(this%state_dot) | |
| if (allocated(this%gradient)) deallocate(this%gradient) | |
| if (allocated(this%input)) deallocate(this%input) | |
| if (allocated(this%Q)) deallocate(this%Q) | |
| if (allocated(this%B)) deallocate(this%B) | |
| call this%J%destroy() | |
| call this%R%destroy() | |
| call this%audit%destroy() | |
| this%initialized = .false. | |
| end subroutine phdae_destroy | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ! C ABI | |
| !ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function bob_phdae_new(n, num_inputs) result(ptr) bind(C, name="bob_phdae_new") | |
| integer(c_int32_t), value :: n, num_inputs | |
| type(c_ptr) :: ptr | |
| type(bob_phdae_t), pointer :: sys | |
| allocate(sys) | |
| call sys%init(int(n,i4), int(num_inputs,i4)) | |
| ptr = c_loc(sys) | |
| end function bob_phdae_new | |
| function bob_phdae_step(sys_ptr, dt) result(err) bind(C, name="bob_phdae_step") | |
| type(c_ptr), value :: sys_ptr | |
| real(c_double), value :: dt | |
| real(c_double) :: err | |
| type(bob_phdae_t), pointer :: sys | |
| type(bob_step_receipt) :: receipt | |
| if (.not. c_associated(sys_ptr)) then; err = -1.0_wp; return; end if | |
| call c_f_pointer(sys_ptr, sys) | |
| call sys%step(real(dt,wp), receipt) | |
| err = real(receipt%balance_err, c_double) | |
| end function bob_phdae_step | |
| subroutine bob_phdae_free(sys_ptr) bind(C, name="bob_phdae_free") | |
| type(c_ptr), value :: sys_ptr | |
| type(bob_phdae_t), pointer :: sys | |
| if (.not. c_associated(sys_ptr)) return | |
| call c_f_pointer(sys_ptr, sys) | |
| call sys%destroy() | |
| deallocate(sys) | |
| end subroutine bob_phdae_free | |
| end module bob_phdae | |
| ! Made with Bob | |