@@ -178,3 +178,114 @@ end
178178 PowerSystems. sanitize_angle_limits! (bad_angle_limits)
179179 )
180180end
181+
182+ @testset " Negative branch rating fails validation cleanly" begin
183+ # Two buses at equal base voltage so the endpoint-voltage check passes and
184+ # validation reaches correct_rate_limits!.
185+ bus_from = ACBus (
186+ 1 , " from" , true , ACBusTypes. REF, 0 , 1.0 , (min = 0.9 , max = 1.05 ), 230 ,
187+ nothing , nothing ,
188+ )
189+ bus_to = ACBus (
190+ 2 , " to" , true , ACBusTypes. PQ, 0 , 1.0 , (min = 0.9 , max = 1.05 ), 230 ,
191+ nothing , nothing ,
192+ )
193+ sys = System (100.0 ; runchecks = false )
194+ add_component! (sys, bus_from)
195+ add_component! (sys, bus_to)
196+ neg_line = Line (
197+ " negline" ,
198+ true ,
199+ 0.0 ,
200+ 0.0 ,
201+ Arc (; from = bus_from, to = bus_to),
202+ 0.01 ,
203+ 0.1 ,
204+ (from = 0.00356 , to = 0.00356 ),
205+ - 1.0 , # negative rating
206+ (min = - pi / 2 , max = pi / 2 ),
207+ )
208+ add_component! (sys, neg_line)
209+
210+ # An IS.MultiLogger is enabled at Error and rethrows log-record-generation
211+ # errors (catch_exceptions(::MultiLogger) == false), exactly like the loggers
212+ # Sienna test suites install. Under such a logger the previous `$(rating)`
213+ # typo raised UndefVarError instead of the intended IS.InvalidValue. A
214+ # NullLogger would *not* catch this regression because Julia never evaluates a
215+ # disabled log message.
216+ test_logger = IS. MultiLogger ([ConsoleLogger (devnull , Logging. Error)])
217+ Logging. with_logger (test_logger) do
218+ @test_throws IS. InvalidValue PowerSystems. check_component (sys, neg_line)
219+ end
220+ end
221+
222+ @testset " line_rating_calculation uses to-side minimum voltage" begin
223+ # Asymmetric endpoint voltage limits expose whether the to-side minimum
224+ # voltage is read from the correct bus.
225+ bus_from = ACBus (
226+ 1 , " from" , true , ACBusTypes. REF, 0 , 1.0 , (min = 0.9 , max = 1.05 ), 230 ,
227+ nothing , nothing ,
228+ )
229+ bus_to = ACBus (
230+ 2 , " to" , true , ACBusTypes. PQ, 0 , 1.0 , (min = 0.5 , max = 1.05 ), 230 ,
231+ nothing , nothing ,
232+ )
233+ line = Line (
234+ " l" ,
235+ true ,
236+ 0.0 ,
237+ 0.0 ,
238+ Arc (; from = bus_from, to = bus_to),
239+ 0.01 ,
240+ 0.1 ,
241+ (from = 0.00356 , to = 0.00356 ),
242+ 100.0 ,
243+ (min = - 0.2 , max = 0.3 ),
244+ )
245+
246+ r, x = 0.01 , 0.1
247+ g = r / (r^ 2 + x^ 2 )
248+ b = - x / (r^ 2 + x^ 2 )
249+ y_mag = sqrt (g^ 2 + b^ 2 )
250+ fr_vmin, to_vmin = 0.9 , 0.5
251+ theta_max = 0.3
252+ c_max = sqrt (fr_vmin^ 2 + to_vmin^ 2 - 2 * fr_vmin * to_vmin * cos (theta_max))
253+ expected = y_mag * max (fr_vmin, to_vmin) * c_max
254+
255+ @test PowerSystems. line_rating_calculation (line) ≈ expected
256+ end
257+
258+ @testset " Negative transformer rating fails validation cleanly" begin
259+ bus_from = ACBus (
260+ 1 , " from" , true , ACBusTypes. REF, 0 , 1.0 , (min = 0.9 , max = 1.05 ), 230 ,
261+ nothing , nothing ,
262+ )
263+ bus_to = ACBus (
264+ 2 , " to" , true , ACBusTypes. PQ, 0 , 1.0 , (min = 0.9 , max = 1.05 ), 230 ,
265+ nothing , nothing ,
266+ )
267+ sys = System (100.0 ; runchecks = false )
268+ add_component! (sys, bus_from)
269+ add_component! (sys, bus_to)
270+ # rating_b has no descriptor valid_range, so only the PSY-level guard can
271+ # reject a negative secondary rating.
272+ xfrm = Transformer2W (;
273+ name = " negxfrm" ,
274+ available = true ,
275+ active_power_flow = 0.0 ,
276+ reactive_power_flow = 0.0 ,
277+ arc = Arc (; from = bus_from, to = bus_to),
278+ r = 0.01 ,
279+ x = 0.1 ,
280+ primary_shunt = 0.0 ,
281+ rating = 1.0 ,
282+ base_power = 100.0 ,
283+ rating_b = - 1.0 , # negative secondary rating
284+ )
285+ add_component! (sys, xfrm)
286+
287+ test_logger = IS. MultiLogger ([ConsoleLogger (devnull , Logging. Error)])
288+ Logging. with_logger (test_logger) do
289+ @test_throws IS. InvalidValue PowerSystems. check_component (sys, xfrm)
290+ end
291+ end
0 commit comments