diff --git a/.github/workflows/build-publish_to_pypi.yml b/.github/workflows/build-publish_to_pypi.yml index d0fff460..6e04732d 100644 --- a/.github/workflows/build-publish_to_pypi.yml +++ b/.github/workflows/build-publish_to_pypi.yml @@ -8,7 +8,6 @@ on: - "src/supy_driver/**" - ".github/workflows/build-publish_to_pypi.yml" - "test/**" - - "pyproject.toml" pull_request: paths: # PR events containing matching files @@ -16,7 +15,7 @@ on: - "src/supy/**" - "src/supy_driver/**" - ".github/workflows/build-publish_to_pypi.yml" - - "pyproject.toml" + jobs: build_wheels: name: Build wheel for ${{ matrix.python }}-${{ matrix.buildplat[1] }} ${{ matrix.buildplat[2] }} @@ -29,31 +28,13 @@ jobs: - [macos-latest, macosx, arm64] - [windows-2019, win, AMD64] - python: ["cp39", "cp310", "cp311", "cp312", "cp313"] - # exclude: - # - buildplat: [macos-latest, macosx, arm64] - # python: ["cp38", "cp39"] + python: ["cp310", "cp311", "cp312"] fail-fast: false env: IS_32_BIT: ${{ matrix.buildplat[2] == 'x86' }} WHEEL_NAME: ${{ matrix.python }}-${{ matrix.buildplat[1] }}-${{ matrix.buildplat[2] }} - CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }}* - CIBW_ARCHS: ${{ matrix.buildplat[2] }} - CIBW_ENVIRONMENT_PASS_LINUX: RUNNER_OS - CIBW_BEFORE_ALL_MACOS: > - brew install gfortran && - brew unlink gfortran && - brew link gfortran - CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" # Use delvewheel on windows - CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}" - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND_MACOS: "python -m pytest '{project}/test'" - CIBW_TEST_COMMAND_LINUX: "python -m pytest '{project}/test'" - CIBW_TEST_COMMAND_WINDOWS: "python -m pytest {project}\\test" - CIBW_TEST_SKIP: "cp310-macosx_arm64" # skip test on arm64 on python 3.10 - cannot pass the water balance test for unknown reason even though it should be the same as x86_64 - MACOSX_DEPLOYMENT_TARGET: ${{ matrix.buildplat[0] == 'macos-13' && '13.0' || '14.0' }} steps: - uses: actions/checkout@v4 @@ -66,7 +47,18 @@ jobs: python-version: 3.12 - name: Build wheels - uses: pypa/cibuildwheel@v2.20 + uses: pypa/cibuildwheel@v2.16.5 + env: + CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }}* + CIBW_ARCHS: ${{ matrix.buildplat[2] }} + CIBW_ENVIRONMENT_PASS_LINUX: RUNNER_OS + CIBW_BEFORE_ALL_MACOS: "brew install gfortran && brew unlink gfortran && brew link gfortran" + CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" # Use delvewheel on windows + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}" + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND_MACOS: "python -m pytest '{project}/test'" + CIBW_TEST_COMMAND_LINUX: "python -m pytest '{project}/test'" + CIBW_TEST_COMMAND_WINDOWS: "python -m pytest {project}\\test" - name: Setup tmate session for debugging if: failure() diff --git a/.gitignore b/.gitignore index e32cf620..efba6899 100644 --- a/.gitignore +++ b/.gitignore @@ -407,16 +407,12 @@ save-AVL_6_vars273/ AVL_*_vars* schema/dev/df_state_test.pkl schema/df_state_test.pkl -schema/dev/df_state_test.pkl -schema/df_state_test.pkl error_info.md schema/dev/config-suews-output.yml schema/dev/convert-yml-df.ipynb file_list.txt data_met_raw.pkl test-quick.py -quick-test.ipynb data_met_raw.pkl test-quick.py df_water*.pkl -quick-test.ipynb diff --git a/pyproject.toml b/pyproject.toml index 4fa0ce52..cc4944e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "matplotlib", "chardet", "scipy", - "pydantic", # for yaml-based config + "pydantic", "f90wrap==0.2.15", # f90wrap is required for f2py-based supy driver "dask", # needs dask for parallel tasks "f90nml", # utility for namelist files @@ -46,7 +46,6 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", "Intended Audience :: Education", "Intended Audience :: Science/Research", "Operating System :: MacOS :: MacOS X", diff --git a/src/suews/src/suews_ctrl_const.f95 b/src/suews/src/suews_ctrl_const.f95 index 6414bfa7..10f2e3ec 100644 --- a/src/suews/src/suews_ctrl_const.f95 +++ b/src/suews/src/suews_ctrl_const.f95 @@ -88,7 +88,7 @@ MODULE allocateArray ncolumnsDataOutRSL = 30*4 + 5 + 13 + 2, & ncolumnsDataOutDebug = 1 + 5 + 103 + 14 + 5 + 4 + 3 + 1, & ncolumnsDataOutSPARTACUS = 5 + 7 + 4*15 + 3 + 6*15 + 2*15 + 4, & - ncolumnsDataOutSTEBBS = 5 + 57 + ncolumnsDataOutSTEBBS = 5 + 32 ! ---- Define input file headers --------------------------------------------------------------- CHARACTER(len=20), DIMENSION(ncolumnsSiteSelect) :: HeaderSiteSelect_File !Header for SiteSelect.txt diff --git a/src/suews/src/suews_ctrl_output.f95 b/src/suews/src/suews_ctrl_output.f95 index dbc68e97..bd265701 100644 --- a/src/suews/src/suews_ctrl_output.f95 +++ b/src/suews/src/suews_ctrl_output.f95 @@ -1189,62 +1189,37 @@ MODULE ctrl_output + ncolumnsDataOutSTEBBS - 5 & )/ & varAttr('ws', 'm s-1', f104, 'Wind speed', aA, 'STEBBS', 0), & - varAttr('Tair_sout', 'degC', f104, 'Air temperature', aA, 'STEBBS', 0), & - varAttr('Tsurf_sout', 'degC', f104, 'Surface temperature', aA, 'STEBBS', 0), & - varAttr('Kroof_sout', 'W m-2', f104, 'Shortwave radiation on roof', aA, 'STEBBS', 0), & - varAttr('Lroof_sout', 'W m-2', f104, 'Longwave radiation on roof', aA, 'STEBBS', 0), & - varAttr('Kwall_sout', 'W m-2', f104, 'Shortwave radiation on wall', aA, 'STEBBS', 0), & - varAttr('Lwall_sout', 'W m-2', f104, 'Longwave radiation on wall', aA, 'STEBBS', 0), & - varAttr('Tair_ind', 'degC', f104, 'Indoor air temperature', aA, 'STEBBS', 0), & - varAttr('Tindoormass', 'degC', f104, 'Indoor mass temperature', aA, 'STEBBS', 0), & - varAttr('Tintwallroof', 'degC', f104, 'Internal wall/roof temperature', aA, 'STEBBS', 0), & - varAttr('Textwallroof', 'degC', f104, 'External wall/roof temperature', aA, 'STEBBS', 0), & - varAttr('Tintwindow', 'degC', f104, 'Internal window temperature', aA, 'STEBBS', 0), & - varAttr('Textwindow', 'degC', f104, 'External window temperature', aA, 'STEBBS', 0), & - varAttr('Tintgrndflr', 'degC', f104, 'Internal ground floor temperature', aA, 'STEBBS', 0), & - varAttr('Textgrndflr', 'degC', f104, 'External ground floor temperature', aA, 'STEBBS', 0), & - varAttr('Qtotal_heat', 'W', f104, 'Total heating', aA, 'STEBBS', 0), & - varAttr('Qtotal_cool', 'W', f104, 'Total cooling', aA, 'STEBBS', 0), & - varAttr('Qsw_trans_win', 'W', f104, 'Shortwave transmitted through window', aA, 'STEBBS', 0), & - varAttr('sw_abs_win', 'W', f104, 'Shortwave absorbed by window', aA, 'STEBBS', 0), & - varAttr('Qsw_abs_wall', 'W', f104, 'Shortwave absorbed by wall/roof', aA, 'STEBBS', 0), & - varAttr('Qconv_indair', 'W', f104, 'Convective heat from indoor air to indoor mass', aA, 'STEBBS', 0), & - varAttr('Qlw_net_intwall', 'W', f104, 'Net longwave from internal wall/roof to other indoor surfaces', aA, 'STEBBS', 0), & - varAttr('lw_net_intwin', 'W', f104, 'Net longwave from internal window to other indoor surfaces', aA, 'STEBBS', 0), & - varAttr('lw_net_intgrnd', 'W', f104, 'Net longwave from internal ground floor to other indoor surfaces', aA, 'STEBBS', 0), & - varAttr('Q_appliance', 'W', f104, 'Appliance heat', aA, 'STEBBS', 0), & - varAttr('Q_ventilation', 'W', f104, 'Ventilation heat', aA, 'STEBBS', 0), & - varAttr('Qconv_indwall', 'W', f104, 'Convective heat from indoor air to internal wall/roof', aA, 'STEBBS', 0), & - varAttr('Qconv_indwin', 'W', f104, 'Convective heat from indoor air to internal window', aA, 'STEBBS', 0), & - varAttr('Qconv_indgrnd', 'W', f104, 'Convective heat from indoor air to internal ground floor', aA, 'STEBBS', 0), & - varAttr('Qloss_eff_heat', 'W', f104, 'Heat loss efficiency', aA, 'STEBBS', 0), & - varAttr('Qcond_wall', 'W', f104, 'Conductive heat through wall/roof', aA, 'STEBBS', 0), & - varAttr('Qcond_window', 'W', f104, 'Conductive heat through window', aA, 'STEBBS', 0), & - varAttr('Qcond_grndflr', 'W', f104, 'Conductive heat through ground floor', aA, 'STEBBS', 0), & - varAttr('Qcond_ground', 'W', f104, 'Conductive heat through ground', aA, 'STEBBS', 0), & - varAttr('Qlw_net_extwall', 'W', f104, 'Net longwave from external wall/roof to outdoor air', aA, 'STEBBS', 0), & - varAttr('Qlw_net_extwin', 'W', f104, 'Net longwave from external window to outdoor air', aA, 'STEBBS', 0), & - varAttr('Qconv_extwall', 'W', f104, 'Convective heat from external wall/roof to outdoor air', aA, 'STEBBS', 0), & - varAttr('Qconv_extwin', 'W', f104, 'Convective heat from external window to outdoor air', aA, 'STEBBS', 0), & - varAttr('q_cooling', 'W', f104, 'Cooling', aA, 'STEBBS', 0), & - varAttr('Qtotal_water', 'W', f104, 'Total water tank heat', aA, 'STEBBS', 0), & - varAttr('Qloss_drain', 'W', f104, 'Drain heat loss', aA, 'STEBBS', 0), & - varAttr('Twater_tank', 'degC', f104, 'Water tank temperature', aA, 'STEBBS', 0), & - varAttr('Tintwall_tank', 'degC', f104, 'Internal wall temperature of tank', aA, 'STEBBS', 0), & - varAttr('Textwall_tank', 'degC', f104, 'External wall temperature of tank', aA, 'STEBBS', 0), & - varAttr('Twater_vessel', 'degC', f104, 'Water vessel temperature', aA, 'STEBBS', 0), & - varAttr('Tintwall_vessel', 'degC', f104, 'Internal wall temperature of vessel', aA, 'STEBBS', 0), & - varAttr('Textwall_vessel', 'degC', f104, 'External wall temperature of vessel', aA, 'STEBBS', 0), & - varAttr('Vwater_vessel', 'm3', f104, 'Volume of water in vessel', aA, 'STEBBS', 0), & - varAttr('Awater_vessel', 'm2', f104, 'Area of water in vessel', aA, 'STEBBS', 0), & - varAttr('Vwall_vessel', 'm3', f104, 'Volume of wall in vessel', aA, 'STEBBS', 0), & - varAttr('qsensible', 'W', f104, 'Sensible heat', aA, 'STEBBS', 0), & - varAttr('qlatent', 'W', f104, 'Latent heat', aA, 'STEBBS', 0), & - varAttr('QS_total', 'W', f104, 'Storage heat flux', aA, 'STEBBS', 0), & - varAttr('QS_fabric', 'W', f104, 'Storage heat flux in fabric', aA, 'STEBBS', 0), & - varAttr('QS_air', 'W', f104, 'Storage heat flux in air', aA, 'STEBBS', 0), & - varAttr('Vwall_tank', 'm3', f104, 'Volume of wall in tank', aA, 'STEBBS', 0), & - varAttr('Vwater_tank', 'm3', f104, 'Volume of water in tank', aA, 'STEBBS', 0) & + varAttr('Tair', 'degC', f104, 'Air temperature', aA, 'STEBBS', 0), & + varAttr('Tsurf', 'degC', f104, 'Surface temperature', aA, 'STEBBS', 0), & + varAttr('Kroof', 'W m-2', f104, 'Downward shortwave radiation', aA, 'STEBBS', 0), & + varAttr('Lroof', 'W m-2', f104, 'Downward longwave radiation', aA, 'STEBBS', 0), & + varAttr('Kwall', 'W m-2', f104, 'Downward shortwave radiation', aA, 'STEBBS', 0), & + varAttr('Lwall', 'W m-2', f104, 'Downward longwave radiation', aA, 'STEBBS', 0), & + varAttr('Qheat_dom', 'W m-2', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qcool_dom', 'W m-2', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('dom_temp', 'degC', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('qfb_hw_dom', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('qfm_dom', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('qfb_dom_air', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qsw_transmitted_window', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qsw_absorbed_window', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qsw_absorbed_wallroof', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qcond_ground', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qlw_net_extwallroof_to_outair', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qlw_net_extwindow_to_outair', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qconv_extwallroof_to_outair', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Qconv_extwindow_to_outair', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('QStar', 'W m-2', f104, 'Net all-wave radiation', aA, 'STEBBS', 0), & + varAttr('QEC', 'W m-2', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('QH', 'W m-2', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('QS', 'W m-2', f104, '(TEMP)', aA, 'STEBBS', 0), & + varAttr('QBAE', 'W m-2', f104, '(TEMP)', aA, 'STEBBS', 0), & + varAttr('QWaste', 'W m-2', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Textwallroof', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Tintwallroof', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Textwindow', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Tintwindow', '', f104, 'TEMP', aA, 'STEBBS', 0), & + varAttr('Tair_ind', '', f104, 'TEMP', aA, 'STEBBS', 0) & / CONTAINS diff --git a/src/suews/src/suews_phys_atmmoiststab.f95 b/src/suews/src/suews_phys_atmmoiststab.f95 index c5c4a4bf..b443d4f6 100644 --- a/src/suews/src/suews_phys_atmmoiststab.f95 +++ b/src/suews/src/suews_phys_atmmoiststab.f95 @@ -1,7 +1,7 @@ MODULE AtmMoistStab_module USE SUEWS_DEF_DTS, ONLY: atm_state, SUEWS_FORCING, SUEWS_TIMER, SUEWS_STATE IMPLICIT NONE - REAL(KIND(1D0)), PARAMETER :: neut_limit = 1.E-2 !Limit for neutral stability + REAL(KIND(1D0)), PARAMETER :: neut_limit = 1.E-4 !Limit for neutral stability REAL(KIND(1D0)), PARAMETER :: k = 0.4 !Von Karman's contant REAL(KIND(1D0)), PARAMETER :: grav = 9.80665 !g - gravity - physics today august 1987 diff --git a/src/suews/src/suews_phys_rslprof.f95 b/src/suews/src/suews_phys_rslprof.f95 index 3fabf6c7..f370c4b8 100644 --- a/src/suews/src/suews_phys_rslprof.f95 +++ b/src/suews/src/suews_phys_rslprof.f95 @@ -1062,9 +1062,11 @@ SUBROUTINE RSLProfile( & ! Step 0: Calculate grid-cell dependent constants and Beta (crucial for H&F method) CALL RSL_cal_prms( & StabilityMethod, & !input - nz_above, zarray(nz_can + 1:nz), & !input + !nz_above, zarray(nz_can + 1:nz), & !input + nz_above + 1, zarray(nz_can:nz), & !input zh, L_MOD, sfr_surf, FAI, PAI, & !input - psihatm_z(nz_can + 1:nz), psihath_z(nz_can + 1:nz), & !output + !psihatm_z(nz_can + 1:nz), psihath_z(nz_can + 1:nz), & !output + psihatm_z(nz_can:nz), psihath_z(nz_can:nz), & !output zH_RSL, L_MOD_RSL, & ! output Lc, beta, zd_RSL, z0_RSL, elm, Scc, fx) diff --git a/src/suews/src/suews_phys_stebbs.f95 b/src/suews/src/suews_phys_stebbs.f95 index 8da96221..f3371587 100644 --- a/src/suews/src/suews_phys_stebbs.f95 +++ b/src/suews/src/suews_phys_stebbs.f95 @@ -650,64 +650,12 @@ SUBROUTINE stebbs_cal_main( & REAL(rprc) :: Kwall_sout, Lwall_sout ! REAL(rprc) :: Tsurf_sout - ! Output variables - REAL(rprc) :: ws - REAL(rprc) :: Tair_sout - ! REAL(rprc) :: Tsurf_sout - REAL(rprc) :: Kroof_sout - REAL(rprc) :: Lroof_sout - ! REAL(rprc) :: Kwall_sout - ! REAL(rprc) :: Lwall_sout - REAL(rprc) :: Tair_ind - REAL(rprc) :: Tindoormass - REAL(rprc) :: Tintwallroof - REAL(rprc) :: Textwallroof - REAL(rprc) :: Tintwindow - REAL(rprc) :: Textwindow - REAL(rprc) :: Tintgroundfloor - REAL(rprc) :: Textgroundfloor - REAL(rprc) :: Qtotal_heating - REAL(rprc) :: Qtotal_cooling - REAL(rprc) :: Qsw_transmitted_window_tstepTotal - REAL(rprc) :: sw_absorbed_window_tstepTotal - REAL(rprc) :: Qsw_absorbed_wallroof_tstepTotal - REAL(rprc) :: Qconv_indair_to_indoormass_tstepTotal - REAL(rprc) :: Qlw_net_intwallroof_to_allotherindoorsurfaces_tstepTotal - REAL(rprc) :: lw_net_intwindow_to_allotherindoorsurfaces_tstepTotal - REAL(rprc) :: lw_net_intgroundfloor_to_allotherindoorsurfaces_tstepTotal - REAL(rprc) :: Q_appliance_tstepTotal - REAL(rprc) :: Q_ventilation_tstepTotal - REAL(rprc) :: Qconv_indair_to_intwallroof_tstepTotal - REAL(rprc) :: Qconv_indair_to_intwindow_tstepTotal - REAL(rprc) :: Qconv_indair_to_intgroundfloor_tstepTotal - REAL(rprc) :: Qloss_efficiency_heating_air_tstepTotal - REAL(rprc) :: Qcond_wallroof_tstepTotal - REAL(rprc) :: Qcond_window_tstepTotal - REAL(rprc) :: Qcond_groundfloor_tstepTotal - REAL(rprc) :: Qcond_ground_tstepTotal - REAL(rprc) :: Qlw_net_extwallroof_to_outair_tstepTotal - REAL(rprc) :: Qlw_net_extwindow_to_outair_tstepTotal - REAL(rprc) :: Qconv_extwallroof_to_outair_tstepTotal - REAL(rprc) :: Qconv_extwindow_to_outair_tstepTotal - REAL(rprc) :: q_cooling_timestepTotal - REAL(rprc) :: Qtotal_water_tank - REAL(rprc) :: Qloss_drain - REAL(rprc) :: Twater_tank - REAL(rprc) :: Tintwall_tank - REAL(rprc) :: Textwall_tank - REAL(rprc) :: Twater_vessel - REAL(rprc) :: Tintwall_vessel - REAL(rprc) :: Textwall_vessel - REAL(rprc) :: Vwater_vessel - REAL(rprc) :: Awater_vessel - REAL(rprc) :: Vwall_vessel - REAL(rprc) :: qsensible_timestepTotal - REAL(rprc) :: qlatent_timestepTotal - REAL(rprc) :: QS_tstepTotal - REAL(rprc) :: QS_fabric_tstepTotal - REAL(rprc) :: QS_air_tstepTotal - REAL(rprc) :: Vwall_tank - REAL(rprc) :: Vwater_tank + REAL(rprc) :: qheat_dom, qcool_dom, dom_temp, qfb_hw_dom, qfm_dom, qfb_dom_air, & + Qsw_transmitted_window, Qsw_absorbed_window, Qsw_absorbed_wallroof, & + Qcond_ground, Qlw_net_extwallroof_to_outair, Qlw_net_extwindow_to_outair, & + Qconv_extwallroof_to_outair, Qconv_extwindow_to_outair, & + QStar, QEC, QH, QS, QBAE, QWaste, & + Textwallroof, Tintwallroof, Textwindow, Tintwindow, Tair_ind ASSOCIATE ( & timestep => timer%tstep, & @@ -960,27 +908,14 @@ SUBROUTINE stebbs_cal_main( & ! END DO flginit = 1 - dataOutLineSTEBBS = [ & - ! Forcing - ws, Tair_sout, Tsurf_sout, Kroof_sout, Lroof_sout, Kwall_sout, Lwall_sout, & - ! Temperatures - Tair_ind, Tindoormass, Tintwallroof, Textwallroof, Tintwindow, Textwindow, Tintgroundfloor, & - Textgroundfloor, Qtotal_heating, Qtotal_cooling, Qsw_transmitted_window_tstepTotal, & - sw_absorbed_window_tstepTotal, Qsw_absorbed_wallroof_tstepTotal, Qconv_indair_to_indoormass_tstepTotal, & - Qlw_net_intwallroof_to_allotherindoorsurfaces_tstepTotal, & - lw_net_intwindow_to_allotherindoorsurfaces_tstepTotal, & - lw_net_intgroundfloor_to_allotherindoorsurfaces_tstepTotal, Q_appliance_tstepTotal, & - Q_ventilation_tstepTotal, Qconv_indair_to_intwallroof_tstepTotal, Qconv_indair_to_intwindow_tstepTotal, & - Qconv_indair_to_intgroundfloor_tstepTotal, Qloss_efficiency_heating_air_tstepTotal, & - Qcond_wallroof_tstepTotal, Qcond_window_tstepTotal, Qcond_groundfloor_tstepTotal, & - Qcond_ground_tstepTotal, Qlw_net_extwallroof_to_outair_tstepTotal, & - Qlw_net_extwindow_to_outair_tstepTotal, Qconv_extwallroof_to_outair_tstepTotal, & - Qconv_extwindow_to_outair_tstepTotal, q_cooling_timestepTotal, Qtotal_water_tank, Qloss_drain, & - Twater_tank, Tintwall_tank, Textwall_tank, Twater_vessel, Tintwall_vessel, Textwall_vessel, & - Vwater_vessel, Awater_vessel, Vwall_vessel, qsensible_timestepTotal, qlatent_timestepTotal, & - QS_tstepTotal, QS_fabric_tstepTotal, QS_air_tstepTotal, & - Vwall_tank, Vwater_tank & - ] + dataOutLineSTEBBS = [ws, Tair_sout, Tsurf_sout, Kroof_sout, Lroof_sout, Kwall_sout, Lwall_sout, & + qheat_dom, qcool_dom, dom_temp, qfb_hw_dom, qfm_dom, qfb_dom_air, & + Qsw_transmitted_window, Qsw_absorbed_window, Qsw_absorbed_wallroof, & + Qcond_ground, Qlw_net_extwallroof_to_outair, Qlw_net_extwindow_to_outair, & + Qconv_extwallroof_to_outair, Qconv_extwindow_to_outair, & + QStar, QEC, QH, QS, QBAE, QWaste, & + Textwallroof, Tintwallroof, Textwindow, Tintwindow, Tair_ind & + ] RETURN END ASSOCIATE END ASSOCIATE @@ -1079,56 +1014,14 @@ SUBROUTINE stebbs_cal(self, flginit, datetimeLine, & REAL(rprc) :: Area, qinternal, qe_cool, qe_heat, q_waste, q_ventilation ! Output variables with INTENT(OUT) - REAL(rprc), INTENT(OUT) :: Tair_ind - REAL(rprc), INTENT(OUT) :: Tindoormass - REAL(rprc), INTENT(OUT) :: Tintwallroof - REAL(rprc), INTENT(OUT) :: Textwallroof - REAL(rprc), INTENT(OUT) :: Tintwindow - REAL(rprc), INTENT(OUT) :: Textwindow - REAL(rprc), INTENT(OUT) :: Tintgroundfloor - REAL(rprc), INTENT(OUT) :: Textgroundfloor - REAL(rprc), INTENT(OUT) :: Qtotal_heating - REAL(rprc), INTENT(OUT) :: Qtotal_cooling - REAL(rprc), INTENT(OUT) :: Qsw_transmitted_window_tstepTotal - REAL(rprc), INTENT(OUT) :: Qsw_absorbed_window_tstepTotal - REAL(rprc), INTENT(OUT) :: Qsw_absorbed_wallroof_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_indair_to_indoormass_tstepTotal - REAL(rprc), INTENT(OUT) :: Qlw_net_intwallroof_to_allotherindoorsurfaces_tstepTotal - REAL(rprc), INTENT(OUT) :: Qlw_net_intwindow_to_allotherindoorsurfaces_tstepTotal - REAL(rprc), INTENT(OUT) :: Qlw_net_intgroundfloor_to_allotherindoorsurfaces_tstepTotal - REAL(rprc), INTENT(OUT) :: Q_appliance_tstepTotal - REAL(rprc), INTENT(OUT) :: Q_ventilation_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_indair_to_intwallroof_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_indair_to_intwindow_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_indair_to_intgroundfloor_tstepTotal - REAL(rprc), INTENT(OUT) :: Qloss_efficiency_heating_air_tstepTotal - REAL(rprc), INTENT(OUT) :: Qcond_wallroof_tstepTotal - REAL(rprc), INTENT(OUT) :: Qcond_window_tstepTotal - REAL(rprc), INTENT(OUT) :: Qcond_groundfloor_tstepTotal - REAL(rprc), INTENT(OUT) :: Qcond_ground_tstepTotal - REAL(rprc), INTENT(OUT) :: Qlw_net_extwallroof_to_outair_tstepTotal - REAL(rprc), INTENT(OUT) :: Qlw_net_extwindow_to_outair_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_extwallroof_to_outair_tstepTotal - REAL(rprc), INTENT(OUT) :: Qconv_extwindow_to_outair_tstepTotal - REAL(rprc), INTENT(OUT) :: q_cooling_timestepTotal - REAL(rprc), INTENT(OUT) :: Qtotal_water_tank - REAL(rprc), INTENT(OUT) :: Qloss_drain - REAL(rprc), INTENT(OUT) :: Twater_tank - REAL(rprc), INTENT(OUT) :: Tintwall_tank - REAL(rprc), INTENT(OUT) :: Textwall_tank - REAL(rprc), INTENT(OUT) :: Twater_vessel - REAL(rprc), INTENT(OUT) :: Tintwall_vessel - REAL(rprc), INTENT(OUT) :: Textwall_vessel - REAL(rprc), INTENT(OUT) :: Vwater_vessel - REAL(rprc), INTENT(OUT) :: Awater_vessel - REAL(rprc), INTENT(OUT) :: Vwall_vessel - REAL(rprc), INTENT(OUT) :: qsensible_timestepTotal - REAL(rprc), INTENT(OUT) :: qlatent_timestepTotal - REAL(rprc), INTENT(OUT) :: QS_tstepTotal - REAL(rprc), INTENT(OUT) :: QS_fabric_tstepTotal - REAL(rprc), INTENT(OUT) :: QS_air_tstepTotal - REAL(rprc), INTENT(OUT) :: Vwall_tank - REAL(rprc), INTENT(OUT) :: Vwater_tank + REAL(rprc), INTENT(OUT) :: qfm_dom, qheat_dom, qcool_dom, qfb_hw_dom, qfb_dom_air, & + dom_temp, Qsw_transmitted_window, Qsw_absorbed_window, & + Qsw_absorbed_wallroof, Qlw_net_extwallroof_to_outair, & + Qlw_net_extwindow_to_outair, QStar, QEC, & + Qconv_extwindow_to_outair, Qconv_extwallroof_to_outair, & + QH, QS, Qcond_ground, QBAE, QWaste, & + Textwallroof, Tintwallroof, Textwindow, Tintwindow, & + Tair_ind ! Other declarations REAL(rprc), DIMENSION(6) :: bem_qf_1 @@ -1208,18 +1101,18 @@ SUBROUTINE stebbs_cal(self, flginit, datetimeLine, & Tintwindow = self%Tintwindow ! # internal surface temperature of window [K] Tair_ind = self%Tair_ind ! # Indoor air temperature [K] END DO - ! self%qfm_dom = qfm_dom - ! self%qheat_dom = qheat_dom - ! self%qcool_dom = qcool_dom - ! self%qfb_hw_dom = qfb_hw_dom - ! self%dom_temp = dom_temp - ! self%QStar = QStar - ! self%QEC = QEC - ! self%QH = QH - ! self%QS = QS - ! self%QBAE = QBAE - ! self%QWaste = QWaste - ! self%flginit = 1 + self%qfm_dom = qfm_dom + self%qheat_dom = qheat_dom + self%qcool_dom = qcool_dom + self%qfb_hw_dom = qfb_hw_dom + self%dom_temp = dom_temp + self%QStar = QStar + self%QEC = QEC + self%QH = QH + self%QS = QS + self%QBAE = QBAE + self%QWaste = QWaste + self%flginit = 1 RETURN END SUBROUTINE stebbs_cal @@ -1584,12 +1477,9 @@ SUBROUTINE tstep( & QS_tstepTotal = 0.0 QS_fabric_tstepTotal = 0.0 QS_air_tstepTotal = 0.0 - - ! Used to recalculate Area of DHW in use IF (Awater_vessel > 0.0) THEN VARatio_water_vessel = Vwater_vessel/Awater_vessel END IF - IF (MOD(timestep, resolution) == 0) THEN looptime: DO i = 1, INT(timestep/resolution), 1 Qsw_transmitted_window = windowInsolation(Qsw_dn_extwall, winT, Awindow) diff --git a/src/suews/src/suews_phys_waterdist.f95 b/src/suews/src/suews_phys_waterdist.f95 index 4f2c4f77..37b760f8 100644 --- a/src/suews/src/suews_phys_waterdist.f95 +++ b/src/suews/src/suews_phys_waterdist.f95 @@ -809,6 +809,7 @@ SUBROUTINE ReDistributeWater( & AddWater(i_receiver) = AddWater(i_receiver) & + (Drain(i_contributor)*sfr_surf(i_contributor) & /sfr_surf(i_receiver))*WaterDist(i_receiver, i_contributor) !Original + ELSE !Snow included, This needs to be fixed at some point. LJ Mar 2013 AddWaterRunoff(i_contributor) = AddWaterRunoff(i_contributor) & @@ -816,9 +817,9 @@ SUBROUTINE ReDistributeWater( & END IF ELSE - !If no receiving surface exists, water fraction goes to AddWaterRunoff AddWaterRunoff(i_contributor) = AddWaterRunoff(i_contributor) & - + WaterDist(i_receiver, i_contributor) + + WaterDist(i_receiver, i_contributor) !If no receiving surface exists, + !water fraction goes to AddWaterRunoff END IF END DO END DO diff --git a/src/supy/__init__.py b/src/supy/__init__.py index 2eef11b3..9ecae90a 100644 --- a/src/supy/__init__.py +++ b/src/supy/__init__.py @@ -19,12 +19,10 @@ load_SampleData, load_sample_data, load_forcing_grid, - load_config_from_df, run_supy, save_supy, check_forcing, check_state, - init_config, ) diff --git a/src/supy/_run.py b/src/supy/_run.py index c0e0a3e5..5bfd896d 100644 --- a/src/supy/_run.py +++ b/src/supy/_run.py @@ -1,4 +1,3 @@ -from ast import literal_eval from shutil import rmtree import tempfile import copy @@ -9,7 +8,6 @@ # import logging import traceback -from ast import literal_eval from pathlib import Path from typing import Tuple import pandas @@ -564,12 +562,6 @@ def run_supy_par(df_forcing_tstep, df_state_init_m, save_state, chunk_day, debug # main calculation end here ############################################################################## -# pack one Series of var into np.array -def pack_var_old(ser_var): - dim = np.array(literal_eval(ser_var.index[-1])) + 1 - val = np.array(ser_var.values.reshape(dim), order="F").astype(float) - return val - # pack one Series of var into np.array def pack_var(ser_var: pd.Series) -> np.ndarray: @@ -608,9 +600,8 @@ def pack_var(ser_var: pd.Series) -> np.ndarray: # Add 1 since indices are 0-based dimensions = np.array(ser_var_indexed.index[-1]) + 1 - # Reshape - NO need to use Fortran-style ordering - # res = np.array(ser_var_indexed.values).reshape(dimensions, order="F") - res = np.array(ser_var_indexed.values).reshape(dimensions) + # Reshape using Fortran-style ordering to match original + res = np.array(ser_var_indexed.values).reshape(dimensions, order="F") try: return res.astype(float) @@ -633,25 +624,7 @@ def pack_grid_dict(ser_grid): dict_var = {} for var in list_var: if var not in ["file_init"]: - # print(f"var: {var}") - # val_packed = pack_var(ser_grid[var]) - # val_packed_old = pack_var_old(ser_grid[var]) - # # Test if the old and new packed values are different - # if not np.array_equal(val_packed_old, val_packed): - # # Save the input Series as a pickle file for debugging - # ser_grid.to_pickle("ser_grid_debug.pkl") - # # Stop execution - # raise ValueError(f"Packed values for variable '{var}' are different between old and new methods.") - - # dict_var[var] = pack_var(ser_grid[var]) - # dict_var[var] = pack_var_old(ser_grid[var]) - try: - # dict_var[var] = pack_var(ser_grid[var]) - dict_var[var] = pack_var_old(ser_grid[var]) - except Exception as e: - print(f"Error packing variable '{var}': {e}") - # dict_var[var] = pack_var_old(ser_grid[var]) - dict_var[var] = pack_var(ser_grid[var]) + dict_var[var] = pack_var(ser_grid[var]) else: pass # dict_var = { diff --git a/src/supy/_supy_module.py b/src/supy/_supy_module.py index ef2a4b1b..4ba80330 100644 --- a/src/supy/_supy_module.py +++ b/src/supy/_supy_module.py @@ -325,8 +325,7 @@ def load_sample_data() -> Tuple[pandas.DataFrame, pandas.DataFrame]: """ trv_sample_data = trv_supy_module / "sample_run" - # path_config_default = trv_sample_data / "defaultConfig.yml" - path_config_default = trv_sample_data / "RunControl.nml" # TODO: to be deprecated - but keep for now to pass tests + path_config_default = trv_sample_data / "defaultConfig.yml" df_state_init = init_supy(path_config_default, force_reload=False) df_forcing = load_forcing_grid( path_config_default, df_state_init.index[0], df_state_init=df_state_init @@ -334,44 +333,6 @@ def load_sample_data() -> Tuple[pandas.DataFrame, pandas.DataFrame]: return df_state_init, df_forcing -def load_config_from_df(df_state: pd.DataFrame): - """Load SUEWS configuration from `df_state`. - - Parameters - ---------- - df_state : pd.DataFrame - DataFrame of model states. - - Returns - ------- - config : SUEWSConfig - SUEWS configuration. - - Examples - -------- - >>> df_state_init, df_forcing = supy.load_sample_data() - >>> config = supy.load_config_from_df(df_state_init) - - """ - - from .util._config import SUEWSConfig - - config = SUEWSConfig.from_df_state(df_state) - - return config - -def init_config(df_state: pd.DataFrame=None): - """ - Initialise SUEWS configuration object either from existing df_state dataframe or as the default configuration. - """ - - if df_state is None: - from .util._config import SUEWSConfig - return SUEWSConfig() - - return load_config_from_df(df_state) - - # input processing code end here ############################################################################## diff --git a/src/supy/util/__init__.py b/src/supy/util/__init__.py index 978e327d..3bcee45b 100644 --- a/src/supy/util/__init__.py +++ b/src/supy/util/__init__.py @@ -56,5 +56,3 @@ from ._debug import diag_rsl, diag_rsl_prm, save_zip_debug from ._spinup import get_spinup_state - -from ._config import init_config_from_yaml \ No newline at end of file diff --git a/src/supy/util/_config.py b/src/supy/util/_config.py index 010b1c6e..0d6f46ba 100644 --- a/src/supy/util/_config.py +++ b/src/supy/util/_config.py @@ -1692,13 +1692,6 @@ def to_df_state(self, grid_id: int) -> pd.DataFrame: df_base = df_base.sort_index(axis=1) return df_base - - @classmethod - def from_df_state(cls, df: pd.DataFrame, grid_id: int, surf_idx: int) -> "NonVegetatedSurfaceProperties": - """Reconstruct non-vegetated surface properties from DataFrame state format.""" - instance = super().from_df_state(df, grid_id, surf_idx) - instance.alb = ValueWithDOI(df.loc[grid_id, ("alb", f"({surf_idx},)")]) - return instance class PavedProperties(NonVegetatedSurfaceProperties): # May need to move VWD for waterdist to here for referencing @@ -2055,17 +2048,17 @@ class BldgsProperties(NonVegetatedSurfaceProperties): # May need to move VWD for ref: Optional[Reference] = None - # @model_validator(mode="after") # This is no longer appropriate - may be reintroduced and altered - # def validate_rsl_zd_range(self) -> "BldgsProperties": - # sfr_bldg_lower_limit = 0.18 - # if self.sfr < sfr_bldg_lower_limit: - # if self.faibldg.value < 0.25 * (1 - self.sfr.value): - # raise ValueError( # This should be a warning not raising an error - # "Frontal Area Index (FAI) is below a lower limit of: 0.25 * (1 - PAI), which is likely to cause a negative displacement height (zd) in the RSL.\n" - # f"\tYou have entered a building FAI of {self.faibldg} and a building PAI of {self.sfr}.\n" - # "\tFor more details, please refer to: https://github.com/UMEP-dev/SUEWS/issues/302" - # ) - # return self + @model_validator(mode="after") + def validate_rsl_zd_range(self) -> "BldgsProperties": + sfr_bldg_lower_limit = 0.18 + if self.sfr < sfr_bldg_lower_limit: + if self.faibldg.value < 0.25 * (1 - self.sfr.value): + raise ValueError( + "Frontal Area Index (FAI) is below a lower limit of: 0.25 * (1 - PAI), which is likely to cause a negative displacement height (zd) in the RSL.\n" + f"\tYou have entered a building FAI of {self.faibldg} and a building PAI of {self.sfr}.\n" + "\tFor more details, please refer to: https://github.com/UMEP-dev/SUEWS/issues/302" + ) + return self def to_df_state(self, grid_id: int) -> pd.DataFrame: """Convert building properties to DataFrame state format.""" @@ -2082,8 +2075,6 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "BldgsProperties": """Reconstruct building properties from DataFrame state format.""" surf_idx = 1 instance = super().from_df_state(df, grid_id, surf_idx) - instance.bldgh = ValueWithDOI(df.loc[grid_id, ("bldgh", "0")]) - instance.faibldg = ValueWithDOI(df.loc[grid_id, ("faibldg", "0")]) return instance @@ -2137,7 +2128,6 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "WaterProperties": """Reconstruct water properties from DataFrame state format.""" surf_idx = 6 instance = super().from_df_state(df, grid_id, surf_idx) - instance.flowchange = ValueWithDOI(df.loc[grid_id, ("flowchange", f"0")]) return instance @@ -2162,15 +2152,6 @@ class ModelControl(BaseModel): ref: Optional[Reference] = None - @field_validator("tstep", "diagnose", mode="after") - def validate_int_float(cls, v): - if isinstance(v, (np.float64, np.float32)): - return float(v) - elif isinstance(v, (np.int64, np.int32)): - return int(v) - return v - - def to_df_state(self, grid_id: int) -> pd.DataFrame: """Convert model control properties to DataFrame state format.""" df_state = init_df_state(grid_id) @@ -2193,8 +2174,7 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "ModelControl": """Reconstruct model control properties from DataFrame state format.""" instance = cls() for attr in ["tstep", "diagnose"]: - value = df.loc[grid_id, (attr, "0")] - setattr(instance, attr, int(value) if isinstance(value, (np.int64, np.int32)) else value) + setattr(instance, attr, df.loc[grid_id, (attr, "0")]) return instance @@ -4197,13 +4177,12 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "ArchetypeProperties": """Reconstruct ArchetypeProperties from DataFrame state format.""" # Extract the values from the DataFrame params = { - field_name: df.loc[grid_id, (field_name.lower(), "0")] + field_name: df.loc[grid_id, (field_name, "0")] for field_name in cls.model_fields.keys() if field_name != "ref" } # Convert params to ValueWithDOI - non_value_with_doi = ["BuildingType", "BuildingName"] - params = {key: ValueWithDOI(value) for key, value in params.items() if key not in non_value_with_doi} + params = {key: ValueWithDOI(value) for key, value in params.items()} # Create an instance using the extracted parameters return cls(**params) @@ -4455,7 +4434,7 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "StebbsProperties": """Reconstruct StebbsProperties from DataFrame state format.""" # Extract the values from the DataFrame params = { - field_name: df.loc[grid_id, (field_name.lower(), "0")] + field_name: df.loc[grid_id, (field_name, "0")] for field_name in cls.model_fields.keys() if field_name != "ref" } @@ -4635,9 +4614,6 @@ def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "SiteProperties": params["land_cover"] = LandCover.from_df_state(df, grid_id) params["vertical_layers"] = VerticalLayers.from_df_state(df, grid_id) - params["stebbs"] = StebbsProperties.from_df_state(df, grid_id) - params["building_archetype"] = ArchetypeProperties.from_df_state(df, grid_id) - return cls(**params) @@ -4675,17 +4651,6 @@ class Model(BaseModel): description="Model physics parameters including surface properties, coefficients, etc.", ) - @model_validator(mode="after") - def validate_radiation_method(self) -> "Model": - if self.physics.netradiationmethod.value == 1 and self.control.forcing_file.value == "forcing.txt": - raise ValueError( - "NetRadiationMethod is set to 1 (using observed Ldown). " - "The sample forcing file lacks observed Ldown. Use netradiation = 3 for sample forcing. " - "If not using sample forcing, ensure that the forcing file contains Ldown and rename from forcing.txt." - # TODO: This is a temporary solution. We need to provide a better way to catch this. - ) - return self - def to_df_state(self, grid_id: int) -> pd.DataFrame: """Convert model to DataFrame state format""" df_state = init_df_state(grid_id) @@ -4693,16 +4658,6 @@ def to_df_state(self, grid_id: int) -> pd.DataFrame: df_physics = self.physics.to_df_state(grid_id) df_state = pd.concat([df_state, df_control, df_physics], axis=1) return df_state - - @classmethod - def from_df_state(cls, df: pd.DataFrame, grid_id: int) -> "Model": - """Reconstruct Model from DataFrame state format.""" - # Extract control and physics parameters - control = ModelControl.from_df_state(df, grid_id) - physics = ModelPhysics.from_df_state(df, grid_id) - - # Create an instance using the extracted parameters - return cls(control=control, physics=physics) class SUEWSConfig(BaseModel): @@ -4832,18 +4787,18 @@ def from_df_state(cls, df: pd.DataFrame) -> "SUEWSConfig": config.site = sites # Reconstruct model - config.model = Model.from_df_state(df, grid_ids[0]) - # for grid_id in grid_ids: - # # Set model control - # model_control = ModelControl.from_df_state(df, grid_id) - # model.control = model_control - - # # Set model physics - # model_physics = ModelPhysics.from_df_state(df, grid_id) - # model.physics = model_physics - # break # Only need one as model is shared across sites - - # config.model = model + model = Model() + for grid_id in grid_ids: + # Set model control + model_control = ModelControl.from_df_state(df, grid_id) + model.control = model_control + + # Set model physics + model_physics = ModelPhysics.from_df_state(df, grid_id) + model.physics = model_physics + break # Only need one as model is shared across sites + + config.model = model return config