@@ -402,6 +402,10 @@ function observed_symbols(nw::Network)
402402end
403403
404404function SII. observed (nw:: Network , snis)
405+ if (snis isa AbstractVector || snis isa Tuple) && any (sni -> sni isa ObservableExpression, snis)
406+ throw (ArgumentError (" Cannot mix normal symbolic indices with @obsex currently!" ))
407+ end
408+
405409 _snis = _expand_and_collect (nw, snis)
406410 isscalar = _snis isa SymbolicIndex
407411 if isscalar
@@ -1079,3 +1083,80 @@ extract_nw(p::IndexingProxy) = extract_nw(p.s)
10791083function extract_nw (:: Nothing )
10801084 throw (ArgumentError (" Needs system context to generate matching indices. Pass Network, sol, prob, ..." ))
10811085end
1086+
1087+ # ###
1088+ # ### Observable Expressions
1089+ # ###
1090+ struct ObservableExpression{VT,F,Ex,N}
1091+ inputs:: Vector{VT}
1092+ f:: F
1093+ ex:: Ex
1094+ name:: N
1095+ end
1096+ function Base. show (io:: IO , mime:: MIME"text/plain" , obsex:: ObservableExpression )
1097+ print (io, " ObservableExpression(" )
1098+ isnothing (obsex. name) || print (io, obsex. name, " = " )
1099+ show (io, mime, obsex. ex)
1100+ print (io, " )" )
1101+ end
1102+
1103+ """
1104+ @obsex([name =] expression)
1105+
1106+ Define observable expressions, which are simple combinations of knonw
1107+ states/parameters/observables. `@obsex(...)` returns an `ObservableExpression`
1108+ which can be used as an symbolic index. This is mainly intended for quick
1109+ plotting or export of common "derived" variables, such as the argument of a
1110+ 2-component complex state. For example:
1111+
1112+ sol(t; idxs=@obsex(arg = atan(VIndex(1,:u_i), VIndex(1,:u_r))]
1113+ sol(t; idxs=@obsex(δrel = VIndex(1,:δ) - VIndex(2,:δ)))
1114+
1115+ """
1116+ macro obsex (ex)
1117+ generate_observable_expression (ex)
1118+ end
1119+
1120+ function generate_observable_expression (:: Any )
1121+ error (" @obsex can only be used when Symbolics.jl is loaded." )
1122+ end
1123+
1124+ # define function stub to overload in SymbolicsExt
1125+ function collect_symbol! end
1126+
1127+ function SII. is_observed (nw:: Network , obsex:: ObservableExpression )
1128+ true
1129+ end
1130+
1131+ SII. symbolic_type (:: Type{<:ObservableExpression} ) = SII. ScalarSymbolic ()
1132+ SII. hasname (:: ObservableExpression ) = true
1133+ function SII. getname (obsex:: ObservableExpression )
1134+ if obsex. name isa Symbol
1135+ return obsex. name
1136+ else
1137+ io = IOBuffer ()
1138+ show (io, MIME " text/plain" (), obsex. ex)
1139+ str = String (take! (io))
1140+ return Symbol (replace (str, " " => " " ))
1141+ end
1142+ end
1143+
1144+ function SII. observed (nw:: Network , obsex:: ObservableExpression )
1145+ inputf = SII. observed (nw, obsex. inputs)
1146+ (u, p, t) -> begin
1147+ input = inputf (u, p, t)
1148+ obsex. f (input)
1149+ end
1150+ end
1151+ function SII. observed (nw:: Network , obsexs:: AbstractVector{<:ObservableExpression} )
1152+ inputfs = map (obsex -> SII. observed (nw, obsex. inputs), obsexs)
1153+ (u, p, t) -> begin
1154+ map (obsexs, inputfs) do obsex, inputf
1155+ input = inputf (u, p, t)
1156+ obsex. f (input)
1157+ end
1158+ end
1159+ end
1160+
1161+ Base. getindex (s:: NWState , idx:: ObservableExpression ) = SII. getu (s, idx)(s)
1162+ Base. getindex (s:: NWParameter , idx:: ObservableExpression ) = SII. getp (s, idx)(s)
0 commit comments