@@ -95,17 +95,71 @@ def test_coder_roundtrip() -> None:
95
95
assert_identical (original , roundtripped )
96
96
97
97
98
- @pytest .mark .parametrize ("unpacked_dtype" , [np .float32 , np .float64 , np .int32 ])
98
+ @pytest .mark .parametrize ("ptype" , "u1 u2 u4 i1 i2 i4" .split ())
99
+ @pytest .mark .parametrize ("utype" , "f4 f8" .split ())
100
+ def test_mask_scale_roundtrip (utype : str , ptype : str ) -> None :
101
+ # this tests cf conforming packing/unpacking via
102
+ # encode_cf_variable/decode_cf_variable
103
+ # f4->i4 packing is skipped as non-conforming
104
+ if utype [1 ] == "4" and ptype [1 ] == "4" :
105
+ pytest .skip ("Can't pack float32 into int32/uint32" )
106
+ # fillvalues according to netCDF4
107
+ filldict = {
108
+ "i1" : - 127 ,
109
+ "u1" : 255 ,
110
+ "i2" : - 32767 ,
111
+ "u2" : 65535 ,
112
+ "i4" : - 2147483647 ,
113
+ "u4" : 4294967295 ,
114
+ }
115
+ fillvalue = filldict [ptype ]
116
+ unpacked_dtype = np .dtype (utype ).type
117
+ packed_dtype = np .dtype (ptype ).type
118
+ info = np .iinfo (packed_dtype )
119
+
120
+ # create original "encoded" Variable
121
+ packed_data = np .array (
122
+ [info .min , fillvalue , info .max - 1 , info .max ], dtype = packed_dtype
123
+ )
124
+ attrs = dict (
125
+ scale_factor = unpacked_dtype (1 ),
126
+ add_offset = unpacked_dtype (0 ),
127
+ _FillValue = packed_dtype (fillvalue ),
128
+ )
129
+ original = xr .Variable (("x" ,), packed_data , attrs = attrs )
130
+
131
+ # create wanted "decoded" Variable
132
+ unpacked_data = np .array (
133
+ [info .min , fillvalue , info .max - 1 , info .max ], dtype = unpacked_dtype
134
+ )
135
+ encoding = dict (
136
+ scale_factor = unpacked_dtype (1 ),
137
+ add_offset = unpacked_dtype (0 ),
138
+ _FillValue = packed_dtype (fillvalue ),
139
+ )
140
+ wanted = xr .Variable (("x" ), unpacked_data , encoding = encoding )
141
+ wanted = wanted .where (wanted != fillvalue )
142
+
143
+ # decode original and compare with wanted
144
+ decoded = decode_cf_variable ("x" , original )
145
+ assert wanted .dtype == decoded .dtype
146
+ xr .testing .assert_identical (wanted , decoded )
147
+
148
+ # encode again and compare with original
149
+ encoded = encode_cf_variable (decoded )
150
+ assert original .dtype == encoded .dtype
151
+ xr .testing .assert_identical (original , encoded )
152
+
153
+
154
+ @pytest .mark .parametrize ("unpacked_dtype" , "f4 f8 i4" .split ())
99
155
@pytest .mark .parametrize ("packed_dtype" , "u1 u2 i1 i2 f2 f4" .split ())
100
- def test_scaling_converts_to_float32 (
101
- packed_dtype : str , unpacked_dtype : type [np .number ]
102
- ) -> None :
156
+ def test_scaling_converts_to_float32 (packed_dtype : str , unpacked_dtype : str ) -> None :
103
157
# if scale_factor but no add_offset is given transform to float32 in any case
104
158
# this minimizes memory usage, see #1840, #1842
105
159
original = xr .Variable (
106
160
("x" ,),
107
161
np .arange (10 , dtype = packed_dtype ),
108
- encoding = dict (scale_factor = unpacked_dtype (10 )),
162
+ encoding = dict (scale_factor = np . dtype ( unpacked_dtype ). type (10 )),
109
163
)
110
164
coder = variables .CFScaleOffsetCoder ()
111
165
encoded = coder .encode (original )
@@ -115,7 +169,7 @@ def test_scaling_converts_to_float32(
115
169
assert roundtripped .dtype == np .float32
116
170
117
171
118
- @pytest .mark .parametrize ("unpacked_dtype" , [ np . float32 , np . float64 , np . int32 ] )
172
+ @pytest .mark .parametrize ("unpacked_dtype" , "f4 f8 i4" . split () )
119
173
@pytest .mark .parametrize ("packed_dtype" , "u1 u2 i1 i2 f2 f4" .split ())
120
174
def test_scaling_converts_to_float64 (
121
175
packed_dtype : str , unpacked_dtype : type [np .number ]
@@ -125,7 +179,7 @@ def test_scaling_converts_to_float64(
125
179
original = xr .Variable (
126
180
("x" ,),
127
181
np .arange (10 , dtype = packed_dtype ),
128
- encoding = dict (add_offset = unpacked_dtype (10 )),
182
+ encoding = dict (add_offset = np . dtype ( unpacked_dtype ). type (10 )),
129
183
)
130
184
coder = variables .CFScaleOffsetCoder ()
131
185
encoded = coder .encode (original )
@@ -139,7 +193,7 @@ def test_scaling_converts_to_float64(
139
193
@pytest .mark .parametrize ("add_offset" , (0.1 , [0.1 ]))
140
194
def test_scaling_offset_as_list (scale_factor , add_offset ) -> None :
141
195
# test for #4631
142
- # att : scale_factor and add_offset are not conforming to cf specs here
196
+ # attention : scale_factor and add_offset are not conforming to cf specs here
143
197
encoding = dict (scale_factor = scale_factor , add_offset = add_offset )
144
198
original = xr .Variable (("x" ,), np .arange (10.0 ), encoding = encoding )
145
199
coder = variables .CFScaleOffsetCoder ()
0 commit comments