@@ -332,6 +332,33 @@ def calc_costs(
332332 # interpolation with 0's
333333 surge_noadapt = []
334334 surge = []
335+
336+ def _check_vals (rh_diff_arr , lslr_arr , seg , check_rh_diff_max = True ):
337+ # for surge_noadapt, rh_diff can be greater than max. This happens when
338+ # a segment chooses retreat10000 in their refA (initial adaptation) and
339+ # also has a declining no-climate-change SLR scenario (e.g. for
340+ # no-climate-change scenarios). However, we automatically assign 0
341+ # expected storm impacts for retreat10000, so this is acceptable and
342+ # does not need to exist in the surge lookup table.
343+ error_str = (
344+ "{0} value is {1} than {2} in storm damage lookup "
345+ f"table for segment { seg } . Please investigate why this is. You may "
346+ "need to re-generate the surge lookup table (or perhaps the `refA` "
347+ "(initial adaptation) table if it was generated for a different "
348+ "set of SLR scenarios or years."
349+ )
350+ # lslr is allowed to be below min surge lookup value b/c we created
351+ # lookup such that < min value will be 0 impact
352+ if (
353+ check_rh_diff_max
354+ and rh_diff_arr .max () > this_surge_lookup .rh_diff .max ()
355+ ):
356+ raise ValueError (error_str .format ("rh_diff" , "higher" , "maximum" ))
357+ if rh_diff_arr .min () < this_surge_lookup .rh_diff .min ():
358+ raise ValueError (error_str .format ("rh_diff" , "lower" , "minimum" ))
359+ if lslr_arr .max () > this_surge_lookup .lslr .max ():
360+ raise ValueError (error_str .format ("lslr" , "higher" , "maximum" ))
361+
335362 for seg in inputs .seg .values :
336363 this_surge_lookup = (
337364 surge_lookup .sel (seg = seg )
@@ -341,32 +368,58 @@ def calc_costs(
341368 )
342369 if this_surge_lookup .sum () == 0 :
343370 continue
344- surge_noadapt .append (
371+
372+ this_rh_diff_noadapt = rh_diff_noadapt .sel (seg = seg , drop = True )
373+ this_lslr = lslr .sel (seg = seg , drop = True )
374+ _check_vals (
375+ this_rh_diff_noadapt , this_lslr , seg , check_rh_diff_max = False
376+ )
377+
378+ lslr_too_low = this_lslr < this_surge_lookup .lslr .min ()
379+
380+ this_surge_noadapt = (
345381 this_surge_lookup .sel (adapttype = "retreat" , drop = True )
346382 .interp (
347- lslr = lslr . sel ( seg = seg ) ,
348- rh_diff = rh_diff_noadapt . sel ( seg = seg ) ,
383+ lslr = this_lslr ,
384+ rh_diff = this_rh_diff_noadapt ,
349385 assume_sorted = True ,
350- kwargs = {"fill_value" : 0 },
351386 )
352387 .reset_coords (drop = True )
353388 .expand_dims (seg = [seg ])
354389 )
355390
391+ # ensure nans are only at the low end of LSLR or high end of rh_diff
392+ rh_diff_too_high = (
393+ this_rh_diff_noadapt > this_surge_lookup .rh_diff .max ()
394+ )
395+ assert (
396+ this_surge_noadapt .notnull () | lslr_too_low | rh_diff_too_high
397+ ).all (), seg
398+
399+ surge_noadapt .append (this_surge_noadapt .fillna (0 ))
400+
356401 surge_adapt = []
402+
403+ this_rh_diff_adapt = rh_diff .sel (seg = seg , drop = True )
404+ _check_vals (this_rh_diff_adapt , this_lslr , seg )
405+
357406 for adapttype in this_surge_lookup .adapttype .values :
358- surge_adapt . append (
407+ this_surge_adapt = (
359408 this_surge_lookup .sel (adapttype = adapttype )
360409 .interp (
361- lslr = lslr . sel ( seg = seg ) ,
362- rh_diff = rh_diff .sel (
363- adapttype = adapttype , seg = seg , drop = True
410+ lslr = this_lslr ,
411+ rh_diff = this_rh_diff_adapt .sel (
412+ adapttype = adapttype , drop = True
364413 ),
365414 assume_sorted = True ,
366- kwargs = {"fill_value" : 0 },
367415 )
368416 .reset_coords (drop = True )
369417 )
418+
419+ # ensure nans are only at the low end of lslr
420+ assert (this_surge_adapt .notnull () | lslr_too_low ).all (), seg
421+
422+ surge_adapt .append (this_surge_adapt .fillna (0 ))
370423 surge .append (
371424 xr .concat (surge_adapt , dim = this_surge_lookup .adapttype ).expand_dims (
372425 seg = [seg ]
@@ -1066,7 +1119,6 @@ def execute_pyciam(
10661119 # determine whether to check for finished jobs
10671120 if output_path is None :
10681121 check = False
1069- tmp_output_path = None
10701122 else :
10711123 check = True
10721124
@@ -1149,7 +1201,7 @@ def execute_pyciam(
11491201 storage_options = storage_options ,
11501202 )
11511203 # block on this calculation
1152- wait (surge_futs )
1204+ client . gather (surge_futs )
11531205
11541206 ###############################
11551207 # define temporary output store
@@ -1372,7 +1424,7 @@ def execute_pyciam(
13721424 ###############################
13731425 # Rechunk and save final
13741426 ###############################
1375- wait (ciam_futs_2 .tolist ())
1427+ client . gather (ciam_futs_2 .tolist ())
13761428 assert [f .status == "finished" for f in ciam_futs_2 .tolist ()]
13771429 client .cancel (ciam_futs_2 )
13781430 del ciam_futs_2
@@ -1465,7 +1517,9 @@ def get_refA(
14651517 ** params .refA_scenario_selectors ,
14661518 )
14671519 slr = slr .unstack ("scen_mc" )
1468- slr = slr .squeeze (drop = True )
1520+ slr = slr .squeeze (
1521+ [d for d in slr .dims if len (slr [d ]) == 1 and d != "seg" ], drop = True
1522+ )
14691523
14701524 costs , refA = calc_costs (
14711525 inputs , slr , surge_lookup = surge , return_year0_hts = True , ** model_kwargs
0 commit comments