-
Notifications
You must be signed in to change notification settings - Fork 17
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
Sebastianwolf/error checking #45
base: master
Are you sure you want to change the base?
Sebastianwolf/error checking #45
Conversation
* There are two debugging files in fastscapelib-fortran/src. The codes are in c and fortran and there is an accompanying Makefile. * code compiles with gnu * Error-checking changes a context-variable, instead of a variable that is local to each function. * Fastscape_run.c contains an error (setup is called before set_nx_ny) to showcase the error-handling
…h indexing. Error 1 gives none. Either remove none, or put index +1.
…ing either abort or return
…o sebastianwolf/ErrorChecking
* Make exception output more compact and readable * Added addition exception types
…o sebastianwolf/ErrorChecking
* Added ierr to Diffusion and checked that it works. * Note that if you define ierr=0 inside tridag, the code produces a segmentation fault. It do not know, why, but it is not necessary anyways. * Added error-checking in debug-c-code * Added errror checking with error.fpp for debug-fortran-code
* Moved setup_not_run errors to api from context. * Now all stop calls in the code are removed and replaced by error-checking
src/FastScape_api.f90
Outdated
end if | ||
FSCAPE_CHKERR(ierr) ! Call FSCAPE_CHKERR() so that all possible exceptions above will be displayed | ||
|
||
call SetUp(ierr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an error. SetUp should not have an argument. Should be SetUp(). Sorry for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pushed update
Thanks a lot @sebastianwolf and @hpc4geo for working on this! In general, I like the approach. I'll be able to reuse I let @jeanbraun comment on the change of the signature of the API functions, since I'm not using them much. I don't have strong opinions on this, however as a maintainer I'm still a bit concerned by those breaking changes. Do you think that we could make those We're already using @sebastianwolf could you merge the master branch here or rebase this branch on top of master, please? I have updated the CI configuration in #46 so it will be easier to see if the changes made here break something or not. |
Would you mind adding fastscapelib-fortran/CMakeLists.txt Line 15 in 508d4c3
|
…-ffree-line-length-none option gives unlimited line length.
I added ErrorCodes to the compile list and pushed the option to allow for macros longer than 132 chars. Still there is a compilation error. Seems like the std=f2008 option is relatively strict on syntax. Removing it might solve the problem. |
Ah I see, I need to merge #47 and then we need to merge (rebase onto) master here, as the line length flag that you added has no effect ( |
Mmm so there's two CI errors:
We need to set So we would need a special case for this, or maybe a cleaner solution would be to add continuation lines in the code where this is needed instead of setting
Update every call to FastScape API functions with an I've tried something like this in integer, optional, intent(out):: ierr
if (present(ierr)) ierr = 0 But this doesn't seem to work ( I guess it would be nice and cleaner to put all api functions in a module? It would still be a breaking change (users would need to insert a I'm far from being an expert in Fortran, though, so I might be missing other possible solutions. |
Quick and dirty fix for Python builds that should work: if(CMAKE_Fortran_COMPILER_ID MATCHES "Flang|GNU")
...
set(pythonflags "-ffree-line-length-none")
endif()
...
# let F2PY (scikit-build) configure dialect flags for F77 and F90 source
# (some source files are generated by F2PY)
if(SKBUILD)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${pythonflags}")
else()
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${dialect} ${ioflags}")
endif()
|
Yes I like a strong recommendation like that :-) ! As a quick test, I've wrapped all subroutines in @sebastianwolf, let me know if you want me to do something. I can submit a PR or commit directly to your branch if that's easier. |
Very good. The 'optional' and specific error message sounds like a good idea. @benbovy you can just directly work on my branch. Not sure you need write permissions on my fork, but I'll give them to you anyways. |
Mimic default value for optional ierr argument + return/stop based on whether ierr is provided
In the last commits I added two macros that handle the now optional The new requirement in external codes using the fastscape's API is to add Everything looks good in CI, except for the python bindings build on windows (Flang doesn't seem to support the same flags than gfortran for relaxing line length constraints). I haven't run all examples, though. |
see: flang-compiler/flang#342 Flang apparently does not support well long line lengths, and it is used here (v5.0) for shipping Python bindings with conda on Windows. I haven't found how to insert Fortran continuation lines in macros without any compile error (is that possible?), so I tried here to split output messages into several print statements to hopefully avoid too long code lines.
It's better to do something more idiomatic on the Python side with the returned error codes (e.g., using `raise`). Also, this should hopefully fix too long code lines for Flang/Windows.
All CI is green now, and everything looks good to me. @sebastianwolf @hpc4geo are you happy with the last changes? Do not hesitate to drop comments if you feel that something could be improved. Is there anything you'd like to add to the TODO list below?
I suggest that we also wait for Jean's feedback before merging this (he's busy with EGU this week, maybe you are participating too?). |
Thanks a lot, @benbovy . I pulled your changes and did some tests as well. I have my custom makefile and one f90 and one c code that both run a simple fastscape setup for 500 timesteps. These codes simulate how Fastscape would be called from another program, like a thermo-mechanical code. I pushed a branch to my fork called sebastianwolf/MakeNRunfile_for_ErrorChecking. You can see and get these files there. Some observations/problems:
|
src/Error.fpp
Outdated
if (present(ierr)) then; \ | ||
ierr = ierr_; \ | ||
else; \ | ||
print '(A)', "*** Depreciation warning *** "; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benbovy This should read "Deprecation" not "Depreciation"
src/Error.fpp
Outdated
ierr = ierr_; \ | ||
else; \ | ||
print '(A)', "*** Depreciation warning *** "; \ | ||
print '(A,A,A)', "Calling ", TRIM((fname)), " without 'ierr' argument (integer) is depreciated! Please update your code!"; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benbovy This should read "deprecated" not "depreciated"
Calling Fortran from C
Yes I think it's really beneficial if we want to expose the subroutines interfaces to 3rd-party codes (I think in general we should). Without those interfaces, it would not be possible to make That said, you can control the function name (among other things) exposed to C by using Fortran ISO C Binding, like explained here. You could also set a custom name with I'd be open to add C interoperability here, although:
Otherwise, it should still be possible for you to write a small Fortran wrapper file with C-interoperable versions of the subset of API functions that you need to call from your C code. VTK issuesI see that Could you try setting the The problem with setting Another possibility would be to refactor the VTK routines and switch from legacy (binary) VTK format to the XML (binary) VTK format (the latter allows setting the endianess in the file header). We could use https://github.com/szaghi/VTKFortran, but that's probably overkill to depend on that library. |
One other impact of using a module for the API routines is that those are now moved in the import fastscapelib_fortran as fs becomes import fastscapelib_fortran
fs = fastscapelib_fortran.fastscapeapi |
On Wed, 28 Apr 2021 at 10:32, Benoit Bovy ***@***.***> wrote:
One other impact of using a module for the API routines is that those are
now moved in the fastscapelib_fortran.fastscapeapi namespace for the
Python bindings. I think it's ok to have this breaking change as I
recommend using https://github.com/fastscape-lem/fastscape, which is
built on top of this library. For those who are still using the low-level
bindings here it's easy to create an alias to avoid too much refactoring of
existing code:
import fastscapelib_fortran as fs
becomes
import fastscapelib_fortran
fs = fastscapelib_fortran.fastscapeapi
Doesn't this choice of names defeat the purpose of namespacing?
Wouldn't this be better
fs = fastscapelib_fortran.api
?
Otherwise we'd end up with
numpy.numpylinalg.linalgsvd
as opposed to
numpy.linalg.svd
I am not sure I completely agree with the choice to put the API functions
into a dedicated module.
The whole point of those API functions was to have a simple way to have a
persistent fscape object accessible to C without having to deal with
special bindings.
How you pack those API functions up into a python object seems like
something which should be confined to only the python side.
Can I ask again what was the rational behind putting all the API functions
in a module? I re-read the comments / discussion and seems like it was to
avoid pain with users having to add args in subroutine calls. But you have
a nice way to catch / check this and report a meaningful error. So if the
missing subroutine arg check is robust, does the justification for putting
APIs in a module still make sense?
Sorry if I am missing something deeper in the logic.
—
… You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#45 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADO6RJ4HQU6LPDP4E6JM7Z3TK7BZRANCNFSM43KREPGQ>
.
|
Yes it would be much nicer. We could easily create an alias in
I think it follows much more the modern Fortran idioms and therefore has a lot of benefits when reused in other Fortran codes. Without the module, from my understanding there's no way for the compiler to be aware of the routine interfaces, hence the missing subroutine arg check is just not possible ( But yeah, there are certainly trade-offs when we want to reuse the code in other languages. What we could do is define the module separately (with only an interface block), so that we keep both advantages, i.e., using the module in Fortran codes and just using the plain subroutines in C. We would have to specify the interface of each subroutine twice, which is not ideal, but I'm not sure how we can avoid it if we want to make things good and flexible enough in Fortran, C and Python. Alternatively, it would perhaps make more sense to create a specific module for C-interoperability, rather than treat reusability in Fortran as a special case. We could have a module fastscapeapi_c
use iso_c_binding
interface
subroutine FastScape_Init(ierr) bind(C, name='c_fastscape_init')
use iso_c_binding, only: c_int
implicit none
integer(c_int), intent(out) :: ierr
end subroutine FastScape_Init
...
end interface
end module fastscapeapi_c |
Hmm so the Other possible options:
module fastscapeapi_c
use fastscapeapi
use iso_c_binding
contains
subroutine c_fastscape_init(ierr) bind(C)
use iso_c_binding, only: c_int
implicit none
integer(c_int), intent(out) :: ierr
call FastScape_Init(ierr)
end subroutine c_fastscape_init
end module fastscapeapi_c Pros: flexible and robust in all languages
Pros: we don't need to create a separate module for C-interoperable routines.
Pros: not much breaking changes. |
Thanks for the explanations, @benbovy. Just a quick update on the VTK problem:
I tested this and it works. Adding |
Good to know, thanks for testing! |
I just wanted to up this thread here again so that we can converge towards a solution :). If I understand correctly, the largest advantage of putting the api into a module is to make the ierr-arg optional. An optional arg can provide the user with a message to update the code and does not result in a seg fault. Wraping the api into a module creates the disadvantage of having mangled routine names that depend on the compiler used. To avoid mangled names, Benoit proposed three options.
If the api goes into a module, which there seems to be good reasons for, then using the iso_c_binding option seems reasonable. So therefore, if a module for the api is used, the simplest solution might be number i). However, any solution is fine with me. Any other thoughts?! |
Agreed @sebastianwolf let's move this forward! Choosing one of the options above is not straightforward. We'll need to make tradeoffs since:
That's a lot of things to consider! :-) Apart from using Otherwise, I think that option i) may be the cleanest one for exposing the API routines to C. Any other thoughts are welcome to help us choosing the best option |
Very good.
I'll test this and report back :). |
…ng from c are the same as the fortran functions, only with lowercase letters. Successfully tested api with legacy pgi and gnu compilers.
Hei guys, I implemented and tested option ii (one interface, optional ierr arg, iso_c binding) and it worked fine also with legacy pgi compilers. The changes are pushed to this branch. I chose the name of each function in c to be the same as the fortran-function, but in lower-case only. I figured that this is a good option. I don't know why two of the tests here failed. Could you have a look at these, @benbovy ? |
I'm not sure if this might be useful for the project, but I have written a (dirty) Python parser which will automatically generate the C function prototypes (and a C header file) directly from the contents of FastScape_api.f90. |
Dave and me (Dave was the main brain :) implemented error-checking in FastScape. See mail for additional information.
Error-checking and raising is done using macros defined in the pre-processor-file Error.fpp.
Some compilation information:
We used gnu-compilers and an extra Makefile to compile and run the code. You can have the makefile if it helps you, but it is not included in the pull request. You can find it if you go back some commits in the branch that I PRed.
Gnu needed -cpp and -ffree-line-length-none for the code to compile. The -cpp option enables pre-processing, the -ffree-line-length-none option is necessary to allow for long lines. The macros are considered as one-liner.
The latter can make problems with legacy pgi-compilers. Pre-processor option for pgi is -Mpreprocess, but if the pgi compiler is older than somewhere 2018, there is a maximum linelength of 132 signs. Newer pgi-compilers apparently have 2k allowed signs per line (as far as I could find on the world wide web). If one wants to use these old compilers, some write statements in the Error.fpp file most be shortened. This is not an issue, I think. Not many people will use old pgi. We have it on one of our servers, so I tested and found it there.
I did not check with the intel compilers.
Error codes are defined in a new file src/FastScapeErrorCodes.f90 this needs to be added to the compilation files.
How does it work?
The main change, and I hope you will like it, is that every api function got one more integer argument ‘ierr’. However, only those ctx-functions that had a stop call got the error argument as well, so that the heart of the code only has minor additions. We tried to make minimal essential and non-invasive changes.
Example:
FastScape_Init(ierr) —> got new arg, same as all other api-funcs
calls Init() —> has no arg as error can not be raised in this function.
FastScape_Execute_Step(ierr) —> got new arg, same as all other api-funcs
...
Diffusion (ierr);FSCAPE_CHKERR(ierr) —> got new arg, as solver function tridag can raise error
…
Once an error is raised, the subroutine is returned early and the error propagated upwards. The macros and error-raising are really powerful, because errors trace back the file, and the line where the error was raised.
List of functions that got new ierr-arg:
I was a bit concerned that you might not like that all api functions got another argument, because this might make existing examples and user-codes non-useable until all functions got an additional argument. Dave convinced me that the way we did it is the best way and helps the project the most. I hope you agree, but of course I’m open for change.