From e76352bb0a5fbaf53ef4640f00cf0763bc15cd7b Mon Sep 17 00:00:00 2001
From: Matt Signorelli <mgs255@cornell.edu>
Date: Sat, 4 Jan 2025 18:59:07 -0500
Subject: [PATCH 1/3] add ODE_DEFAULT_NORM overloads

---
 ext/DiffEqBaseGTPSAExt.jl | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/ext/DiffEqBaseGTPSAExt.jl b/ext/DiffEqBaseGTPSAExt.jl
index 655b5002e..3d9f50c82 100644
--- a/ext/DiffEqBaseGTPSAExt.jl
+++ b/ext/DiffEqBaseGTPSAExt.jl
@@ -2,16 +2,26 @@ module DiffEqBaseGTPSAExt
 
 if isdefined(Base, :get_extension)
     using DiffEqBase
-    import DiffEqBase: value
+    import DiffEqBase: value, ODE_DEFAULT_NORM
     using GTPSA
 else
     using ..DiffEqBase
-    import ..DiffEqBase: value
+    import ..DiffEqBase: value, ODE_DEFAULT_NORM
     using ..GTPSA
 end
 
-value(x::TPS) = scalar(x);
+value(x::TPS) = scalar(x)
 value(::Type{TPS{T}}) where {T} = T
 
+ODE_DEFAULT_NORM(u::TPS, t) = @fastmath abs(value(u))
+ODE_DEFAULT_NORM(f::F, u::TPS, t) where {F} = @fastmath abs(f(value(u)))
+
+function ODE_DEFAULT_NORM(u::AbstractArray{TPS{T}}, t) where {T <: Union{AbstractFloat, Complex}}
+    x = zero(real(T))
+    @inbounds @fastmath for ui in u
+        x += abs2(value(ui))
+    end
+    Base.FastMath.sqrt_fast(x / max(length(u), 1))
+end
 
 end
\ No newline at end of file

From 14350ab9d0c84d6f267c669d2111c1040ff498e9 Mon Sep 17 00:00:00 2001
From: Matt Signorelli <mgs255@cornell.edu>
Date: Sat, 4 Jan 2025 19:11:01 -0500
Subject: [PATCH 2/3] add test

---
 test/downstream/gtpsa.jl | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/test/downstream/gtpsa.jl b/test/downstream/gtpsa.jl
index 90f5b06c3..f60d1285c 100644
--- a/test/downstream/gtpsa.jl
+++ b/test/downstream/gtpsa.jl
@@ -1,5 +1,7 @@
 using OrdinaryDiffEq, ForwardDiff, GTPSA, Test
 
+# ODEProblem 1 =======================
+
 f!(du, u, p, t) = du .= p .* u
 
 # Initial variables and parameters
@@ -37,3 +39,38 @@ for i in 1:3
     @test Hi_FD ≈ GTPSA.hessian(sol_GTPSA.u[end][i], include_params=true)
 end
 
+
+# ODEProblem 2 =======================
+pdot!(dq, p, q, params, t) = dq .= [0.0, 0.0, 0.0] 
+qdot!(dp, p, q, params, t) = dp .= [p[1] / sqrt((1 + p[3])^2 - p[1]^2 - p[2]^2), 
+                                    p[2] / sqrt((1 + p[3])^2 - p[1]^2 - p[2]^2),
+                                    p[3] / sqrt(1 + p[3]^2) - (p[3] + 1)/sqrt((1 + p[3])^2 - p[1]^2 - p[2]^2)]
+
+prob = DynamicalODEProblem(pdot!, qdot!, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], (0.0, 25.0))
+sol = solve(prob, Yoshida6(), dt = 1.0, reltol=1e-16, abstol=1e-16)
+
+desc = Descriptor(6, 2) # 6 variables to 2nd order
+dx  = vars(desc) # identity map
+prob_GTPSA = DynamicalODEProblem(pdot!, qdot!, dx[1:3], dx[4:6], (0.0, 25.0))
+sol_GTPSA = solve(prob_GTPSA, Yoshida6(), dt = 1.0, reltol=1e-16, abstol=1e-16)
+
+@test sol.u[end] ≈ scalar.(sol_GTPSA.u[end]) # scalar gets 0th order part
+
+# Compare Jacobian against ForwardDiff
+J_FD = ForwardDiff.jacobian(zeros(6)) do t
+    prob = DynamicalODEProblem(pdot!, qdot!, t[1:3], t[4:6], (0.0, 25.0))
+    sol = solve(prob, Yoshida6(), dt = 1.0, reltol=1e-16, abstol=1e-16)
+    sol.u[end]
+end
+
+@test J_FD ≈ GTPSA.jacobian(sol_GTPSA.u[end], include_params=true)
+
+# Compare Hessians against ForwardDiff
+for i in 1:6
+    Hi_FD = ForwardDiff.hessian(zeros(6)) do t
+        prob =  DynamicalODEProblem(pdot!, qdot!, t[1:3], t[4:6], (0.0, 25.0))
+        sol = solve(prob, Yoshida6(), dt = 1.0, reltol=1e-16, abstol=1e-16)
+        sol.u[end][i]
+    end
+    @test Hi_FD ≈ GTPSA.hessian(sol_GTPSA.u[end][i], include_params=true)
+end

From 0f8481cdb83740d52c6ead858097edeca58f4e10 Mon Sep 17 00:00:00 2001
From: Matt Signorelli <mgs255@cornell.edu>
Date: Sun, 5 Jan 2025 09:37:06 -0500
Subject: [PATCH 3/3] value -> normTPS

---
 ext/DiffEqBaseGTPSAExt.jl | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/ext/DiffEqBaseGTPSAExt.jl b/ext/DiffEqBaseGTPSAExt.jl
index 3d9f50c82..f0ee539a7 100644
--- a/ext/DiffEqBaseGTPSAExt.jl
+++ b/ext/DiffEqBaseGTPSAExt.jl
@@ -13,13 +13,21 @@ end
 value(x::TPS) = scalar(x)
 value(::Type{TPS{T}}) where {T} = T
 
-ODE_DEFAULT_NORM(u::TPS, t) = @fastmath abs(value(u))
-ODE_DEFAULT_NORM(f::F, u::TPS, t) where {F} = @fastmath abs(f(value(u)))
+ODE_DEFAULT_NORM(u::TPS, t) = normTPS(u)
+ODE_DEFAULT_NORM(f::F, u::TPS, t) where {F} = normTPS(f(u))
 
-function ODE_DEFAULT_NORM(u::AbstractArray{TPS{T}}, t) where {T <: Union{AbstractFloat, Complex}}
+function ODE_DEFAULT_NORM(u::AbstractArray{TPS{T}}, t) where {T}
     x = zero(real(T))
     @inbounds @fastmath for ui in u
-        x += abs2(value(ui))
+        x += normTPS(ui)^2
+    end
+    Base.FastMath.sqrt_fast(x / max(length(u), 1))
+end
+
+function ODE_DEFAULT_NORM(f::F, u::AbstractArray{TPS{T}}, t) where {F, T}
+    x = zero(real(T))
+    @inbounds @fastmath for ui in u
+        x += normTPS(f(ui))^2
     end
     Base.FastMath.sqrt_fast(x / max(length(u), 1))
 end