Skip to content

Commit 8b595fd

Browse files
jvdp1igirault
authored andcommitted
Merge pull request fortran-lang#757 from jalvesz/readme-info
Add CLI option for building with fpm
2 parents 7be6485 + 5bccf62 commit 8b595fd

9 files changed

+302
-1
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ git checkout stdlib-fpm
184184
fpm build --profile release
185185
```
186186

187+
**Alternative**: as `fpm` does not currently support `fypp` natively, building `stdlib` with `fpm` can be done in two steps: a) launch the preprocessor through the `fpm-deployment.sh` script, which creates a subfolder `stdlib-fpm` and b) build the project using the processed files within the latter subfolder. This process can be done with the following commands:
188+
189+
```sh
190+
source ./ci/fpm-deployment.sh
191+
cd stdlib-fpm/
192+
fpm build --profile release
193+
```
194+
187195
You can run the examples with `fpm` as:
188196

189197
```sh

doc/specs/stdlib_math.md

+53
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,56 @@ When both `prepend` and `append` are not present, the result `y` has one fewer e
554554
```fortran
555555
{!example/math/example_diff.f90!}
556556
```
557+
558+
### `meshgrid` subroutine
559+
560+
#### Description
561+
562+
Computes a list of coordinate matrices from coordinate vectors.
563+
564+
For $n \geq 1$ coordinate vectors $(x_1, x_2, ..., x_n)$ of sizes $(s_1, s_2, ..., s_n)$, `meshgrid` computes $N$ coordinate matrices $(X_1, X_2, ..., X_n)$ with identical shape corresponding to the selected indexing:
565+
- Cartesian indexing (default behavior): the shape of the coordinate matrices is $(s_2, s_1, s_3, s_4, ... s_n)$.
566+
- matrix indexing: the shape of the coordinate matrices is $(s_1, s_2, s_3, s_4, ... s_n)$.
567+
568+
#### Syntax
569+
570+
For a 2D problem in Cartesian indexing:
571+
`call [[stdlib_math(module):meshgrid(interface)]](x, y, xm, ym)`
572+
573+
For a 3D problem in Cartesian indexing:
574+
`call [[stdlib_math(module):meshgrid(interface)]](x, y, z, xm, ym, zm)`
575+
576+
For a 3D problem in matrix indexing:
577+
`call [[stdlib_math(module):meshgrid(interface)]](x, y, z, xm, ym, zm, indexing="ij")`
578+
579+
The subroutine can be called in $n$-dimensional situations, as long as $n$ is inferior to the maximum allowed array rank.
580+
581+
#### Status
582+
583+
Experimental.
584+
585+
#### Class
586+
587+
Subroutine.
588+
589+
#### Arguments
590+
591+
For a `n`-dimensional problem:
592+
593+
`n` arguments `x1, x2, ..., xn`: The coordinate vectors.
594+
Shall be a `real/integer` and `rank-1` array.
595+
These arguments are `intent(in)`.
596+
597+
`n` arguments `xm1, xm2, ..., xmn`: The coordinate matrices.
598+
Shall be `real/integer` arrays of adequate shape:
599+
- for Cartesian indexing, the shape of the coordinate matrices must be `[size(x2), size(x1), size(x3), ..., size(xn)]`
600+
- for matrix indexing, the shape of the coordinate matrices must be `[size(x1), size(x2), size(x3), ..., size(xn)]`
601+
These argument are `intent(out)`.
602+
603+
`indexing`: the selected indexing.
604+
Shall be a `character(len=2)` equal to `"xy"` for Cartesian indexing (default), or `"ij"` for matrix indexing.
605+
This argument is `intent(in)` and `optional`, and is equal to `"xy"` by default.
606+
607+
#### Example
608+
609+
TO ADD

example/math/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ ADD_EXAMPLE(math_argd)
1313
ADD_EXAMPLE(math_arg)
1414
ADD_EXAMPLE(math_argpi)
1515
ADD_EXAMPLE(math_is_close)
16+
ADD_EXAMPLE(meshgrid)

example/math/example_meshgrid.f90

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
program example_meshgrid
2+
3+
use stdlib_math, only: meshgrid, linspace
4+
use stdlib_kinds, only: sp
5+
6+
implicit none
7+
8+
integer, parameter :: nx = 3, ny = 2
9+
real(sp) :: x(nx), y(ny), &
10+
xm_cart(ny, nx), ym_cart(ny, nx), &
11+
xm_mat(nx, ny), ym_mat(nx, ny)
12+
13+
x = linspace(0_sp, 1_sp, nx)
14+
y = linspace(0_sp, 1_sp, ny)
15+
16+
call meshgrid(x, y, xm_cart, ym_cart)
17+
print *, "xm_cart = "
18+
call print_2d_array(xm_cart)
19+
print *, "ym_cart = "
20+
call print_2d_array(ym_cart)
21+
22+
call meshgrid(x, y, xm_mat, ym_mat, indexing="ij")
23+
print *, "xm_mat = "
24+
call print_2d_array(xm_mat)
25+
print *, "ym_mat = "
26+
call print_2d_array(ym_mat)
27+
28+
contains
29+
subroutine print_2d_array(array)
30+
real(sp), intent(in) :: array(:, :)
31+
integer :: i
32+
33+
do i = 1, size(array, dim=1)
34+
print *, array(i, :)
35+
end do
36+
end subroutine
37+
end program example_meshgrid

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ set(fppFiles
5555
stdlib_math_is_close.fypp
5656
stdlib_math_all_close.fypp
5757
stdlib_math_diff.fypp
58+
stdlib_math_meshgrid.fypp
5859
stdlib_string_type.fypp
5960
stdlib_string_type_constructor.fypp
6061
stdlib_strings_to_string.fypp

src/stdlib_math.fypp

+25-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module stdlib_math
1414
public :: EULERS_NUMBER_QP
1515
#:endif
1616
public :: DEFAULT_LINSPACE_LENGTH, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH
17-
public :: arange, arg, argd, argpi, is_close, all_close, diff
17+
public :: arange, arg, argd, argpi, is_close, all_close, diff, meshgrid
1818

1919
integer, parameter :: DEFAULT_LINSPACE_LENGTH = 100
2020
integer, parameter :: DEFAULT_LOGSPACE_LENGTH = 50
@@ -382,6 +382,30 @@ module stdlib_math
382382
#:endfor
383383
end interface diff
384384

385+
386+
!> Version: experimental
387+
!>
388+
!> Computes a list of coordinate matrices from coordinate vectors.
389+
!> ([Specification](../page/specs/stdlib_math.html#meshgrid))
390+
interface meshgrid
391+
#:set RANKS = range(1, MAXRANK + 1)
392+
#:for k1, t1 in IR_KINDS_TYPES
393+
#:for rank in RANKS
394+
#:set RName = rname("meshgrid", rank, t1, k1)
395+
module subroutine ${RName}$(&
396+
${"".join(f"x{i}, " for i in range(1, rank + 1))}$ &
397+
${"".join(f"xm{i}, " for i in range(1, rank + 1))}$ &
398+
indexing &
399+
)
400+
#:for i in range(1, rank + 1)
401+
${t1}$, intent(in) :: x${i}$(:)
402+
${t1}$, intent(out) :: xm${i}$ ${ranksuffix(rank)}$
403+
#:endfor
404+
character(len=2), intent(in), optional :: indexing
405+
end subroutine ${RName}$
406+
#:endfor
407+
#:endfor
408+
end interface meshgrid
385409
contains
386410

387411
#:for k1, t1 in IR_KINDS_TYPES

src/stdlib_math_meshgrid.fypp

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#:include "common.fypp"
2+
#:set IR_KINDS_TYPES = INT_KINDS_TYPES + REAL_KINDS_TYPES
3+
#:set RANKS = range(1, MAXRANK + 1)
4+
5+
#:def meshgrid_loop(indices)
6+
#:for j in reversed(indices)
7+
do i${j}$ = 1, size(x${j}$)
8+
#:endfor
9+
#:for j in indices
10+
xm${j}$(${"".join(f"i{j}," for j in indices).removesuffix(",")}$) = &
11+
x${j}$(i${j}$)
12+
#:endfor
13+
#:for j in indices
14+
end do
15+
#:endfor
16+
#:enddef
17+
18+
submodule(stdlib_math) stdlib_math_meshgrid
19+
20+
contains
21+
22+
#:for k1, t1 in IR_KINDS_TYPES
23+
#:for rank in RANKS
24+
#:if rank == 1
25+
#:set XY_INDICES = [1]
26+
#:set IJ_INDICES = [1]
27+
#:else
28+
#:set XY_INDICES = [2, 1] + [j for j in range(3, rank + 1)]
29+
#:set IJ_INDICES = [1, 2] + [j for j in range(3, rank + 1)]
30+
#:endif
31+
#: set RName = rname("meshgrid", rank, t1, k1)
32+
module procedure ${RName}$
33+
use stdlib_optval, only: optval
34+
use stdlib_error, only: error_stop
35+
36+
integer :: ${"".join(f"i{j}," for j in range(1, rank + 1)).removesuffix(",")}$
37+
38+
select case (optval(indexing, "xy"))
39+
case ("xy")
40+
$:meshgrid_loop(XY_INDICES)
41+
case ("ij")
42+
$:meshgrid_loop(IJ_INDICES)
43+
case default
44+
call error_stop("ERROR (meshgrid): unexpected indexing.")
45+
end select
46+
end procedure
47+
#:endfor
48+
#:endfor
49+
50+
end submodule

test/math/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
set(
22
fppFiles
33
"test_stdlib_math.fypp"
4+
"test_meshgrid.fypp"
45
)
56
fypp_f90("${fyppFlags}" "${fppFiles}" outFiles)
67

78
ADDTEST(stdlib_math)
89
ADDTEST(linspace)
910
ADDTEST(logspace)
11+
ADDTEST(meshgrid)

test/math/test_meshgrid.fypp

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
! SPDX-Identifier: MIT
2+
3+
#:include "common.fypp"
4+
#:set IR_KINDS_TYPES = INT_KINDS_TYPES + REAL_KINDS_TYPES
5+
#:set RANKS = range(1, MAXRANK + 1)
6+
#:set INDEXINGS = ["default", "xy", "ij"]
7+
8+
#:def OPTIONAL_PART_IN_SIGNATURE(indexing)
9+
#:if indexing in ("xy", "ij")
10+
${f', "{indexing}"'}$
11+
#:endif
12+
#:enddef
13+
14+
module test_meshgrid
15+
use testdrive, only : new_unittest, unittest_type, error_type, check
16+
use stdlib_math, only: meshgrid
17+
use stdlib_kinds, only: int8, int16, int32, int64, sp, dp, xdp, qp
18+
implicit none
19+
20+
public :: collect_meshgrid
21+
22+
#:for k1 in REAL_KINDS
23+
real(kind=${k1}$), parameter :: PI_${k1}$ = acos(-1.0_${k1}$)
24+
#:endfor
25+
26+
contains
27+
28+
!> Collect all exported unit tests
29+
subroutine collect_meshgrid(testsuite)
30+
!> Collection of tests
31+
type(unittest_type), allocatable, intent(out) :: testsuite(:)
32+
33+
testsuite = [ &
34+
#:for k1, t1 in IR_KINDS_TYPES
35+
#:for rank in RANKS
36+
#:for INDEXING in INDEXINGS
37+
#: set RName = rname(f"meshgrid_{INDEXING}", rank, t1, k1)
38+
new_unittest("${RName}$", test_${RName}$), &
39+
#:endfor
40+
#:endfor
41+
#:endfor
42+
new_unittest("dummy", test_dummy) &
43+
]
44+
45+
end subroutine collect_meshgrid
46+
47+
#:for k1, t1 in IR_KINDS_TYPES
48+
#:for rank in RANKS
49+
#:for INDEXING in INDEXINGS
50+
#:if rank == 1
51+
#:set INDICES = [1]
52+
#:else
53+
#:if INDEXING in ("default", "xy")
54+
#:set INDICES = [2, 1] + [j for j in range(3, rank + 1)]
55+
#:elif INDEXING == "ij"
56+
#:set INDICES = [1, 2] + [j for j in range(3, rank + 1)]
57+
#:endif
58+
#:endif
59+
#: set RName = rname(f"meshgrid_{INDEXING}", rank, t1, k1)
60+
#: set GRIDSHAPE = "".join("length," for j in range(rank)).removesuffix(",")
61+
subroutine test_${RName}$(error)
62+
!> Error handling
63+
type(error_type), allocatable, intent(out) :: error
64+
integer, parameter :: length = 3
65+
${t1}$ :: ${"".join(f"x{j}(length)," for j in range(1, rank + 1)).removesuffix(",")}$
66+
${t1}$ :: ${"".join(f"xm{j}({GRIDSHAPE})," for j in range(1, rank + 1)).removesuffix(",")}$
67+
${t1}$ :: ${"".join(f"xm{j}_exact({GRIDSHAPE})," for j in range(1, rank + 1)).removesuffix(",")}$
68+
integer :: i
69+
integer :: ${"".join(f"i{j}," for j in range(1, rank + 1)).removesuffix(",")}$
70+
${t1}$, parameter :: ZERO = 0
71+
! valid test case
72+
#:for index in range(1, rank + 1)
73+
x${index}$ = [(i, i = length * ${index - 1}$ + 1, length * ${index}$)]
74+
#:endfor
75+
#:for j in range(1, rank + 1)
76+
xm${j}$_exact = reshape( &
77+
[${"".join("(" for dummy in range(rank)) + f"x{j}(i{j})" + "".join(f", i{index} = 1, size(x{index}))" for index in INDICES)}$], &
78+
shape=[${GRIDSHAPE}$] &
79+
)
80+
#:endfor
81+
call meshgrid( &
82+
${"".join(f"x{j}," for j in range(1, rank + 1))}$ &
83+
${"".join(f"xm{j}," for j in range(1, rank + 1)).removesuffix(",")}$ &
84+
${OPTIONAL_PART_IN_SIGNATURE(INDEXING)}$ )
85+
#:for j in range(1, rank + 1)
86+
call check(error, abs(maxval(xm${j}$ - xm${j}$_exact)), ZERO)
87+
if (allocated(error)) return
88+
#:endfor
89+
end subroutine test_${RName}$
90+
#:endfor
91+
#:endfor
92+
#:endfor
93+
94+
subroutine test_dummy(error)
95+
!> Error handling
96+
type(error_type), allocatable, intent(out) :: error
97+
end subroutine
98+
99+
end module test_meshgrid
100+
101+
program tester
102+
use, intrinsic :: iso_fortran_env, only : error_unit
103+
use testdrive, only : run_testsuite, new_testsuite, testsuite_type
104+
use test_meshgrid, only : collect_meshgrid
105+
implicit none
106+
integer :: stat, is
107+
type(testsuite_type), allocatable :: testsuites(:)
108+
character(len=*), parameter :: fmt = '("#", *(1x, a))'
109+
110+
stat = 0
111+
112+
testsuites = [ &
113+
new_testsuite("meshgrid", collect_meshgrid) &
114+
]
115+
116+
do is = 1, size(testsuites)
117+
write(error_unit, fmt) "Testing:", testsuites(is)%name
118+
call run_testsuite(testsuites(is)%collect, error_unit, stat)
119+
end do
120+
121+
if (stat > 0) then
122+
write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!"
123+
error stop
124+
end if
125+
end program tester

0 commit comments

Comments
 (0)