1111
1212import flopy
1313import numpy as np
14+ import pandas as pd
1415import pytest
1516from framework import TestFramework
1617
17- cases = ["sfe-conductn" , "sfe-conducti" , "sfe-conducto" , "sfe-conductm" ]
18+ cases = ["sfe-conductn" , "sfe-conducti" , "sfe-conducto" , "sfe-conductm" , "sfe_conductk" ]
1819#
1920# The last letter in the names above indicates the following
2021# n = "no gw/sw exchange"
2122# i = "gwf into strm"
2223# o = "strm to gw"
2324# m = "mixed" (i.e., convection one direction, conductive gradient the other direction?)
25+ # k = "known answer"
2426
2527k11 = 500.0
26- rhk = [0.0 , k11 , k11 , k11 ]
27- strt_gw_temp = [4.0 , 4.0 , 4.0 , 20.0 ]
28- strm_temp = [18.0 , 18.0 , 20.0 , 4.0 ]
29- chd_condition = ["n" , "i" , "o" , "m" ]
28+ rhk = [0.0 , k11 , k11 , k11 , 0.0 ]
29+ strt_gw_temp = [4.0 , 4.0 , 4.0 , 20.0 , 1.0 ]
30+ strm_temp = [18.0 , 18.0 , 20.0 , 4.0 , 10.0 ]
31+ chd_condition = ["n" , "i" , "o" , "m" , "k" ]
3032surf_Q_in = [
3133 [8.64 , 0.0 ],
3234 [8640.0 , 0.0 ],
3335 [8640.0 , 0.0 ],
3436 [8640.0 , 0.0 ],
37+ [8640.0 , 0.0 ],
3538] # 86400 m^3/d = 1 m^3/s = 35.315 cfs
3639
3740
@@ -200,7 +203,7 @@ def trenddetector(list_of_index, array_of_data, order=1):
200203lhv = 2454000.0 # Latent heat of vaporization ($J/kg$)
201204# Thermal conductivity of the streambed material ($W/m/C$)
202205K_therm_strmbed = [1.5 , 1.75 , 2.0 ]
203- rbthcnd = [0.0001 , 0.0001 , 0.0001 , 0.0001 ]
206+ rbthcnd = [0.0001 , 0.0001 , 0.0001 , 0.0001 , 0.0001 ]
204207
205208# time params
206209steady = {0 : False , 1 : False }
@@ -324,6 +327,9 @@ def build_models(idx, test):
324327 elif chd_condition [idx ] == "m" :
325328 chdelev1 = top [0 , 0 ] - 3.0 # convection from stream to gw,
326329 chdelev2 = top [0 , - 1 ] - 3.0 # conduction from gw to strm
330+ elif chd_condition [idx ] == "k" :
331+ chdelev1 = top [0 , 0 ] - 3.0
332+ chdelev2 = top [0 , - 1 ] - 3.0
327333
328334 # Instantiate constant head boundary package
329335 if chd_on :
@@ -540,6 +546,23 @@ def build_models(idx, test):
540546 sfeperioddata .append ((irno , "INFLOW" , strm_temp [idx ]))
541547 # sfeperioddata.append((irno, sfr_applied_bnd[idx], sfe_applied_temp[idx]))
542548
549+ sfe_obs = {
550+ (gwename + ".sfe.obs.csv" ,): [
551+ (f"sfe-{ i + 1 } -temp" , "TEMPERATURE" , i + 1 ) for i in range (3 )
552+ ]
553+ + [
554+ ("sfe-extin" , "EXT-INFLOW" , 1 ),
555+ ("sfe-rain" , "RAINFALL" , 1 ),
556+ ("sfe-roff" , "RUNOFF" , 1 ),
557+ ("sfe-evap" , "EVAPORATION" , 1 ),
558+ ("sfe-extout" , "EXT-OUTFLOW" , 3 ),
559+ ("sfe-sfe" , "SFE" , 2 ),
560+ ("sfe-strmbd1" , "STRMBD-COND" , 1 ),
561+ ("sfe-strmbd2" , "STRMBD-COND" , 2 ),
562+ ("sfe-strmbd3" , "STRMBD-COND" , 3 ),
563+ ],
564+ }
565+
543566 flopy .mf6 .modflow .ModflowGwesfe (
544567 gwe ,
545568 boundnames = False ,
@@ -552,6 +575,7 @@ def build_models(idx, test):
552575 packagedata = sfepackagedata ,
553576 reachperioddata = sfeperioddata ,
554577 flow_package_name = "SFR-1" ,
578+ observations = sfe_obs ,
555579 pname = "SFE-1" ,
556580 filename = f"{ gwename } .sfe" ,
557581 )
@@ -584,6 +608,7 @@ def check_output(idx, test):
584608 # read flow results from model
585609 name = cases [idx ]
586610 gwfname = "gwf-" + name
611+ gwename = "gwe-" + name
587612
588613 fname = gwfname + ".sfr.cbc"
589614 fname = os .path .join (test .workspace , fname )
@@ -608,6 +633,7 @@ def check_output(idx, test):
608633 shared_area = np .array (shared_area )
609634
610635 # Calculate wetted streambed area for comparison
636+ wa_lst = []
611637 for j , stg in enumerate (list (sfrstg [0 ])[1 :]):
612638 wp = calc_wp (j , stg )
613639 wa = wp * delr
@@ -618,6 +644,7 @@ def check_output(idx, test):
618644 )
619645
620646 assert np .isclose (wa , shared_area [0 , j ], atol = 1e-4 ), msg
647+ wa_lst .append (wa )
621648
622649 # Sub-scenario checks
623650 # initialize search term
@@ -627,10 +654,31 @@ def check_output(idx, test):
627654 fname = "gwe-" + name + ".lst"
628655 fname = os .path .join (test .workspace , fname )
629656
657+ # pull in SFE CSV output for comparison with lst file budget term
658+ fpth = os .path .join (test .workspace , gwename + ".sfe.obs.csv" )
659+ df = pd .read_csv (fpth )
660+
630661 # gw exchng (item 'GWF') should be zero in heat transport budget
631662 T_in , T_out , in_bud_lst , out_bud_lst = get_bud (fname , srchStr )
632663 assert np .isclose (T_in , T_out , atol = 0.1 ), "There is a heat budget discrepancy"
633664
665+ # compare individual streambed conduction obs with total
666+ df ["sum_strmbd_cond" ] = df .iloc [:, - 3 :].sum (axis = 1 )
667+ if name [- 1 ] != "m" :
668+ assert np .isclose (
669+ out_bud_lst ["STRMBD-COND" ],
670+ abs (df .loc [0 , "sum_strmbd_cond" ]),
671+ atol = 0.0001 ,
672+ ), "There is a streambed conductance discrepancy " + str (
673+ out_bud_lst ["STRMBD-COND" ] - abs (df .loc [0 , "sum_strmbd_cond" ])
674+ )
675+ else :
676+ assert np .isclose (
677+ in_bud_lst ["STRMBD-COND" ], abs (df .loc [0 , "sum_strmbd_cond" ]), atol = 0.0001
678+ ), "There is a streambed conductance discrepancy " + str (
679+ out_bud_lst ["STRMBD-COND" ] - abs (df .loc [0 , "sum_strmbd_cond" ])
680+ )
681+
634682 # Get temperature of streamwater
635683 fname1 = "gwe-" + name + ".sfe.bin"
636684 fname1 = os .path .join (test .workspace , fname1 )
@@ -655,6 +703,10 @@ def check_output(idx, test):
655703 "conductive losses from the stream to the aquifer "
656704 "(i.e., greater shared wetted areas)"
657705 )
706+ msg5 = (
707+ "The conductive exchange of energy calculated by GWE in the 5th "
708+ "sub-test does not match an externally calculated solution"
709+ )
658710 if name [- 1 ] == "n" :
659711 # no gw/sw convective exchange, simulates conductive exchange only
660712 assert in_bud_lst ["GWF" ] == 0.0 , msg1
@@ -663,8 +715,8 @@ def check_output(idx, test):
663715 # Determine gw/sfe temperature gradient direction
664716 if sfe_temps [0 , 0 , 0 , 0 ] > gw_temps [0 , 0 , 0 , 0 ]:
665717 # conduction will be from stream to gw
666- assert in_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
667- assert out_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
718+ assert in_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
719+ assert out_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
668720
669721 slp = trenddetector (
670722 np .arange (0 , sfe_temps .shape [- 1 ]), sfe_temps [0 , 0 , 0 , :]
@@ -675,8 +727,8 @@ def check_output(idx, test):
675727 assert slp > 0.0 , msg4
676728
677729 else :
678- assert in_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
679- assert out_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
730+ assert in_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
731+ assert out_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
680732
681733 # streamflow gain from aquifer ("into stream")
682734 if name [- 1 ] == "i" :
@@ -687,8 +739,8 @@ def check_output(idx, test):
687739 # Determine gw/sfe temperature gradient direction
688740 if sfe_temps [0 , 0 , 0 , 0 ] > gw_temps [0 , 0 , 0 , 0 ]:
689741 # conduction will be from stream to gw
690- assert in_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
691- assert out_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
742+ assert in_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
743+ assert out_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
692744
693745 slp = trenddetector (
694746 np .arange (0 , sfe_temps .shape [- 1 ]), sfe_temps [0 , 0 , 0 , :]
@@ -699,8 +751,8 @@ def check_output(idx, test):
699751 assert slp > 0.0 , msg4
700752
701753 else :
702- assert in_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
703- assert out_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
754+ assert in_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
755+ assert out_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
704756
705757 # streamflow loss to aquifer ("out of stream")
706758 if name [- 1 ] == "o" :
@@ -711,8 +763,8 @@ def check_output(idx, test):
711763 # Determine gw/sfe temperature gradient direction
712764 if sfe_temps [0 , 0 , 0 , 0 ] > gw_temps [0 , 0 , 0 , 0 ]:
713765 # conduction will be from stream to gw
714- assert in_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
715- assert out_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
766+ assert in_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
767+ assert out_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
716768
717769 slp = trenddetector (
718770 np .arange (0 , sfe_temps .shape [- 1 ]), sfe_temps [0 , 0 , 0 , :]
@@ -723,8 +775,8 @@ def check_output(idx, test):
723775 assert slp < 0.0 , msg4
724776
725777 else :
726- assert in_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
727- assert out_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
778+ assert in_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
779+ assert out_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
728780
729781 # Reverse temperature gradient (cold stream, warm aquifer)
730782 # Loss of streamwater to aquifer
@@ -737,12 +789,12 @@ def check_output(idx, test):
737789 # Determine gw/sfe temperature gradient direction
738790 if sfe_temps [0 , 0 , 0 , 0 ] > gw_temps [0 , 0 , 0 , 0 ]:
739791 # conduction will be from stream to gw
740- assert in_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
741- assert out_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
792+ assert in_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
793+ assert out_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
742794
743795 else :
744- assert in_bud_lst ["STREAMBED -COND" ] > 0.0 , msg2
745- assert out_bud_lst ["STREAMBED -COND" ] == 0.0 , msg2
796+ assert in_bud_lst ["STRMBD -COND" ] > 0.0 , msg2
797+ assert out_bud_lst ["STRMBD -COND" ] == 0.0 , msg2
746798
747799 slp = trenddetector (
748800 np .arange (0 , sfe_temps .shape [- 1 ]), sfe_temps [0 , 0 , 0 , :]
@@ -752,6 +804,31 @@ def check_output(idx, test):
752804 slp = trenddetector (np .arange (0 , gw_temps .shape [- 2 ]), gw_temps [0 , 0 , 1 , :])
753805 assert slp > 0.0 , msg4
754806
807+ if name [- 1 ] == "k" : # 'k' for known
808+ wa = shared_area [0 ][0 ]
809+ K_t_sb = K_therm_strmbed [0 ]
810+ sbthermthk = rbthcnd [idx ]
811+
812+ # final stream temperature, reach 1
813+ strm_temp = df .at [0 , "SFE-1-TEMP" ]
814+ gw_temp = gw_temps [0 , 0 , 1 , 0 ]
815+
816+ thermcond = wa * K_t_sb / sbthermthk * (gw_temp - strm_temp )
817+
818+ assert np .isclose (thermcond , df .at [0 , "SFE-STRMBD1" ], atol = 1e-4 ), (
819+ msg5
820+ + ". Values are thermcond: "
821+ + str (thermcond )
822+ + " GWE: "
823+ + str (df .at [0 , "SFE-STRMBD1" ])
824+ + " wa: "
825+ + str (wa )
826+ + " ktsb: "
827+ + str (K_t_sb )
828+ + " sbthk: "
829+ + str (sbthermthk )
830+ )
831+
755832
756833# - No need to change any code below
757834@pytest .mark .parametrize (
0 commit comments