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

Fixed logical evaluation of PRESENT intrinsics on Array variables #341

Merged
merged 9 commits into from
Aug 13, 2024

Conversation

JoeffreyLegaux
Copy link
Contributor

@JoeffreyLegaux JoeffreyLegaux commented Jul 3, 2024

During inlining_member_procedures, PRESENT intrinsics are evaluated according the the actual presence of the concerned variables in the caller arguments.

However, this evaluation always returns false when evaluating Array variables.

This is due to the fact that we compare the array in the PRESENT call which has only the array's name, with the array declaration (from call.arg_map) which includes the dimensions.

I propose to correct that by checking the existence of the variable in the list of variables names from call.arg_map, instead of checking directly in the variables from call.arg_map.

@JoeffreyLegaux
Copy link
Contributor Author

Example of routine with member call

! test_inline.F90
SUBROUTINE TEST_INLINE(YDXFU, YDCPG_OPTS,  YDMF_PHYS_OUT)

USE CPG_OPTS_TYPE_MOD, ONLY : CPG_OPTS_TYPE
USE PARKIND1         , ONLY : JPIM, JPRB
USE YOMXFU           , ONLY : TXFU
USE MF_PHYS_TYPE_MOD , ONLY : MF_PHYS_OUT_TYPE

IMPLICIT NONE

TYPE(TXFU)              ,INTENT(INOUT)            :: YDXFU
TYPE(CPG_OPTS_TYPE)     ,INTENT(IN)               :: YDCPG_OPTS
TYPE(MF_PHYS_OUT_TYPE)  ,INTENT(IN)               :: YDMF_PHYS_OUT
 

IF (YDXFU%YVISICLD%LACTIVE) THEN
  CALL MEMBER_ROUT (YDXFU%VISICLD, PVMIN=YDMF_PHYS_OUT%VISICLD, PSMAX=0.0_JPRB, PSMIN=-1.0_JPRB)
ENDIF


CONTAINS

SUBROUTINE MEMBER_ROUT (X, PVMIN, PVMAX, PSMIN, PSMAX)

REAL(KIND=JPRB)         ,INTENT(INOUT)            :: X(1:YDCPG_OPTS%KLON)
REAL(KIND=JPRB)         ,INTENT(IN)    ,OPTIONAL  :: PVMIN(1:YDCPG_OPTS%KLON)
REAL(KIND=JPRB)         ,INTENT(IN)    ,OPTIONAL  :: PVMAX(1:YDCPG_OPTS%KLON)
REAL(KIND=JPRB)         ,INTENT(IN)    ,OPTIONAL  :: PSMIN
REAL(KIND=JPRB)         ,INTENT(IN)    ,OPTIONAL  :: PSMAX

IF (PRESENT (PSMIN)) THEN
  X = X + PSMIN
ELSEIF (PRESENT (PSMAX)) THEN
  X = X + PSMAX
ELSEIF (PRESENT (PVMIN)) THEN
  X = X + MINVAL(PVMIN(:))
ELSEIF (PRESENT (PVMAX)) THEN
  X = X + MAXVAL(PVMAX(:))
ENDIF

END SUBROUTINE MEMBER_ROUT 

END SUBROUTINE TEST_INLINE

Example loki script that applies inlining

from loki import Frontend, Sourcefile
from loki.transformations import inline_member_procedures, resolve_associates

source = Sourcefile.from_file('./test_inline.F90', frontend=Frontend.FP)

f = open('./test_inline_transfo.F90', 'w')

for routine in source.subroutines:
    inline_member_procedures(routine)
    f.write(routine.to_fortran())
f.write('\n')
f.close()

Without the proposed change, PRESENT(PVMIN) becomes .false. in the transformed routine.

Copy link
Collaborator

@reuterbal reuterbal left a comment

Choose a reason for hiding this comment

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

Thanks for this, nice find and great reproducer!

The change looks good in principal but I suspect we may have an issue if the casing in the present check doesn't match the routine argument. You can check this, e.g., using PRESENT(psmin) and PRESENT(pvmin) in your reproducer. I left a suggestion to work around this as a comment but this is untested!

To make sure we capture this behaviour in the tests, can I please ask that you add your reproducer as a test in https://github.com/ecmwf-ifs/loki/blob/main/loki/transformations/tests/test_inline.py?

Take a look at the other tests in there to draw inspiration how to implement this and test that the outcome is what you expect. Ideally, make sure the test fails without your change in the transformation and then passes after applying the change.

Thanks and let me know if something is unclear.

loki/transformations/inline.py Outdated Show resolved Hide resolved
Copy link

codecov bot commented Jul 3, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.36%. Comparing base (abf7968) to head (65b58a1).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #341   +/-   ##
=======================================
  Coverage   95.36%   95.36%           
=======================================
  Files         173      173           
  Lines       36902    36914   +12     
=======================================
+ Hits        35191    35204   +13     
+ Misses       1711     1710    -1     
Flag Coverage Δ
lint_rules 96.39% <ø> (ø)
loki 95.35% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@reuterbal reuterbal left a comment

Choose a reason for hiding this comment

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

Many thanks for adding the test and verifying the case-insensitivity! Some remarks to make the tests pass successfully, otherwise this is good to go!

@@ -415,7 +415,8 @@ def _map_unbound_dims(var, val):
check for check in FindInlineCalls().visit(callee.body) if check.function == 'PRESENT'
)
present_map = {
check: sym.Literal('.true.') if check.arguments[0] in call.arg_map else sym.Literal('.false.')
check: sym.Literal('.true.') if check.arguments[0] in [arg.name for arg in call.arg_map]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pylint flagged a trailing white space here

@@ -706,6 +706,60 @@ def test_inline_member_routines_with_associate(frontend):
assert len(assocs) == 2


@pytest.mark.parametrize('frontend', available_frontends())
def test_inline_member_routines_with_optionals(frontend):
Copy link
Collaborator

Choose a reason for hiding this comment

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

OMNI currently fails this test, highlighting that the source is not valid fortran at the moment:

 INFO     Loki:logging.py:123 [Loki::OMNI] Parsing /tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90
ERROR    Loki:logging.py:123 Error: Execution of F_Front failed:
ERROR    Loki:logging.py:123   Full command: F_Front -fleave-comment /tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90
ERROR    Loki:logging.py:123   Output of the command:


"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 5: "klev" is not a dummy argument.
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 5: "kidia" is not a dummy argument.
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 5: "kfdia" is not a dummy argument.
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 5: "ktdia" is not a dummy argument.
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 7: type txfu not found
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 7: Invalid type spec
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 8: type mf_phys_out_type not found
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 8: Invalid type spec
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 10: invalid left operand of '%'
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 10: invalid left operand of '%'
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 21: type toto not found
"/tmp/loki/10428_os7au7hp/omni-a61442a405c4f93e25410eedc4838fd9.f90", line 21: Invalid type spec

In particular, klev, kidia, kfdia, ktdia, and ydmf_phys_out are missing from the subroutine interface, and we should include dummy imports of txfu and mf_phys_out_type. That makes this at least fully correct Fortran code.
OMNI will then still fail the test because it is going to lack the relevant type definitions, so it is fine to exclude it from the frontend parameterisation as xfail.

real(kind=8) ,intent(in) ,optional :: pvmax(1:klon)
real(kind=8) ,intent(in) ,optional :: psmin
real(kind=8) ,intent(in) ,optional :: psmax
type(toto), intent(in), optional :: dfmin
Copy link
Collaborator

Choose a reason for hiding this comment

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

dfmin should also be added to the interface of member_rout, and a corresponding import of toto should be added.

Copy link
Collaborator

@mlange05 mlange05 left a comment

Choose a reason for hiding this comment

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

@JoeffreyLegaux Many thanks for the fixes. This looks good now and should be ready to go.

@mlange05 mlange05 added the ready for merge This PR has been approved and is ready to be merged label Aug 12, 2024
@mlange05 mlange05 merged commit a0207ce into ecmwf-ifs:main Aug 13, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for merge This PR has been approved and is ready to be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants