From f590f42fd53c58b10c25be63ce968d698e145b75 Mon Sep 17 00:00:00 2001 From: mjr-deltares <45555666+mjr-deltares@users.noreply.github.com> Date: Wed, 4 Dec 2024 09:57:16 +0100 Subject: [PATCH] ci(api): add autotest to test API routines (e.g. get_version) (#2082) * - add general libmf6 test * - unused module * - python format * - one more fprettify * - ruff --- autotest/test_gwf_libmf6_evt01.py | 2 +- autotest/test_gwf_libmf6_ghb01.py | 2 +- autotest/test_gwf_libmf6_ifmod01.py | 2 +- autotest/test_gwf_libmf6_ifmod02.py | 2 +- autotest/test_gwf_libmf6_ifmod03.py | 2 +- autotest/test_gwf_libmf6_rch01.py | 2 +- autotest/test_gwf_libmf6_rch02.py | 2 +- autotest/test_gwf_libmf6_riv01.py | 2 +- autotest/test_gwf_libmf6_riv02.py | 2 +- autotest/test_gwf_libmf6_sto01.py | 2 +- autotest/test_libmf6_api.py | 172 ++++++++++++++++++ autotest/test_prt_libmf6_budget.py | 2 +- .../Idm/mf6blockfile/StructVector.f90 | 1 - srcbmi/mf6xmi.F90 | 9 +- 14 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 autotest/test_libmf6_api.py diff --git a/autotest/test_gwf_libmf6_evt01.py b/autotest/test_gwf_libmf6_evt01.py index b515e811af3..440083c387a 100644 --- a/autotest/test_gwf_libmf6_evt01.py +++ b/autotest/test_gwf_libmf6_evt01.py @@ -156,7 +156,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_ghb01.py b/autotest/test_gwf_libmf6_ghb01.py index ecbf98cb0c2..29ba9389c44 100644 --- a/autotest/test_gwf_libmf6_ghb01.py +++ b/autotest/test_gwf_libmf6_ghb01.py @@ -188,7 +188,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_ifmod01.py b/autotest/test_gwf_libmf6_ifmod01.py index b55ccfab2bb..0f848a2bd7b 100644 --- a/autotest/test_gwf_libmf6_ifmod01.py +++ b/autotest/test_gwf_libmf6_ifmod01.py @@ -212,7 +212,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_ifmod02.py b/autotest/test_gwf_libmf6_ifmod02.py index f74948647bc..537a3094586 100644 --- a/autotest/test_gwf_libmf6_ifmod02.py +++ b/autotest/test_gwf_libmf6_ifmod02.py @@ -309,7 +309,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_ifmod03.py b/autotest/test_gwf_libmf6_ifmod03.py index da5105b5d0b..4a2fed241ac 100644 --- a/autotest/test_gwf_libmf6_ifmod03.py +++ b/autotest/test_gwf_libmf6_ifmod03.py @@ -219,7 +219,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_rch01.py b/autotest/test_gwf_libmf6_rch01.py index 239359e36bb..f49cdfe06f5 100644 --- a/autotest/test_gwf_libmf6_rch01.py +++ b/autotest/test_gwf_libmf6_rch01.py @@ -156,7 +156,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_rch02.py b/autotest/test_gwf_libmf6_rch02.py index b41763dcc53..540de179aab 100644 --- a/autotest/test_gwf_libmf6_rch02.py +++ b/autotest/test_gwf_libmf6_rch02.py @@ -196,7 +196,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_riv01.py b/autotest/test_gwf_libmf6_riv01.py index 303f99e0c6d..946206e6077 100644 --- a/autotest/test_gwf_libmf6_riv01.py +++ b/autotest/test_gwf_libmf6_riv01.py @@ -154,7 +154,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_riv02.py b/autotest/test_gwf_libmf6_riv02.py index 153863b00ee..390fad7fa19 100644 --- a/autotest/test_gwf_libmf6_riv02.py +++ b/autotest/test_gwf_libmf6_riv02.py @@ -170,7 +170,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_gwf_libmf6_sto01.py b/autotest/test_gwf_libmf6_sto01.py index 4756beef882..7c56a73da64 100644 --- a/autotest/test_gwf_libmf6_sto01.py +++ b/autotest/test_gwf_libmf6_sto01.py @@ -159,7 +159,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/autotest/test_libmf6_api.py b/autotest/test_libmf6_api.py new file mode 100644 index 00000000000..b6828ed9249 --- /dev/null +++ b/autotest/test_libmf6_api.py @@ -0,0 +1,172 @@ +""" +Test the MODFLOW API with a basic 1x1x5 model: + + [ BC | 1 | 2 | 3 | BC ] + +""" + +import os + +import flopy +import pytest +from framework import TestFramework +from modflow_devtools.markers import requires_pkg + +cases = ["libmf6_api"] + + +def get_model(dir): + # parameters and spd + # tdis + nper = 1 + tdis_rc = [] + for i in range(nper): + tdis_rc.append((1.0, 1, 1)) + + # solver data + nouter, ninner = 100, 300 + hclose, rclose = 10e-9, 1e-3 + + # model spatial discretization + nlay = 1 + nrow = 1 + ncol = 5 + + # cell spacing + delr = 1.0 + delc = 1.0 + + # top/bot of the aquifer + tops = [0.0, -1.0] + + # hydraulic conductivity + k11 = 1.0 + + # boundary stress period data + h_left = 0.0 + h_right = 5.0 + + # initial head + h_start = 0.0 + + sim = flopy.mf6.MFSimulation( + sim_name="sim", version="mf6", exe_name="mf6", sim_ws=dir + ) + + tdis = flopy.mf6.ModflowTdis(sim, time_units="DAYS", nper=nper, perioddata=tdis_rc) + + ims = flopy.mf6.ModflowIms( + sim, + print_option="SUMMARY", + outer_dvclose=hclose, + outer_maximum=nouter, + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="CG", + ) + + # submodel on the left: + chd_data = [[(0, 0, 0), h_left], [(0, 0, ncol - 1), h_right]] + chd_spd = {0: chd_data} + + gwf = flopy.mf6.ModflowGwf(sim, modelname="model", save_flows=True) + dis = flopy.mf6.ModflowGwfdis( + gwf, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=tops[0], + botm=tops[1:], + ) + ic = flopy.mf6.ModflowGwfic(gwf, strt=h_start) + npf = flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + save_flows=True, + icelltype=0, + k=k11, + ) + chd = flopy.mf6.ModflowGwfchd(gwf, stress_period_data=chd_spd) + oc = flopy.mf6.ModflowGwfoc( + gwf, + head_filerecord="model.hds", + budget_filerecord="model.cbc", + headprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")], + saverecord=[("HEAD", "LAST"), ("BUDGET", "LAST")], + ) + + return sim + + +def build_models(idx, test): + # build MODFLOW 6 files + ws = test.workspace + name = cases[idx] + sim = get_model(ws) + + # build comparison model + ws = os.path.join(test.workspace, "libmf6") + sim_compare = get_model(ws) + + return sim, sim_compare + + +def api_func(exe, idx, model_ws=None): + from modflowapi import ModflowApi + + if model_ws is None: + model_ws = "." + output_file_path = os.path.join(model_ws, "mfsim.stdout") + + try: + mf6 = ModflowApi(exe, working_directory=model_ws) + except Exception as e: + print("Failed to load " + str(exe)) + print("with message: " + str(e)) + return False, open(output_file_path).readlines() + + # initialize the model + try: + mf6.initialize() + except: + return False, open(output_file_path).readlines() + + # testing API + comp_str = mf6.get_component_name() + version_str = mf6.get_version() + print(f"Loaded {comp_str} with version {version_str}") + + # time loop + current_time = mf6.get_current_time() + end_time = mf6.get_end_time() + while current_time < end_time: + try: + mf6.update() + except: + return False, open(output_file_path).readlines() + current_time = mf6.get_current_time() + + # finish + try: + mf6.finalize() + except: + return False, open(output_file_path).readlines() + + # cleanup and return + return True, open(output_file_path).readlines() + + +@requires_pkg("modflowapi") +@pytest.mark.parametrize("idx, name", enumerate(cases)) +def test_mf6model(idx, name, function_tmpdir, targets): + test = TestFramework( + name=name, + workspace=function_tmpdir, + build=lambda t: build_models(idx, t), + targets=targets, + api_func=lambda exe, ws: api_func(exe, idx, ws), + ) + test.run() diff --git a/autotest/test_prt_libmf6_budget.py b/autotest/test_prt_libmf6_budget.py index f4796a5853b..0f02a665329 100644 --- a/autotest/test_prt_libmf6_budget.py +++ b/autotest/test_prt_libmf6_budget.py @@ -48,7 +48,7 @@ def api_func(exe, idx, model_ws=None): try: mf6 = ModflowApi(exe, working_directory=model_ws) except Exception as e: - print("Failed to load " + exe) + print("Failed to load " + str(exe)) print("with message: " + str(e)) return False, open(output_file_path).readlines() diff --git a/src/Utilities/Idm/mf6blockfile/StructVector.f90 b/src/Utilities/Idm/mf6blockfile/StructVector.f90 index c42a17268aa..191b144747f 100644 --- a/src/Utilities/Idm/mf6blockfile/StructVector.f90 +++ b/src/Utilities/Idm/mf6blockfile/StructVector.f90 @@ -13,7 +13,6 @@ module StructVectorModule use InputDefinitionModule, only: InputParamDefinitionType use CharacterStringModule, only: CharacterStringType use STLVecIntModule, only: STLVecInt - use ArrayHandlersModule, only: expandarray implicit none private diff --git a/srcbmi/mf6xmi.F90 b/srcbmi/mf6xmi.F90 index e3e870fa2c6..9bc3aff7793 100644 --- a/srcbmi/mf6xmi.F90 +++ b/srcbmi/mf6xmi.F90 @@ -349,15 +349,8 @@ function xmi_get_version(mf_version) result(bmi_status) & ! -- dummy variables character(kind=c_char), intent(inout) :: mf_version(BMI_LENVERSION) integer(kind=c_int) :: bmi_status !< BMI status code - ! -- local variables - character(len=BMI_LENVERSION) :: vstr - if (IDEVELOPMODE == 1) then - vstr = VERSIONNUMBER//'-dev' - else - vstr = VERSIONNUMBER - end if - mf_version = string_to_char_array(vstr, len_trim(vstr)) + mf_version = string_to_char_array(VERSIONNUMBER, len_trim(VERSIONNUMBER)) bmi_status = BMI_SUCCESS end function xmi_get_version