From 9f54690410e54524ebbbc9b8200a9947d9755f5c Mon Sep 17 00:00:00 2001 From: Till Ehrengruber Date: Tue, 2 Jul 2024 12:46:53 +0200 Subject: [PATCH] All tests should pass again --- README.md | 31 ++++- src/GridTools.jl | 24 +--- src/gt2py/gt2py.jl | 21 +-- src/gt2py/jast_to_foast.jl | 21 ++- test/embedded_test.jl | 10 +- test/gt2py_fo_exec.jl | 271 +++++++++++++++++++------------------ test/gt2py_fo_parser.jl | 145 ++++++++++---------- test/mesh_definitions.jl | 25 ++++ test/runtests.jl | 32 +---- 9 files changed, 316 insertions(+), 264 deletions(-) create mode 100644 test/mesh_definitions.jl diff --git a/README.md b/README.md index c956ea0..6e14736 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,32 @@ # GridTools -[![Build Status](https://github.com/jeffzwe/GridTools.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/jeffzwe/GridTools.jl/actions/workflows/CI.yml?query=branch%3Amain) [![Static Badge](https://img.shields.io/badge/docs-stable-blue.svg)](https://jeffzwe.github.io/GridTools.jl/dev) \ No newline at end of file +[![Build Status](https://github.com/jeffzwe/GridTools.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/jeffzwe/GridTools.jl/actions/workflows/CI.yml?query=branch%3Amain) [![Static Badge](https://img.shields.io/badge/docs-stable-blue.svg)](https://jeffzwe.github.io/GridTools.jl/dev) + +## Installation + +### Setup python virtual environment + + +### Development installation + +```bash +export GRIDTOOLS_JL_PATH="..." +export GT4PY_PATH="..." +# create python virtual environemnt +# make sure to use a python version that is compatible with GT4Py +python -m venv venv +# activate virtual env +# this command has be run everytime GridTools.jl is used +source venv/bin/activate +# clone gt4py +git clone git@github.com:GridTools/gt4py.git $GT4PY_PATH +pip install -r $GT4PY_PATH/requirements-dev.txt +pip install -e $GT4PY_PATH +# +``` + +## Troubleshooting + +__undefined symbol: PyObject_Vectorcall__ + +Make sure to run everything in the same environment that you have build `PyCall` with. A common reason is you have built PyCall in a virtual environement and then didn't load it when executing stencils. \ No newline at end of file diff --git a/src/GridTools.jl b/src/GridTools.jl index f353bbc..6dcbf7e 100644 --- a/src/GridTools.jl +++ b/src/GridTools.jl @@ -234,17 +234,6 @@ compute_remapped_field_info( conn )= ((), ()) -function remap_ts( - field::Field, - offset::FieldOffsetTS{OffsetName, SourceDim, Tuple{TargetDim}}, - nb_ind::Int64)::Field where {OffsetName, SourceDim <: Dimension, TargetDim <:Dimension} - conn = OFFSET_PROVIDER[string(OffsetName)] - - new_offsets = Dict(field.dims[i] => field.origin[i] for i in 1:length(field.dims)) - new_offsets[conn] = nb_ind - return Field(field.dims, field.data, field.broadcast_dims, origin = new_offsets) -end - function remap_broadcast_dims( broadcast_dims::Tuple{T, Vararg{Dimension}}, @@ -282,7 +271,7 @@ function remap_ts( # eltype(field.data)(0) # end #end, CartesianIndices(map(len -> Base.OneTo(len), out_field_size))) - out_field = Array{eltype(field.data)}(undef, out_field_size) + out_field = zeros(eltype(field.data), out_field_size) for position in eachindex(IndexCartesian(), out_field) neighbor_exists, new_position = remap_position(Tuple(position), out_field_dims, offset, nb_ind, conn) if neighbor_exists @@ -371,10 +360,10 @@ copyfield!(target, source) = target .= source # Field operator functionalities ------------------------------------------------------------ -OFFSET_PROVIDER::Union{Dict{String, Union{Connectivity, Dimension}}, Nothing} = nothing +OFFSET_PROVIDER::Union{Dict{String, <:Union{Connectivity, Dimension}}, Nothing} = nothing FIELD_OPERATORS::Dict{Symbol, PyObject} = Dict{Symbol, PyObject}() -function (fo::FieldOp)(args...; offset_provider::Dict{String, Union{Connectivity, Dimension}} = Dict{String, Union{Connectivity, Dimension}}(), backend::String = "embedded", out = nothing, kwargs...) +function (fo::FieldOp)(args...; offset_provider::Dict{String, <:Union{Connectivity, Dimension}} = Dict{String, Union{Connectivity, Dimension}}(), backend::String = "embedded", out = nothing, kwargs...) is_outermost_fo = isnothing(OFFSET_PROVIDER) if is_outermost_fo @@ -476,13 +465,14 @@ end macro module_vars() return esc(quote + # TODO(tehrengruber): for some reasons this was needed from some point on. cleanup + base_vars = Dict(name => Core.eval(Base, name) for name in [:Int64, :Int32, :Float32, :Float64]) module_vars = Dict(name => Core.eval(@__MODULE__, name) for name in names(@__MODULE__)) local_vars = Base.@locals - merge(module_vars, local_vars, GridTools.builtin_op) + merge(base_vars, module_vars, local_vars, GridTools.builtin_op) end) end - """ @field_operator @@ -499,7 +489,7 @@ macro field_operator(expr::Expr) expr_dict = splitdef(expr) expr_dict[:name] = generate_unique_name(f_name) unique_expr = combinedef(expr_dict) - + return Expr(:(=), esc(f_name), :(FieldOp(namify($(Expr(:quote, expr))), $(esc(unique_expr)), $(Expr(:quote, expr)), get_closure_vars($(Expr(:quote, expr)), @module_vars)))) end diff --git a/src/gt2py/gt2py.jl b/src/gt2py/gt2py.jl index 53e07dd..470c8b3 100644 --- a/src/gt2py/gt2py.jl +++ b/src/gt2py/gt2py.jl @@ -126,7 +126,10 @@ CLOSURE_VARS::Dict = Dict() # Methods ----------------------------------------------------------------------------------- -function py_field_operator(fo, backend = py_backends["gpu"], grid_type = py"None"o, operator_attributes = Dict()) +function py_field_operator(fo, backend = Nothing, grid_type = py"None"o, operator_attributes = Dict()) + if backend == Nothing + backend = py_backends["gpu"] + end foast_definition_node, closure_vars = jast_to_foast(fo.expr, fo.closure_vars) loc = foast_definition_node.location @@ -146,12 +149,12 @@ function py_field_operator(fo, backend = py_backends["gpu"], grid_type = py"None foast_node = FieldOperatorTypeDeduction.apply(untyped_foast_node) return FieldOperater( - foast_node=foast_node, - closure_vars=closure_vars, - definition=py"None"o, - backend=backend, - grid_type=grid_type, - ) + foast_node=foast_node, + closure_vars=closure_vars, + definition=py"None"o, + backend=backend, + grid_type=grid_type, + ) end function jast_to_foast(expr::Expr, closure_vars::Dict) @@ -187,7 +190,7 @@ function postprocess_definition(foast_node, closure_vars, annotations) end py_args(args::Union{Base.Pairs, Dict}) = Dict(i.first => convert_type(i.second) for i in args) -py_args(args::Tuple) = [map(convert_type, args)...] +py_args(args::Tuple) = [convert_type(arg) for arg in args] py_args(arg) = convert_type(arg) py_args(n::Nothing) = nothing @@ -204,6 +207,8 @@ function convert_type(a::Field) new_data = np.asarray(a.data) @warn "Dtype of the Field: $a is not concrete. Data must be copied to Python which may affect performance. Try using dtypes <: Array." end + println(new_data) + println(typeof(new_data)) offset = Dict(convert_type(dim) => a.origin[i] for (i, dim) in enumerate(a.dims)) diff --git a/src/gt2py/jast_to_foast.jl b/src/gt2py/jast_to_foast.jl index ad2bf71..a8e5ec9 100644 --- a/src/gt2py/jast_to_foast.jl +++ b/src/gt2py/jast_to_foast.jl @@ -19,7 +19,7 @@ function _builtin_type_constructor_symbols(captured_vars, loc)::Tuple if name in fbuiltins.TYPE_BUILTIN_NAMES && value == py"getattr"(fbuiltins, name) # NOTE: Should be === . Doesnt work with pyobjects ) - to_be_inserted = union(python_type_builtins, captured_type_builtins) + to_be_inserted = merge(python_type_builtins, captured_type_builtins) for (name, value) in to_be_inserted push!(result, @@ -67,13 +67,22 @@ function visit_function(args::Array, closure_vars::Dict) function_header = function_header.args[1] end - for param in Base.tail((Tuple(function_header.args))) - function_params = vcat(function_params, visit_types(param.args, closure_vars, inner_loc)) + func_name, func_params_expr... = [function_header.args...] + + # canonicalize keyword arguments + # TODO(tehrengruber): ensure this is tested properly + if length(func_params_expr) >= 1 && func_params_expr[1] isa Expr && func_params_expr[1].head == :parameters + func_kwparams_exprs = popfirst!(func_params_expr).args + push!(func_params_expr, func_kwparams_exprs...) + end + + for param in func_params_expr + push!(function_params, visit_types(param.args, closure_vars, inner_loc)) end return foast.FunctionDefinition( - id=string(function_header.args[1]), - params= function_params, + id=string(func_name), + params=function_params, body=function_body, closure_vars=closure_var_symbols, location=inner_loc @@ -417,6 +426,8 @@ function from_type_hint(expr::Expr, closure_vars::Dict) dim = [] (dims, dtype) = param_type[2:end] + # TODO: do some sanity checks here for example Field{Int64, Dims} will fail terribly + for d in dims.args[2:end] @assert string(d) in keys(closure_vars) push!(dim, closure_vars[string(d)]) diff --git a/test/embedded_test.jl b/test/embedded_test.jl index 0bb70b3..25a7ef4 100644 --- a/test/embedded_test.jl +++ b/test/embedded_test.jl @@ -1,6 +1,7 @@ using Statistics # Setup ------------------------------------------------------------------------------------------------------------------------------ +include("mesh_definitions.jl") cell_values = Field(Cell, [1.0, 1.0, 2.0, 3.0, 5.0, 8.0]) edge_to_cell_table = [ @@ -23,7 +24,10 @@ C2E_offset_provider = Connectivity(edge_to_cell_table, Edge, Cell, 2) offset_provider = Dict{String, Union{Connectivity, Dimension}}( "E2C" => E2C_offset_provider, - "C2E" => C2E_offset_provider + "C2E" => C2E_offset_provider, + # TODO(tehrengruber): cleanup + "E2CDim" => E2C_offset_provider, + "C2EDim" => C2E_offset_provider ) x = Field((Cell, K), reshape(collect(-3.0:2.0), (3, 2))) @@ -94,7 +98,7 @@ end end fo_remapping(cell_values, offset_provider=offset_provider, out = out) - @test out == result_offset_call + @test out == result_offset_call end @@ -169,5 +173,5 @@ end 1.0 1.0 1.0] result = Field((Cell, K), result_data) - @test concat(x, y).data == result + @test concat(a, b).data == result end \ No newline at end of file diff --git a/test/gt2py_fo_exec.jl b/test/gt2py_fo_exec.jl index 9d66995..c084765 100644 --- a/test/gt2py_fo_exec.jl +++ b/test/gt2py_fo_exec.jl @@ -1,15 +1,22 @@ +using Test +using GridTools +using MacroTools + +include("mesh_definitions.jl") struct TestFailedException <: Exception message::String end macro to_py(expr::Expr) - try - eval(expr) - return true - catch e - throw(TestFailedException("The following test: $(namify(expr)) encountered following error: $e")) + res = quote try + $(esc(expr)) + true + catch e + throw(TestFailedException("The following test: $($(string(namify(expr)))) encountered following error: $e")) + end end + return res end # Setup ------------------------------------------------------------------------------------------ @@ -49,179 +56,183 @@ offset_provider = Dict{String, Connectivity}( # Tests ------------------------------------------------ +function test_gt4py_fo_exec() + a = Field(Cell, collect(1.:15.)) + b = Field(Cell, collect(-1.:-1:-15.)) + out = Field(Cell, zeros(Float64, 15)) -a = Field(Cell, collect(1.:15.)) -b = Field(Cell, collect(-1.:-1:-15.)) -out = Field(Cell, zeros(Float64, 15)) - -@field_operator function fo_addition(a::Field{Tuple{Cell_}, Float64}, b::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} - return a .+ b -end - -@test @to_py fo_addition(a, b, backend = "py", out = out) - -# ------------------------------------------------ - -a = Field(Cell, collect(1:15)) -out = Field(Cell, zeros(Int64, 15)) + @field_operator function fo_addition(a::Field{Tuple{Cell_}, Float64}, b::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + return a .+ b + end -@field_operator function fo_nested_if_else(f::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Int64} - tmp = f - if 1. .< 10.0 - tmp = f .+ 1 - if 30 > 5 - tmp = tmp .+ 20 - tmp = tmp .- 10 - elseif 40 < 4 - tmp = 4 == 5 ? tmp : tmp .- 100 - else - tmp = tmp .* 5 + fo_addition(a, b, backend = "py", out = out) + @test all(out.data .== 0) + + # ------------------------------------------------ + + a = Field(Cell, collect(Int32, 1:15)) # TODO(tehrengruber): if we don't use the right dtype here we get a horrible error in python + out = Field(Cell, zeros(Int32, 15)) + + @field_operator function fo_nested_if_else(f::Field{Tuple{Cell_}, Int32})::Field{Tuple{Cell_}, Int32} + tmp = f + if 1. .< 10.0 + tmp = f .+ 1 + if 30 > 5 + tmp = tmp .+ 20 + tmp = tmp .- 10 + elseif 40 < 4 + tmp = 4 == 5 ? tmp : tmp .- 100 + else + tmp = tmp .* 5 + end + tmp = tmp .+ 10 + elseif 10 < 20 + tmp = f .- 1 + else + tmp = tmp .* 10 + tmp = tmp .+ 10 + tmp = tmp .+ 100 end - tmp = tmp .+ 10 - elseif 10 < 20 - tmp = f .- 1 - else - tmp = tmp .* 10 - tmp = tmp .+ 10 - tmp = tmp .+ 100 + return tmp end - return tmp -end -@test @to_py fo_nested_if_else(a, backend = "py", out = out) + @test @to_py fo_nested_if_else(a, backend = "py", out = out) -# ------------------------------------------------ -a = Field(Cell, collect(1.:15.)) -out = Field(Edge, zeros(Float64, 12)) + # ------------------------------------------------ + a = Field(Cell, collect(1.:15.)) + out = Field(Edge, zeros(Float64, 12)) -@field_operator function fo_remapping(a::Field{Tuple{Cell_}, Float64})::Field{Float64, 1, Tuple{Edge_}} - return a(E2C[1]) -end + @field_operator function fo_remapping(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Edge_}, Float64} + return a(E2C[1]) + end -@test @to_py fo_remapping(a, offset_provider=offset_provider, backend = "py", out = out) + @test @to_py fo_remapping(a, offset_provider=offset_provider, backend = "py", out = out) -# ------------------------------------------------ -a = Field(Cell, collect(1.:15.)) -out = Field(Edge, zeros(Float64, 12)) + # ------------------------------------------------ + a = Field(Cell, collect(1.:15.)) + out = Field(Edge, zeros(Float64, 12)) -@field_operator function fo_neighbor_sum(a::Field{Tuple{Cell_}, Float64})::Field{Float64, 1, Tuple{Edge_}} - return neighbor_sum(a(E2C), axis=E2CDim) -end + @field_operator function fo_neighbor_sum(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Edge_}, Float64} + return neighbor_sum(a(E2C), axis=E2CDim) + end -@test @to_py fo_neighbor_sum(a, offset_provider=offset_provider, backend = "py", out = out) + @test @to_py fo_neighbor_sum(a, offset_provider=offset_provider, backend = "py", out = out) -# ------------------------------------------------ -a = Field(Cell, collect(1.:15.)) -out = Field(Edge, zeros(Float64, 12)) + # ------------------------------------------------ + a = Field(Cell, collect(1.:15.)) + out = Field(Edge, zeros(Float64, 12)) -@field_operator function fo_max_over(a::Field{Tuple{Cell_}, Float64})::Field{Float64, 1, Tuple{Edge_}} - return max_over(a(E2C), axis=E2CDim) -end + @field_operator function fo_max_over(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Edge_}, Float64} + return max_over(a(E2C), axis=E2CDim) + end -@test @to_py fo_max_over(a, offset_provider=offset_provider, backend = "py", out = out) + @test @to_py fo_max_over(a, offset_provider=offset_provider, backend = "py", out = out) -# ------------------------------------------------ + # ------------------------------------------------ -a = Field(Cell, collect(1.:15.)) -out = Field(Edge, zeros(Float64, 12)) + a = Field(Cell, collect(1.:15.)) + out = Field(Edge, zeros(Float64, 12)) -@field_operator function fo_min_over(a::Field{Tuple{Cell_}, Float64})::Field{Float64, 1, Tuple{Edge_}} - return min_over(a(E2C), axis=E2CDim) -end + @field_operator function fo_min_over(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Edge_}, Float64} + return min_over(a(E2C), axis=E2CDim) + end -@test @to_py fo_min_over(a, offset_provider=offset_provider, backend = "py", out = out) + @test @to_py fo_min_over(a, offset_provider=offset_provider, backend = "py", out = out) -# ------------------------------------------------ + # ------------------------------------------------ -a = Field(Cell, collect(1.:15.)) -out = Field((Cell, K), zeros(15, 5)) + a = Field(Cell, collect(1.:15.)) + out = Field((Cell, K), zeros(15, 5)) -@field_operator function fo_simple_broadcast(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_, K_}, Float64} - return broadcast(a, (Cell, K)) -end - -@test @to_py fo_simple_broadcast(a, backend = "py", out = out) + @field_operator function fo_simple_broadcast(a::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_, K_}, Float64} + return broadcast(a, (Cell, K)) + end + + @test @to_py fo_simple_broadcast(a, backend = "py", out = out) -# ------------------------------------------------ + # ------------------------------------------------ -j_out = Field((), fill(0.), (Cell, K)) -py_out = Field((Cell, K), fill(0., (10, 10))) + j_out = Field((), fill(0.), (Cell, K)) + py_out = Field((Cell, K), fill(0., (10, 10))) -@field_operator function fo_scalar_broadcast()::Field{Tuple{Cell_, K_}, Float64} - return broadcast(5., (Cell, K)) -end + @field_operator function fo_scalar_broadcast()::Field{Tuple{Cell_, K_}, Float64} + return broadcast(5., (Cell, K)) + end -@test @to_py fo_scalar_broadcast(backend = "py", out = py_out) + @test @to_py fo_scalar_broadcast(backend = "py", out = py_out) -# ------------------------------------------------ + # ------------------------------------------------ -a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) -b = Field((Cell, K), fill(-1., (6, 2))) -mask = Field((Cell, K), rand(Bool, (6, 2))) -out = Field((Cell, K), zeros(6, 2)) + a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) + b = Field((Cell, K), fill(-1., (6, 2))) + mask = Field((Cell, K), rand(Bool, (6, 2))) + out = Field((Cell, K), zeros(6, 2)) -@field_operator function fo_where(mask::Field{Tuple{Cell_, K_}, Bool}, a::Field{Tuple{Cell_, K_}, Float64}, b::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} - return where(mask, a, b) -end + @field_operator function fo_where(mask::Field{Tuple{Cell_, K_}, Bool}, a::Field{Tuple{Cell_, K_}, Float64}, b::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} + return where(mask, a, b) + end -@test @to_py fo_where(mask, a, b, backend = "py", out = out) + @test @to_py fo_where(mask, a, b, backend = "py", out = out) -# ------------------------------------------------- + # ------------------------------------------------- -a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) -out = Field((Cell, K), zeros(Int64, (6, 2))) + a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) + out = Field((Cell, K), zeros(Int64, (6, 2))) -@field_operator function fo_astype(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Int64} - return convert(Int64, a) -end + @field_operator function fo_astype(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Int64} + return convert(Int64, a) + end -@test @to_py fo_astype(a, backend = "py", out = out) + @test @to_py fo_astype(a, backend = "py", out = out) -# ------------------------------------------------- + # ------------------------------------------------- -a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) -out = Field((Cell, K), zeros((6, 2))) + a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) + out = Field((Cell, K), zeros((6, 2))) -@field_operator function fo_sin(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} - return sin.(a) -end + @field_operator function fo_sin(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} + return sin.(a) + end -@test @to_py fo_sin(a, backend = "py", out = out) + @test @to_py fo_sin(a, backend = "py", out = out) -# ------------------------------------------------- + # ------------------------------------------------- -a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) -out = Field((Cell, K), zeros((6, 2))) + a = Field((Cell, K), reshape(collect(1.:12.), (6, 2))) + out = Field((Cell, K), zeros((6, 2))) -@field_operator function fo_asinh(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} - return asinh.(a) -end + @field_operator function fo_asinh(a::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} + return asinh.(a) + end -@test @to_py fo_asinh(a, backend = "py", out = out) + @test @to_py fo_asinh(a, backend = "py", out = out) -# ------------------------------------------------- + # ------------------------------------------------- -# TODO OffsetArray is ignored for the moment + # TODO OffsetArray is ignored for the moment -A = Field((Vertex, K), reshape(collect(1.:15.), 3, 5), origin = Dict(Vertex => -2, K => -1)) -B = Field((K, Edge), reshape(ones(6), 3, 2)) + A = Field((Vertex, K), reshape(collect(1.:15.), 3, 5), origin = Dict(Vertex => -2, K => -1)) + B = Field((K, Edge), reshape(ones(6), 3, 2)) -out = Field((Vertex, K, Edge), zeros(3,3,2)) + out = Field((Vertex, K, Edge), zeros(3,3,2)) -@field_operator function fo_offset_array(A::Field{Tuple{Vertex_, K_}, Float64}, B::Field{Tuple{K_, Edge_}, Float64})::Field{Tuple{Vertex_, K_, Edge_}, Float64} - return A .+ B - end + @field_operator function fo_offset_array(A::Field{Tuple{Vertex_, K_}, Float64}, B::Field{Tuple{K_, Edge_}, Float64})::Field{Tuple{Vertex_, K_, Edge_}, Float64} + return A .+ B + end + + @test @to_py fo_offset_array(A, B, backend="py", out=out) -@test @to_py fo_offset_array(A, B, backend="py", out=out) + # ------------------------------------------------- -# ------------------------------------------------- + a = Field(Cell, collect(1.:15.)) + b = Field(Cell, ones(15)) + out = Field(Cell, zeros(15)) -a = Field(Cell, collect(1.:15.)) -b = Field(Cell, ones(15)) -out = Field(Cell, zeros(15)) + @field_operator function nested_fo(a::Field{Tuple{Cell_}, Float64}, b::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + res = fo_addition(a, b) + return res .+ a + end +end -@field_operator function nested_fo(a::Field{Tuple{Cell_}, Float64}, b::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} - res = fo_addition(a, b) - return res .+ a -end \ No newline at end of file +@testset "Testset GT2Py fo exec" test_gt4py_fo_exec() \ No newline at end of file diff --git a/test/gt2py_fo_parser.jl b/test/gt2py_fo_parser.jl index 0353215..e56dacc 100644 --- a/test/gt2py_fo_parser.jl +++ b/test/gt2py_fo_parser.jl @@ -1,119 +1,122 @@ +using Test +using GridTools + + +include("mesh_definitions.jl") struct TestFailedException <: Exception message::String end -function to_py(expr::Expr) +function to_py(field_operator::GridTools.FieldOp) try - py_field_operator(expr, @__MODULE__) + GridTools.py_field_operator(field_operator) return true catch e - throw(TestFailedException("The following test: $(namify(expr)) encountered the following error:")) + throw(TestFailedException("The following test: $(field_operator.name) encountered the following error: $e")) end end - -@testset "Testset Arithmetic Operators" begin - - expr = :(function test_addition(f::Field{Tuple{Cell_}, Int64}, g::Field{Tuple{Cell_}, Int64}) - return f + g - end) - @test to_py(expr) - - expr = :(function test_b_addition(f::Field{Tuple{Cell_}, Int64}, g::Field{Tuple{Cell_}, Int64}) +function test_arithmetic_operators() + expr = @field_operator function test_b_addition(f::Field{Tuple{Cell_}, Int64}, g::Field{Tuple{Cell_}, Int64}) return f .+ g - end) + end @test to_py(expr) - expr = :(function test_bit_and(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) + expr = @field_operator function test_bit_and(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) return f .& g - end) + end @test to_py(expr) - expr = :(function test_bit_xor(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) + expr = @field_operator function test_bit_xor(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) return f .⊻ g - end) + end @test to_py(expr) - expr = :(function test_not(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) + expr = @field_operator function test_not(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) return .~f - end) + end @test to_py(expr) - expr = :(function test_bool_and(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) + expr = @field_operator function test_bool_and(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) return f && g - end) + end @test to_py(expr) - expr = :(function test_bool_or(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) + expr = @field_operator function test_bool_or(f::Field{Tuple{Cell_}, Bool}, g::Field{Tuple{Cell_}, Bool}) return f || g - end) + end @test to_py(expr) end -@testset "Testset function header annotation" begin +@testset "Testset Arithmetic Operators" test_arithmetic_operators() - expr = :(function test_no_annotation(inp) - return inp - end) - @test_throws TestFailedException to_py(expr) +@testset "Testset function header annotation" begin + @test_throws AssertionError begin + expr = @field_operator function test_no_annotation(inp) + return inp + end + to_py(expr) + end # Type annotation error - expr = :(function test_no_annotation_kwargs(f::Field{Tuple{Cell_}, Float64}; g) - return g - end) - @test_throws TestFailedException to_py(expr) + @test_throws AssertionError begin + expr = @field_operator function test_no_annotation_kwargs(f::Field{Tuple{Cell_}, Float64}; g) + return g + end + to_py(expr) + end - expr = :(function test_simple_annotation(inp::Integer) + expr = @field_operator function test_simple_annotation(inp::Integer) return inp - end) + end @test to_py(expr) - expr = :(function test_kwargs(inp::Integer ; inp2::AbstractFloat) + expr = @field_operator function test_kwargs(inp::Integer; inp2::AbstractFloat, inp3::AbstractFloat) return inp - end) + end @test to_py(expr) - expr = :(function test_return_annotation(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_return_annotation(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} return f - end) + end @test to_py(expr) # Type deduction error - expr = :(function test_incompatible_types(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_incompatible_types(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Float64} return f .+ g - end) + end @test_throws TestFailedException to_py(expr) # Return statement error - expr = :(function test_no_return_stmt(f::Field{Tuple{Cell_}, Float64}; g::Field{Tuple{Cell_}, Int64}) + expr = @field_operator function test_no_return_stmt(f::Field{Tuple{Cell_}, Float64}; g::Field{Tuple{Cell_}, Int64}) g .+ 1 - end) + end @test_throws TestFailedException to_py(expr) end @testset "Testset Preprocessing" begin - expr = :(function test_multi_assign(f::Field{Tuple{Cell_}, Float64}) + expr = @field_operator function test_multi_assign(f::Field{Tuple{Cell_}, Float64}) a, b = 5., 6. return f .+ a .- b - end) + end @test to_py(expr) - expr = :(function test_renaming_variables(f::Field{Tuple{Cell_}, Float64}) - tmp = 1 - tmp = 2 + tmp - tmp = 3 + tmp + expr = @field_operator function test_renaming_variables(f::Field{Tuple{Cell_}, Float64}) + tmp = 1. + tmp = 2. + tmp + tmp = 3. + tmp return tmp - end) + end @test to_py(expr) - expr = :(function test_ternary_expr(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_ternary_expr(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} return 1 < 2 ? f : g - end) + end @test to_py(expr) - expr = :(function test_renaming_conditional(f::Field{Tuple{Cell_}, Int64}) + expr = @field_operator function test_renaming_conditional(f::Field{Tuple{Cell_}, Int32}) if 1. .< 10. tmp = f .+ 1 elseif 1. < 10. @@ -122,10 +125,10 @@ end tmp = f .+ 1 end return tmp - end) + end @test to_py(expr) - expr = :(function test_renaming_nested_conditional(f::Field{Tuple{Cell_}, Int64}, g::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Int64} + expr = @field_operator function test_renaming_nested_conditional(f::Field{Tuple{Cell_}, Int32}, g::Field{Tuple{Cell_}, Int32})::Field{Tuple{Cell_}, Int32} tmp = f if 1. .< 10. tmp = f .+ 1 @@ -146,54 +149,54 @@ end tmp = tmp ./ 1 end return tmp - end) + end @test to_py(expr) - expr = :(function test_compair_chain(f::Field{Tuple{Cell_}, Int64}, g::Field{Tuple{Cell_}, Int64})::Field{Tuple{Cell_}, Bool} + expr = @field_operator function test_compair_chain(f::Field{Tuple{Cell_}, Int32}, g::Field{Tuple{Cell_}, Int32})::Field{Tuple{Cell_}, Bool} return 1 < 10 .< f .< g .< 50 > -1 - end) + end @test to_py(expr) # Only constants in condition, Should this actually fail? TODO - expr = :(function test_if_with_field(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_if_with_field(f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} return f .< 2 ? f : g - end) + end @test_throws TestFailedException to_py(expr) end @testset "Test built-ins and closure variables" begin - expr = :(function test_where(mask::Field{Tuple{Cell_}, Bool}, f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_where(mask::Field{Tuple{Cell_}, Bool}, f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} return where(mask, f, g) - end) + end @test to_py(expr) # Error due to non boolean mask argument - expr = :(function test_where(mask::Field{Tuple{Cell_}, Bool}, f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} + expr = @field_operator function test_where(mask::Field{Tuple{Cell_}, Bool}, f::Field{Tuple{Cell_}, Float64}, g::Field{Tuple{Cell_}, Float64})::Field{Tuple{Cell_}, Float64} return where(f, f, g) - end) + end @test_throws TestFailedException to_py(expr) - expr = :(function test_maxover(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Edge_}, Float64} + expr = @field_operator function test_maxover(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Edge_}, Float64} return max_over(f(E2C[1]), axis=K) - end) + end @test to_py(expr) - expr = :(function test_neighborsum(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Edge_, K_}, Float64} + expr = @field_operator function test_neighborsum(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Edge_, K_}, Float64} return neighbor_sum(f(E2C), axis=E2CDim) - end) + end @test to_py(expr) - expr = :(function test_broadcast(f::Field{ Tuple{Cell_}, Float64}) + expr = @field_operator function test_broadcast(f::Field{ Tuple{Cell_}, Float64}) return broadcast(f, (Cell, K)) - end) + end @test to_py(expr) - expr = :(function test_julia_builtin(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} + expr = @field_operator function test_julia_builtin(f::Field{Tuple{Cell_, K_}, Float64})::Field{Tuple{Cell_, K_}, Float64} return sin.(f) - end) + end @test to_py(expr) end diff --git a/test/mesh_definitions.jl b/test/mesh_definitions.jl new file mode 100644 index 0000000..51fdaa4 --- /dev/null +++ b/test/mesh_definitions.jl @@ -0,0 +1,25 @@ +const global Cell_ = Dimension{:Cell_, HORIZONTAL} +const global K_ = Dimension{:K_, HORIZONTAL} +const global Edge_ = Dimension{:Edge_, HORIZONTAL} +const global Vertex_ = Dimension{:Vertex_, HORIZONTAL} +const global V2VDim_ = Dimension{:V2VDim_, LOCAL} +const global V2EDim_ = Dimension{:V2EDim_, LOCAL} +const global E2VDim_ = Dimension{:E2VDim_, LOCAL} +const global E2CDim_ = Dimension{:E2CDim_, LOCAL} +const global C2EDim_ = Dimension{:C2EDim_, LOCAL} +const global Cell = Cell_() +const global K = K_() +const global Edge = Edge_() +const global Vertex = Vertex_() +const global V2VDim = V2VDim_() +const global V2EDim = V2EDim_() +const global E2VDim = E2VDim_() +const global E2CDim = E2CDim_() +const global C2EDim = C2EDim_() + +const global V2V = FieldOffset("V2V", source=Vertex, target=(Vertex, V2VDim)) +const global E2V = FieldOffset("E2V", source=Vertex, target=(Edge, E2VDim)) +const global V2E = FieldOffset("V2E", source=Edge, target=(Vertex, V2EDim)) +const global E2C = FieldOffset("E2C", source=Cell, target=(Edge, E2CDim)) +const global C2E = FieldOffset("C2E", source=Edge, target=(Cell, C2EDim)) +const global Koff = FieldOffset("Koff", source=K, target=K) \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 3d68f83..1d59d83 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,39 +1,13 @@ -# push!(LOAD_PATH, "/Users/jeffreyzweidler/Documents/CSCS/GridTools/src") +# push!(LOAD_PATH, "/Users/jeffreyzweidler/Documents/CSCS/GridTools/src")dlopen(PyCall.libpython, RTLD_GLOBAL) using GridTools using Test using OffsetArrays -include("../src/gt2py/gt2py.jl") +#include("../src/gt2py/gt2py.jl") # Setup -------------------------------------- -Cell_ = Dimension{:Cell_, HORIZONTAL} -K_ = Dimension{:K_, HORIZONTAL} -Edge_ = Dimension{:Edge_, HORIZONTAL} -Vertex_ = Dimension{:Vertex_, HORIZONTAL} -V2VDim_ = Dimension{:V2VDim_, LOCAL} -V2EDim_ = Dimension{:V2EDim_, LOCAL} -E2VDim_ = Dimension{:E2VDim_, LOCAL} -E2CDim_ = Dimension{:E2CDim_, LOCAL} -C2EDim_ = Dimension{:C2EDim_, LOCAL} -Cell = Cell_() -K = K_() -Edge = Edge_() -Vertex = Vertex_() -V2VDim = V2VDim_() -V2EDim = V2EDim_() -E2VDim = E2VDim_() -E2CDim = E2CDim_() -C2EDim = C2EDim_() - -V2V = FieldOffset("V2V", source=Vertex, target=(Vertex, V2VDim)) -E2V = FieldOffset("E2V", source=Vertex, target=(Edge, E2VDim)) -V2E = FieldOffset("V2E", source=Edge, target=(Vertex, V2EDim)) -E2C = FieldOffset("E2C", source=Cell, target=(Edge, E2CDim)) -C2E = FieldOffset("C2E", source=Edge, target=(Cell, C2EDim)) -Koff = FieldOffset("Koff", source=K, target=K) - @testset "GridTools tests" begin @testset "Embedded Testsuit" begin @@ -44,4 +18,4 @@ Koff = FieldOffset("Koff", source=K, target=K) include("gt2py_fo_parser.jl") include("gt2py_fo_exec.jl") end -end +end \ No newline at end of file