From dedb2f855cd0516d21d7d7c356f24525673214cd Mon Sep 17 00:00:00 2001 From: Sunny Titus Date: Wed, 13 May 2026 16:51:36 +0200 Subject: [PATCH] Test fypp to generate templated containers --- .github/common/check_vfproj.py | 5 +- .gitignore | 1 + meson.build | 3 + msvs/mf6core.vfproj | 13 +-- pixi.lock | 26 ++++++ pixi.toml | 1 + src/Utilities/STLContainers/STLVecDbl.fypp | 4 + src/Utilities/STLContainers/STLVecInt.fypp | 5 ++ src/Utilities/STLContainers/meson.build | 20 +++++ .../STLVecInt.f90 => include/STLVector.fypp} | 81 ++++++++++--------- src/meson.build | 5 +- 11 files changed, 119 insertions(+), 45 deletions(-) create mode 100644 src/Utilities/STLContainers/STLVecDbl.fypp create mode 100644 src/Utilities/STLContainers/STLVecInt.fypp create mode 100644 src/Utilities/STLContainers/meson.build rename src/{Utilities/STLVecInt.f90 => include/STLVector.fypp} (75%) diff --git a/.github/common/check_vfproj.py b/.github/common/check_vfproj.py index 6c1e8eade7c..2c6ad55437e 100644 --- a/.github/common/check_vfproj.py +++ b/.github/common/check_vfproj.py @@ -13,7 +13,8 @@ def get_source_files(src_path, verbose=False): extensions = ("*.[fF]", "*.[fF]9[05]", "*.inc") for ext in extensions: for path in src_path.glob(f"**/{ext}"): - yield path.absolute() + if "generated" not in path.parts: + yield path.absolute() def get_extra_files(extrafiles_path, src_path, extra_path, verbose=False): @@ -36,6 +37,8 @@ def get_msvs_files(vfproj_path, src_path, extra_path=None, verbose=False): root = tree.getroot() for f in root.iter("File"): path = f.attrib["RelativePath"].replace("\\", "/") + if "generated" in path.split("/"): + continue yield ( ( extra_path diff --git a/.gitignore b/.gitignore index 3c96e75bc6e..12861fb13a2 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ dist mod_temp/ obj_temp/ +msvs/generated/ src_temp/ pymake/dependencies/ pymake/dependencies_std/ diff --git a/meson.build b/meson.build index 6e4cd95d31d..22a610d6012 100644 --- a/meson.build +++ b/meson.build @@ -285,6 +285,9 @@ if is_parallel_build and build_machine.system() == 'windows' include_directories: petsc_incdir) endif +# Tool used to generate templated Fortran sources +fypp = find_program('fypp', required : true) + # build mf6 and libmf6 buildname = get_option('buildname') subdir('src') diff --git a/msvs/mf6core.vfproj b/msvs/mf6core.vfproj index ebf5108455a..5008b67af76 100644 --- a/msvs/mf6core.vfproj +++ b/msvs/mf6core.vfproj @@ -12,7 +12,7 @@ - + @@ -22,7 +22,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -930,12 +930,15 @@ - + + + + diff --git a/pixi.lock b/pixi.lock index c31516eff6f..2b2b2ad6fc9 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5,6 +5,8 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -89,6 +91,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -454,6 +457,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freexl-2.0.0-h82fd2cb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.16-he30d5cf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.3-h90308e0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -811,6 +815,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/freexl-2.0.0-h3183152_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fribidi-1.0.16-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/gdk-pixbuf-2.44.3-h07555a4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -1115,6 +1120,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.3-h7542897_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -1414,6 +1420,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.0-hdade9fe_0.conda @@ -1654,6 +1661,8 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -1748,6 +1757,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -2167,6 +2177,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freexl-2.0.0-h82fd2cb_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.16-he30d5cf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.3-h90308e0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -2579,6 +2590,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-64/freexl-2.0.0-h3183152_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fribidi-1.0.16-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/gdk-pixbuf-2.44.3-h07555a4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -2938,6 +2950,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.3-h7542897_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda @@ -3291,6 +3304,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/freexl-2.0.0-hf297d47_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/fribidi-1.0.16-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.12.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/geopandas-base-1.1.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/geos-3.14.0-hdade9fe_0.conda @@ -7269,6 +7283,18 @@ packages: - pkg:pypi/fsspec?source=compressed-mapping size: 147391 timestamp: 1764784920938 +- conda: https://conda.anaconda.org/conda-forge/noarch/fypp-3.2-pyhd8ed1ab_1.conda + sha256: 91d5ddb8a455527e36d15ec5e5cbdefb7366f796c0dd45bb8d971ef8739e742e + md5: d6ab05755452c30d96f04643cab62c46 + depends: + - pip + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/fypp?source=hash-mapping + size: 32250 + timestamp: 1734645346030 - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.3-h2b0a6b4_0.conda sha256: bbacfedf7aef18117a3642c29a916c3e93b08b5d2d4c4b077ce30617c29e966e md5: 8a6c305c6c6b169f839eefb951608e29 diff --git a/pixi.toml b/pixi.toml index f46dbe698b8..f8a8810a034 100644 --- a/pixi.toml +++ b/pixi.toml @@ -12,6 +12,7 @@ filelock = "*" flaky = "*" fortran-language-server = "*" fprettify = "*" +fypp = "*" geopandas = "*" gitpython = "*" jinja2 = ">=3.1.5,<4" diff --git a/src/Utilities/STLContainers/STLVecDbl.fypp b/src/Utilities/STLContainers/STLVecDbl.fypp new file mode 100644 index 00000000000..e2e46ea1e42 --- /dev/null +++ b/src/Utilities/STLContainers/STLVecDbl.fypp @@ -0,0 +1,4 @@ + +#:include "STLVector.fypp" + +$:VECTOR_TEMPLATE("Dbl", "real(DP)") diff --git a/src/Utilities/STLContainers/STLVecInt.fypp b/src/Utilities/STLContainers/STLVecInt.fypp new file mode 100644 index 00000000000..0bdd6f4b177 --- /dev/null +++ b/src/Utilities/STLContainers/STLVecInt.fypp @@ -0,0 +1,5 @@ + +#:include "STLVector.fypp" + +$:VECTOR_TEMPLATE("Int", "integer(I4B)") + diff --git a/src/Utilities/STLContainers/meson.build b/src/Utilities/STLContainers/meson.build new file mode 100644 index 00000000000..3bf955d823f --- /dev/null +++ b/src/Utilities/STLContainers/meson.build @@ -0,0 +1,20 @@ +fypp_files = [ + 'STLVecInt.fypp', + 'STLVecDbl.fypp', +] + +generated_sources = [] + +foreach f : fypp_files + generated_sources += custom_target( + f.replace('.fypp', '') + '_gen', + input : f, + output : f.replace('.fypp', '.f90'), + command: [ + fypp, + '-I' + meson.project_source_root() / 'src' / 'include', + '@INPUT@', + '@OUTPUT@' + ] + ) +endforeach diff --git a/src/Utilities/STLVecInt.f90 b/src/include/STLVector.fypp similarity index 75% rename from src/Utilities/STLVecInt.f90 rename to src/include/STLVector.fypp index 322df0a0e2b..e08aab2069a 100644 --- a/src/Utilities/STLVecInt.f90 +++ b/src/include/STLVector.fypp @@ -1,16 +1,18 @@ -module STLVecIntModule - use KindModule, only: I4B, LGP +#:mute +#:def VECTOR_TEMPLATE(NAME, TYPE) + +module STLVec${NAME}$Module + use KindModule, only: I4B, DP, LGP use SimModule, only: ustop use ArrayHandlersModule, only: ExpandArray implicit none private - public :: STLVecInt + public :: STLVec${NAME}$ integer(I4B), parameter :: defaultInitialCapacity = 4 - ! This is a dynamic vector type for integers - type :: STLVecInt - integer(I4B), private, allocatable :: values(:) !< the internal array for storage + type :: STLVec${NAME}$ + ${TYPE}$, private, allocatable :: values(:) !< the internal array for storage integer(I4B) :: size !< the number of elements integer(I4B), private :: capacity !< the reserved storage contains @@ -31,12 +33,12 @@ module STLVecIntModule procedure, pass(this) :: get_values !< returns a copy of the values ! private procedure, private, pass(this) :: expand - end type STLVecInt + end type STLVec${NAME}$ contains ! module routines subroutine init(this, capacity) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this integer(I4B), intent(in), optional :: capacity ! the initial capacity, when given if (present(capacity)) then @@ -51,8 +53,8 @@ subroutine init(this, capacity) end subroutine init subroutine push_back(this, newValue) - class(STLVecInt), intent(inout) :: this - integer(I4B) :: newValue + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$ :: newValue ! check capacity if (this%size + 1 > this%capacity) then call this%expand() @@ -64,8 +66,8 @@ subroutine push_back(this, newValue) end subroutine push_back subroutine push_back_unique(this, newValue) - class(STLVecInt), intent(inout) :: this - integer(I4B) :: newValue + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$ :: newValue if (.not. this%contains(newValue)) then call this%push_back(newValue) @@ -74,20 +76,20 @@ subroutine push_back_unique(this, newValue) end subroutine push_back_unique subroutine pop(this) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this if (this%size > 0) then this%size = this%size - 1 else - write (*, *) 'STLVecInt exception: cannot pop from an empty array' + write (*, *) 'STLVec${NAME}$ exception: cannot pop from an empty array' call ustop() end if end subroutine subroutine add_array(this, array) - class(STLVecInt), intent(inout) :: this - integer(I4B), dimension(:), pointer :: array + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$, dimension(:), pointer :: array ! local integer(I4B) :: i @@ -98,8 +100,8 @@ subroutine add_array(this, array) end subroutine add_array subroutine add_array_unique(this, array) - class(STLVecInt), intent(inout) :: this - integer(I4B), dimension(:), pointer :: array + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$, dimension(:), pointer :: array ! local integer(I4B) :: i @@ -112,21 +114,21 @@ subroutine add_array_unique(this, array) end subroutine add_array_unique function at(this, idx) result(value) - class(STLVecInt), intent(in) :: this + class(STLVec${NAME}$), intent(in) :: this integer(I4B), intent(in) :: idx - integer(I4B) :: value + ${TYPE}$ :: value value = this%values(idx) end function at function at_safe(this, idx) result(value) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this integer(I4B), intent(in) :: idx - integer(I4B) :: value + ${TYPE}$ :: value if (idx > this%size) then - write (*, *) 'STLVecInt exception: access out of bounds, index ', idx, & + write (*, *) 'STLVec${NAME}$ exception: access out of bounds, index ', idx, & ' exceeds actual size (', this%size, ')' call ustop() end if @@ -135,16 +137,16 @@ function at_safe(this, idx) result(value) end function at_safe subroutine set(this, idx, value) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this integer(I4B), intent(in) :: idx - integer(I4B) :: value + ${TYPE}$ :: value this%values(idx) = value end subroutine set subroutine clear(this) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this ! really, this is all there is to it... this%size = 0 @@ -152,9 +154,9 @@ subroutine clear(this) end subroutine clear subroutine shrink_to_fit(this) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this ! local - integer(I4B), allocatable :: tempValues(:) + ${TYPE}$, allocatable :: tempValues(:) integer(I4B) :: i, newSize if (this%size == this%capacity) then @@ -180,14 +182,14 @@ subroutine shrink_to_fit(this) end subroutine shrink_to_fit subroutine destroy(this) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this if (allocated(this%values)) then deallocate (this%values) this%size = 0 this%capacity = 0 else - write (*, *) 'STLVecInt exception: cannot delete an unallocated array' + write (*, *) 'STLVec${NAME}$ exception: cannot delete an unallocated array' call ustop() end if @@ -196,7 +198,7 @@ end subroutine destroy ! expand the array with the given strategy, at ! least by 1 subroutine expand(this) - class(STLVecInt), intent(inout) :: this + class(STLVec${NAME}$), intent(inout) :: this integer(I4B) :: increment ! expansion strategy @@ -208,8 +210,8 @@ end subroutine expand ! check if the element is already present function contains(this, val) result(res) - class(STLVecInt), intent(inout) :: this - integer(I4B) :: val + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$ :: val logical(LGP) :: res ! local integer(I4B) :: i @@ -227,8 +229,8 @@ end function contains !> @brief Return index of first occurrence, !< returns -1 when not present function get_index(this, val) result(idx) - class(STLVecInt), intent(inout) :: this - integer(I4B) :: val + class(STLVec${NAME}$), intent(inout) :: this + ${TYPE}$ :: val integer(I4B) :: idx ! local integer(I4B) :: i @@ -244,11 +246,14 @@ function get_index(this, val) result(idx) end function get_index function get_values(this) result(values) - class(STLVecInt), intent(in) :: this - integer(I4B), dimension(:), allocatable :: values + class(STLVec${NAME}$), intent(in) :: this + ${TYPE}$, dimension(:), allocatable :: values values = this%values(1:this%size) end function get_values -end module STLVecIntModule +end module STLVec${NAME}$Module + +#:enddef +#:endmute diff --git a/src/meson.build b/src/meson.build index 9cfbe91a49f..9ffae49831b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -490,7 +490,6 @@ modflow_sources = files( 'Utilities' / 'SimVariables.f90', 'Utilities' / 'SimStages.f90', 'Utilities' / 'Sim.f90', - 'Utilities' / 'STLVecInt.f90', 'Utilities' / 'STLStackInt.f90', 'Utilities' / 'Table.f90', 'Utilities' / 'TableTerm.f90', @@ -547,6 +546,10 @@ endif mf6_external = static_library('mf6_external', external_libraries) +# Generated templated sources +subdir('Utilities/STLContainers') +modflow_sources += generated_sources + message('MODFLOW 6 executable name: ' + buildname) if build_machine.system() == 'windows' and (with_petsc or with_netcdf)