Skip to content

Commit 0192258

Browse files
authored
fix(gwe-sfe): add support for strmbd-cond observation type in sfe (#2268)
* fix(gwe-sfe): add support for strmbd-cond observation type in sfe * remove unnecessary variables * Addressing review comments from @aprovost-usgs and noted in #2268 (review) * adding an external check of the streambed conduction amount to the autotest test_gwe_sfe_strmbedcond.py. Finishes addressing review comments noted in #2268 (review) * remove unused variables * attempt to guess CI failure. Was passing with local windows build * vector copy sizing * simplifications upon realizing xnew has the gw temperatures in addition to the sfe feature temperatures * remove unused variables
1 parent 1f44dca commit 0192258

File tree

3 files changed

+193
-87
lines changed

3 files changed

+193
-87
lines changed

autotest/test_gwe_sfe_strmbedcond.py

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,30 @@
1111

1212
import flopy
1313
import numpy as np
14+
import pandas as pd
1415
import pytest
1516
from 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

2527
k11 = 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"]
3032
surf_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):
200203
lhv = 2454000.0 # Latent heat of vaporization ($J/kg$)
201204
# Thermal conductivity of the streambed material ($W/m/C$)
202205
K_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
206209
steady = {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(

doc/ReleaseNotes/develop.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ description = "A new example was added demonstrating transient thermal loading o
2424
[[items]]
2525
section = "features"
2626
subsection = "internal"
27-
description = "Added interbed-compaction-pct observation to the CSUB package."
27+
description = "Added interbed-compaction-pct observation to the CSUB package."
28+
29+
[[items]]
30+
section = "fixes"
31+
description = "The mf6io.pdf guide for the SFE Package lists the availability of the STRMBD-COND observation type. However, the SFE Package did not actually support this observation type and if listed would cause the program to exit with error message. Functionality has been added to SFE for writing the amount of streambed conductive heat exchange to the CSV output file that contains the user-specified observations."

0 commit comments

Comments
 (0)