66from iam_units import registry
77
88from pyam import IamDataFrame
9- from pyam ._ops import _op_data
9+ from pyam .operations import apply_ops
1010from pyam .testing import assert_iamframe_equal
1111from pyam .utils import IAMC_IDX
1212
@@ -119,6 +119,7 @@ def test_add_variable_ignore_units(test_df_year, arg, df_func, fillna, append):
119119
120120@pytest .mark .parametrize ("append" , (False , True ))
121121def test_add_variable_non_si_unit (test_df_year , append ):
122+ """Check that in-dataframe addition works with non-SI-units"""
122123 df = test_df_year .rename (unit = {"EJ/yr" : "foo" })
123124
124125 exp = df_ops_variable (operator .add , "Sum" , unit = "foo" , meta = test_df_year .meta )
@@ -135,7 +136,7 @@ def test_add_variable_non_si_unit(test_df_year, append):
135136
136137@pytest .mark .parametrize ("append" , (False , True ))
137138def test_add_scenario (test_df_year , append ):
138- """Verify that in-dataframe addition works on a custom axis (`scenario`)"""
139+ """Check that in-dataframe addition works on a custom axis (`scenario`)"""
139140
140141 v = ("scen_a" , "scen_b" , "scen_sum" )
141142 exp = IamDataFrame (
@@ -218,7 +219,8 @@ def test_subtract_variable_ignore_units(test_df_year, arg, df_func, fillna, appe
218219
219220
220221@pytest .mark .parametrize ("append" , (False , True ))
221- def test_subtract_variable_non_si_unit_unit (test_df_year , append ):
222+ def test_subtract_variable_non_si_unit (test_df_year , append ):
223+ """Check that in-dataframe addition works with non-SI units"""
222224 df = test_df_year .rename (unit = {"EJ/yr" : "foo" })
223225
224226 exp = df_ops_variable (operator .sub , "Diff" , unit = "foo" , meta = test_df_year .meta )
@@ -235,7 +237,7 @@ def test_subtract_variable_non_si_unit_unit(test_df_year, append):
235237
236238@pytest .mark .parametrize ("append" , (False , True ))
237239def test_subtract_scenario (test_df_year , append ):
238- """Verify that in-dataframe subtraction works on a custom axis (`scenario`)"""
240+ """Check that in-dataframe subtraction works on a custom axis (`scenario`)"""
239241
240242 v = ("scen_a" , "scen_b" , "scen_diff" )
241243 exp = IamDataFrame (
@@ -265,7 +267,7 @@ def test_subtract_scenario(test_df_year, append):
265267)
266268@pytest .mark .parametrize ("append" , (False , True ))
267269def test_multiply_variable (test_df_year , arg , df_func , expected_unit , append ):
268- """Check that in-dataframe addition works on the default `variable` axis"""
270+ """Check that in-dataframe multiplication works on the default `variable` axis"""
269271
270272 exp = df_func (operator .mul , "Prod" , unit = expected_unit , meta = test_df_year .meta )
271273
@@ -316,7 +318,7 @@ def test_multiply_variable_ignore_units(test_df_year, arg, df_func, fillna, appe
316318
317319@pytest .mark .parametrize ("append" , (False , True ))
318320def test_multiply_scenario (test_df_year , append ):
319- """Verify that in-dataframe addition works on a custom axis (`scenario`)"""
321+ """Check that in-dataframe multiplication works on a custom axis (`scenario`)"""
320322
321323 v = ("scen_a" , "scen_b" , "scen_product" )
322324 exp = IamDataFrame (
@@ -347,7 +349,7 @@ def test_multiply_scenario(test_df_year, append):
347349)
348350@pytest .mark .parametrize ("append" , (False , True ))
349351def test_divide_variable (test_df_year , arg , df_func , expected_unit , append ):
350- """Check that in-dataframe addition works on the default `variable` axis"""
352+ """Check that in-dataframe division works on the default `variable` axis"""
351353
352354 exp = df_func (operator .truediv , "Ratio" , unit = expected_unit , meta = test_df_year .meta )
353355
@@ -361,6 +363,42 @@ def test_divide_variable(test_df_year, arg, df_func, expected_unit, append):
361363 assert_iamframe_equal (exp , obs )
362364
363365
366+ @pytest .mark .parametrize ("value" , (0 , 0.0 , registry .Quantity (0 , "EJ/yr" )))
367+ def test_divide_by_zero_raises (test_df_year , value ):
368+ """Check that division by zero (as single value) raises an error"""
369+ with pytest .raises (ZeroDivisionError ):
370+ test_df_year .divide ("Primary Energy" , value , "Ratio" )
371+
372+
373+ @pytest .mark .parametrize ("append" , (False , True ))
374+ def test_divide_by_zero_drop_zero (test_df_year , append , caplog ):
375+ """Check that division by zero in a series removes zeroes and writes to log"""
376+
377+ exp = df_ops_variable (operator .truediv , "Ratio" , unit = "" , meta = test_df_year .meta )
378+ exp .filter (year = 2005 , inplace = True )
379+
380+ test_df_year ._data .loc [
381+ "model_a" , "scen_a" , "World" , "Primary Energy|Coal" , "EJ/yr" , 2010
382+ ] = 0
383+
384+ if append :
385+ obs = test_df_year .copy ()
386+ obs .divide ("Primary Energy" , "Primary Energy|Coal" , "Ratio" , append = True )
387+ exp = test_df_year .append (exp )
388+ else :
389+ obs = test_df_year .divide ("Primary Energy" , "Primary Energy|Coal" , "Ratio" )
390+
391+ assert_iamframe_equal (exp , obs )
392+
393+ msg = (
394+ "Dropped 1 datapoints to avoid division by zero:\n "
395+ " model scenario region year\n "
396+ "0 model_a scen_a World 2010"
397+ )
398+ idx = caplog .messages .index (msg )
399+ assert caplog .records [idx ].levelname == "WARNING"
400+
401+
364402@pytest .mark .parametrize (
365403 "arg, df_func, fillna" ,
366404 (
@@ -370,7 +408,7 @@ def test_divide_variable(test_df_year, arg, df_func, expected_unit, append):
370408)
371409@pytest .mark .parametrize ("append" , (False , True ))
372410def test_divide_variable_ignore_units (test_df_year , arg , df_func , fillna , append ):
373- """Check that in-dataframe addition works with ignore_units"""
411+ """Check that in-dataframe division works with ignore_units"""
374412
375413 # change one unit to make ignore_units strictly necessary
376414 test_df_year .rename (
@@ -399,6 +437,7 @@ def test_divide_variable_ignore_units(test_df_year, arg, df_func, fillna, append
399437
400438@pytest .mark .parametrize ("append" , (False , True ))
401439def test_divide_variable_non_si_unit_unit (test_df_year , append ):
440+ """Check that in-dataframe addition works with non-SI units"""
402441 df = test_df_year .rename (unit = {"EJ/yr" : "foo" })
403442
404443 exp = df_ops_variable (operator .truediv , "Ratio" , unit = "" , meta = test_df_year .meta )
@@ -415,7 +454,7 @@ def test_divide_variable_non_si_unit_unit(test_df_year, append):
415454
416455@pytest .mark .parametrize ("append" , (False , True ))
417456def test_divide_scenario (test_df_year , append ):
418- """Verify that in-dataframe addition works on a custom axis (`scenario`)"""
457+ """Check that in-dataframe division works on a custom axis (`scenario`)"""
419458
420459 v = ("scen_a" , "scen_b" , "scen_ratio" )
421460 exp = IamDataFrame (
@@ -438,7 +477,7 @@ def test_divide_scenario(test_df_year, append):
438477
439478@pytest .mark .parametrize ("append" , (False , True ))
440479def test_apply_variable (test_df_year , append ):
441- """Verify that in-dataframe apply works on the default `variable` axis"""
480+ """Check that in-dataframe ` apply` works on the default `variable` axis"""
442481
443482 def custom_func (a , b , c , d ):
444483 return a * b + c * d
@@ -471,13 +510,13 @@ def custom_func(a, b, c, d):
471510def test_ops_unknown_axis (test_df_year ):
472511 """Using an unknown axis raises an error"""
473512 with pytest .raises (ValueError , match = "Unknown axis: foo" ):
474- _op_data (test_df_year , "_" , "_" , "foo" )
513+ apply_ops (test_df_year , "_" , "_" , "foo" )
475514
476515
477516def test_ops_unknown_method (test_df_year ):
478517 """Using an unknown method raises an error"""
479518 with pytest .raises (ValueError , match = "Unknown method: foo" ):
480- _op_data (test_df_year , "_" , "foo" , "variable" )
519+ apply_ops (test_df_year , "_" , "foo" , "variable" )
481520
482521
483522@pytest .mark .parametrize ("periods, year" , (({}, 2010 ), ({"periods" : - 1 }, 2005 )))
0 commit comments