Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt py2fgen for gt4py programs #371

Merged
merged 24 commits into from
Feb 28, 2024
Merged

Conversation

samkellerhals
Copy link
Contributor

@samkellerhals samkellerhals commented Jan 31, 2024

Description

I've refactored py2fgen to enhance its functionality and integrate it more seamlessly with Fortran.

Key changes include:

  • Integration Tests: Added tests with Fortran driver codes for straightforward scenarios to ensure that code generated with py2fgen compiles, runs as expected from Fortran, and delivers the anticipated results.

  • Python Wrapper Function Generation: Modified py2fgen to automatically generate a Python wrapper function for the exposed Python function, applicable to both gt4py.Program and Simple Python function types.

  • New Options for Debug Mode and gt4py Backend Selection: Introduced options to enable debug mode and select the gt4py backend. See the README for more details.

  • Singular Function Binding Generation: Altered the tool to generate bindings and libraries for one function at a time, streamlining the process.

  • Removal of @CffiMethod and @to_fields Decorators: These decorators have been removed to simplify the usage and enhance the tool's compatibility.

Future Work

  • Expand the test suite, the code coverage is quite low, and we need to test more edge cases.
  • Determine what the overhead is of invoking a Python interpreter from CFFI-Fortran.
  • Test if this works with more complex cases, for example the Diffusion granule.
  • Check if we can determine array size in the f90 interface using SIZE similar to how it is being handled in the bindings generator here:
    right_side=f"SIZE({field.name}, 2)",

@samkellerhals samkellerhals marked this pull request as draft January 31, 2024 11:23
@samkellerhals samkellerhals changed the title Adapt py2fgen for single functions and gt4py programs Adapt py2fgen for gt4py programs Jan 31, 2024
@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

@samkellerhals samkellerhals marked this pull request as ready for review February 19, 2024 16:43
@samkellerhals
Copy link
Contributor Author

launch jenkins spack

Copy link

Mandatory Tests

Please make sure you run these tests via comment before you merge!

  • cscs-ci run default
  • launch jenkins spack

Optional Tests

To run benchmarks you can use:

  • cscs-ci run benchmark

To run tests and benchmarks with the DaCe backend you can use:

  • cscs-ci run dace

In case your change might affect downstream icon-exclaim, please consider running

  • launch jenkins icon

For more detailed information please look at CI in the EXCLAIM universe.

@samkellerhals
Copy link
Contributor Author

cscs-ci run default

Copy link
Contributor

@halungge halungge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets discuss it, I am not sure I did understand everything.

def _parse_function(module: ModuleType, function_name: str) -> Func:
func = unwrap(getattr(module, function_name))
is_gt4py_program = isinstance(func, Program)
type_hints = _extract_type_hint_strings(module, func, is_gt4py_program, function_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you relay on the type hints being GT4Py type hints, even for non-program case? If yes, you should add that as a restriction to the README.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



class CffiPlugin(Node):
module_name: str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make these (frozen) dataclasses? Or is this already the case from the Node inheritance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it makes sense for these to be frozen dataclasses as the attributes change after initialisation post_init etc. For the purposes of code generation with eve I think just creating subclasses of eve.Nodes is generally the right approach.

# Allocate GT4Py Fields
{% for arg in _this_node.function.args %}
{% if arg.is_array %}
{{ arg.name }} = np_as_located_field({{ ", ".join(arg.gtdims) }})({{ arg.name }})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this one (np_as_located_field ) deprecated and we should use as_field instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree in principle however when I tried using as_field for some reason the data that was returned when I tried to print the arrays on the Python side was just filled with garbage values. I'm not sure if I used as_field wrongly before or if some implementation detail changed between those two functions on the gt4py side but it is definitely something I would like to investigate in the new cycle project (will add it to the hackmd doc).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They changed the representation of the buffers. Before it Field[CellDim, KDim] was in C like style, the k had stride 1, now it is the otherway round. That needed a fix in GHEX.

from gt4py.next.iterator.embedded import np_as_located_field
from gt4py.next.program_processors.runners.gtfn import run_gtfn, run_gtfn_gpu
from gt4py.next.program_processors.runners.roundtrip import backend as run_roundtrip
from icon4py.model.common.grid.simple import SimpleGrid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the BaseGrid here? You cannot instantiate it later, I guess. But when running the diffusion you will need the IconGrid. The instantiation needs to be injected somehow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could maybe use the same pattern as for the backends.

Copy link
Contributor Author

@samkellerhals samkellerhals Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand, isn't the grid instantiated inside the function that runs the diffusion granule code? e.g.

grid = IconGrid().with_config(mesh_config).with_connectivities(connectivities)

With this PR I think the focus should just be on these two simple use cases (square, multi_return), once we want to support running the granules as well we can make the necessary changes to the code generation. However I first need to understand how to call the diffusion code from Python correctly myself. I've updated the hackmd here: https://hackmd.io/Ri-3S3joQ5SebR5A3_rH5g#Granule-CFFI-Plugin-Generation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You import the SimpleGrid and then later on in the template you have a grid = SimpleGrid() So that only works when you run your function on the simple grid. If you only used the common base class (that is only rely on the interface of the grid, it would work with whatever implementation you need. But maybe the the template is not meant that general.

tools/src/icon4pytools/py2fgen/utils.py Show resolved Hide resolved
@samkellerhals
Copy link
Contributor Author

launch jenkins spack

Copy link
Contributor

@halungge halungge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I approve, as we discussed for me its fine to merge, since you continue to work on it any. You should have a look at the as_field again, maybe the changed representation hit you there.

# Allocate GT4Py Fields
{% for arg in _this_node.function.args %}
{% if arg.is_array %}
{{ arg.name }} = np_as_located_field({{ ", ".join(arg.gtdims) }})({{ arg.name }})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They changed the representation of the buffers. Before it Field[CellDim, KDim] was in C like style, the k had stride 1, now it is the otherway round. That needed a fix in GHEX.

from gt4py.next.iterator.embedded import np_as_located_field
from gt4py.next.program_processors.runners.gtfn import run_gtfn, run_gtfn_gpu
from gt4py.next.program_processors.runners.roundtrip import backend as run_roundtrip
from icon4py.model.common.grid.simple import SimpleGrid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You import the SimpleGrid and then later on in the template you have a grid = SimpleGrid() So that only works when you run your function on the simple grid. If you only used the common base class (that is only rely on the interface of the grid, it would work with whatever implementation you need. But maybe the the template is not meant that general.

@samkellerhals samkellerhals merged commit 5d94f3e into main Feb 28, 2024
5 checks passed
@samkellerhals samkellerhals deleted the py2f-integration-testing branch February 28, 2024 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants