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)