diff --git a/src/systems/unit_check.jl b/src/systems/unit_check.jl index acf7451065..1f030a998e 100644 --- a/src/systems/unit_check.jl +++ b/src/systems/unit_check.jl @@ -104,7 +104,27 @@ function get_unit(op::Integral, args) return get_unit(args[1]) * unit end -equivalent(x, y) = isequal(x, y) +function equivalent(x, y) + # For DynamicQuantities, check dimensional compatibility rather than exact equality + if x isa DQ.AbstractQuantity && y isa DQ.AbstractQuantity + return DQ.dimension(x) == DQ.dimension(y) + elseif !(x isa DQ.AbstractQuantity) && !(y isa DQ.AbstractQuantity) + # Both are not quantities, use exact equality + return isequal(x, y) + else + # One is quantity, one is not - check if quantity is dimensionless + q = x isa DQ.AbstractQuantity ? x : y + non_q = x isa DQ.AbstractQuantity ? y : x + # Only consider equivalent if the quantity is dimensionless and other is zero/number + return DQ.dimension(q) == DQ.dimension(DQ.Quantity(1.0)) && (isequal(non_q, 0) || non_q isa Number) + end +end + +# For connections, we may want stricter equivalence (same scale for safety) +function equivalent_strict(x, y) + # Use exact equality for connections to ensure same scales + return isequal(x, y) +end function get_unit(op::Conditional, args) terms = get_unit.(args) terms[1] == unitless || @@ -234,7 +254,7 @@ function _validate(conn::Connection; info::String = "") else aunit = safe_get_unit(x, info * string(nameof(sys)) * "#$i") bunit = safe_get_unit(sst[j], info * string(nameof(s)) * "#$j") - if !equivalent(aunit, bunit) + if !equivalent_strict(aunit, bunit) valid = false str = "$info: connected system unknowns $x ($aunit) and $(sst[j]) ($bunit) have mismatched units." if oneunit(aunit) == oneunit(bunit) diff --git a/test/dq_units.jl b/test/dq_units.jl index ea1103db57..3e7a3a3558 100644 --- a/test/dq_units.jl +++ b/test/dq_units.jl @@ -283,3 +283,25 @@ end @test ModelingToolkit.get_unit.(filter(x -> occursin("ˍt", string(x)), unknowns(pend))) == [u"m/s", u"m/s"] end + +# Test for issue #3787: Validation of units with non-SI units should work +@testset "Issue #3787: Non-SI unit validation" begin + @parameters τ_ms [unit = u"ms"] + @variables t_ms [unit = u"ms"] E_kJ(t_ms) [unit = u"kJ"] P_MW(t_ms) [unit = u"MW"] + D_ms = Differential(t_ms) + + # This should pass: kJ/ms = kW, and MW and kW are dimensionally compatible (both power) + eqs_3787 = [D_ms(E_kJ) ~ P_MW - E_kJ / τ_ms, + 0 ~ P_MW] + @test MT.validate(eqs_3787) == true + + # Additional test: compatible units of different scales + @variables x_m [unit = u"m"] y_km [unit = u"km"] + eq_scales = [x_m ~ y_km] + @test MT.validate(eq_scales) == true + + # Test that incompatible dimensions still fail + @variables a_length [unit = u"m"] b_time [unit = u"s"] + eq_incompatible = [a_length ~ b_time] + @test MT.validate(eq_incompatible) == false +end