I took a pass at writing a Riemann module based on what I suggested,
see the comments in the attached file.
Hi,
since Aron CCed me, I decided to write my personal experience with Fortran.
Maybe you might find it useful. But I am not suggesting anything for the clawpack project, I am only posting it here for inspiration:
On Monday, April 22, 2013 4:25:03 AM UTC-6, Aron Ahmadia wrote:
> Hi Jed,
>
>
> Thanks for taking a look at the code and sorry for the delay in response, I was off facing 50 mph winds and hail on top of Cadair Idris this weekend :)
>
>
>
> > Aron, why does this use iso_c types everywhere? ... Now if your objective is to export an ABI that is usable without shims from C, Python, and Fortran, what you have is a reasonable approach, but I think it's unnatural as a Fortran API and I think that trying to mix C and Fortran without shims tends to produce crummy interfaces on both sides
>
>
>
> Yes, interoperability was my objective. Because I need interoperable procedures at the outer kernel level and the Riemann kernel level, I modified the interfaces to both the outer solver (step1.f90) and the Riemann kernel (rp1_advection.f90)
In general, I think the best approach is to code in modern Fortran (that is, no C types), and use everything that modern Fortran offers (as long as it is reasonably supported by compilers), in particular array support, allocatable arrays etc., all of which you loose if you stick to iso_c_binding, which only has explicit shape arrays.
But it depends how complicated your program is. If it is just one subroutine, then your approach might make sense. But if it is more subroutines, calling each other (that is typical in my programs/libraries), then I would recommend to write a simple C wrapper for the API that you want to export.
>
>
>
> > If I was writing this primarily for Fortran users, I would use Fortran
> types everywhere except when _library_ code needs to pass the context.
>
> When user code gets the context (as a c_ptr), I would immediately
> promote it to its Fortran type, and I would only reduce it to
>
> c_ptr(param) when passing it as an argument to a library function.
Exactly.
>
>
>
> The "library" code in your statement is the outer solver routine that calls the Riemann solver kernel (step1.f90 and friends), and your 'user' code is the Riemann kernel itself (rp1_advection.f90 and other Riemann kernels). Just to make this more precise, I'm going to use "outer solver", "Riemann kernel", and a third type of code, "driver", which refers to code that sets up the problem domain and solver routines before calling them.
>
>
>
> My understanding is that unlike PETSc, there is pretty equal development in all three types of code, and a new user is equally likely to be working with any of the three code bases, depending on whether they are focusing on existing applications, time integrators, or solving new applications. Of the three sections of code, only the "outer solver" deals with the context as an opaque type. If you look at step1.f90, you'll notice that it leaves context as an opaque type throughout the code. One aspect of the code you may be taking issue with is my choice of passing a C function pointer in to represent the Riemann kernel instead of the native Fortran function pointer? I agree that I could have written the core routine to be native Fortran, but since we are already incorporating ISO C bindings in this routine's interface, we might as well bind the routine to make it interoperable instead of writing a redundant shim function.
>
>
>
> In the Riemann kernel rp1_advection.f90, I immediately move from the opaque c_ptr to a Fortran-native derived type and use it there. The same interface and module is used in the driver routine to set up and destroy the context object (the new and delete methods). I think this is what you're suggesting, so I'm confused as to where exactly your criticism is being directed at. I suspect I'm still missing something here, so I'd appreciate clarification when you've got a few minutes.
Is this the code that we are talking about?
https://github.com/clawpack/riemann/tree/master/src
https://github.com/clawpack/pyclaw/blob/master/src/pyclaw/classic/step1.f90
-------------------------
I personally have been using Python for probably 13 years now, so I am reasonably proficient in it.
I used to have the main thing in Python and call little C kernels from it.
Then I learned modern Fortran, about 2 years ago. Since then I actually just use modern Fortran only, because to me it feels like Python/NumPy, just with types (types and compiler can really help!), and much much faster, for free.
And there is high value in using just one language, rather than a mix.
I wrote this Python/Fortran side by side comparison page:
http://fortran90.org/src/rosetta.html
Here is my Fortran code for inspiration:
https://github.com/certik/hfsolver/tree/master/src
and here is another code (F95 for maximum compatibility) that we published in Computer Physics Communications recently (article is cited in the README):
https://github.com/certik/dftatom
In dftatom I do have optional Python wrappers, but only for the high level functionality.
My experience from the last 2 years is this (this is just my own opinion and I know that some people will not agree with me):
* it's really simple to write very fast code (usually faster than equivalent C++ code, see e.g. my recent test at [1])
* modern Fortran code looks similar to the mathematics it solves (arrays, complex numbers, ...), that really helps
* Debugging and profiling Python+Fortran mix is not worth my time, it's much faster to just use Fortran for everything. Both the result and my development time is faster that way. In particular, the dftatom used to be 100x slower than it is now. We needed it to be as fast as an old legacy optimized f77 codes (see the article for details). It was really tough --- I even suspect that f77 might sometimes be a little faster than f90, but only slightly. In particular, I had to give up Python and just put everything in Fortran to get the job done in my time frame. Things became simpler, faster and more maintainable.
* For any nontrivial numerical code that can be well expressed using arrays (e.g. almost all algorithms that I use), if I need to squeeze the maximum performance, then I would definitely choose Fortran over C or C++.
One such example is this competition that we did some time ago [2].
The goal was to solve some mathematical problem the fastest.
First we all use different algorithms, so it wasn't clear which language is the fastest. But then Jed figured out the fastest algorithm. He coded it in Octave and Julia. Aron coded it in Cython/NumPy. I coded it directly in modern Fortran.
All three versions are very readable and pretty much equivalent, as all Octave, Julia, NumPy and (modern) Fortran have pretty much the same expressiveness.
Nobody tried C or C++, as probably it would take much longer to write.
And the Fortran version beats all other approaches by an order of magnitude.
And that's pretty much my whole point --- if I need to get the fastest code, and I have little time (just like everybody else), then Fortran is the way to go. Also it shows why Python+Cython or NumPy is many times not competitive in my experience if I need raw speed for nontrivial programs.
* I like to use Python for experiments and plotting. I use ipython notebook for that.
* Easy to compile, runs on clusters etc. However, as a Python enthusiast, I do want to help with the Python compilation problem, I do believe it doesn't have to be that hard as it is now to compile mixed Python/C/Fortran code.
Jed and I had a long debate about modern Fortran about a year ago I think. Let's say that he didn't share my enthusiasm for modern Fortran. :) But the above is my honest experience.
Again, I just wanted to point my own experience, and I am in no way suggesting anything.
Ondrej
[1] http://scicomp.stackexchange.com/questions/5477/evaluate-the-sum/6831#6831
[2] https://plus.google.com/u/0/116518787475147930287/posts/jiULACjiGnW