Self-Assessment Quiz: Fortran-Python Interoperability
Twenty questions to confirm you can wrap, call, and benchmark Fortran from Python before moving on. Aim for 16 or more. Answers and a topic map are at the end — try the whole quiz first.
Question 1
In the command f2py -c -m mykernel foo.f90, the string mykernel is:
- A. the name of the Fortran source file
- B. the name of the Fortran module inside the source
- C. the name of the extension module you will import in Python
- D. a compiler optimization level
Question 2
An extension module is:
- A. a plain-text .py file of pure Python
- B. a compiled shared library (.so/.pyd) that Python can import like any module
- C. a Fortran module program unit
- D. a Jupyter notebook
Question 3
A Fortran dummy argument declared intent(out) is presented by f2py to Python as:
- A. a required input argument
- B. a return value
- C. an argument modified in place
- D. a hidden argument
Question 4
Which NumPy dtype matches Fortran real(real64) (the book's dp)?
- A. np.float32
- B. np.int64
- C. np.float64
- D. np.longdouble
Question 5
By default, a freshly created 2-D NumPy array is: - A. F-contiguous (column-major) - B. C-contiguous (row-major) - C. neither — always a copy - D. stored on the GPU
Question 6
You pass a default (C-contiguous) 2-D array to an f2py routine with an intent(in) argument. f2py:
- A. refuses and raises an error
- B. transposes your indices silently
- C. makes an F-contiguous copy before the call — correct answer, but a per-call cost
- D. corrupts the data
Question 7
The one-keyword cure for the silent copy in Question 6 is to create the array with:
- A. dtype=object
- B. order='F'
- C. copy=False
- D. ndmin=2
Question 8
True or false: "f2py transposes your array, so a[i, j] in Python becomes u(j, i) in Fortran." Justify.
Question 9
A 1-D NumPy array is: - A. only C-contiguous - B. only F-contiguous - C. both C- and F-contiguous - D. never contiguous
Question 10
You pass a C-contiguous array to an f2py routine whose argument is intent(inout). The most likely result:
- A. it works, in place, silently
- B. f2py raises a ValueError about the array not being Fortran-contiguous
- C. it transposes the array
- D. it converts the array to a Python list
Question 11
ctypes and cffi can call Fortran only if the Fortran procedure has:
- A. implicit none
- B. intent on every argument
- C. the bind(c) attribute
- D. an allocatable result
Question 12
Without bind(c), gfortran typically exports a module-less subroutine foo under the symbol name:
- A. foo
- B. FOO
- C. foo_ (lowercased, trailing underscore)
- D. a random hash
Question 13
In the two-language workflow, which work belongs on the Fortran side? - A. reading command-line arguments - B. the hot numerical kernel (the loop that dominates runtime) - C. drawing the matplotlib figure - D. parsing a config file
Question 14
The whole-program speed of a numerical code is set mainly by: - A. the language of its longest source file - B. the language of its hot loop - C. the number of comments - D. the operating system
Question 15
Why does this book present the pure-Python-vs-Fortran speedup as "10–100×, measure it yourself" rather than a single number? - A. because the speedup is always exactly 50× - B. because no code in the book is executed, and the real figure depends on hardware, compiler, flags, and size - C. because f2py has no timing tools - D. because Fortran is slower
Question 16
The pure-Python element loop is slow (relative to compiled Fortran) mainly because of: - A. the arithmetic itself being different - B. per-iteration interpreter overhead (bytecode dispatch, type checks, boxing) around each operation - C. Python using less memory - D. NumPy being uninstalled
Question 17
When a hot loop can be written as a few whole-array NumPy slice operations, you should usually: - A. still rewrite it in Fortran - B. use the NumPy version — it is already compiled C underneath - C. use a pure-Python loop - D. give up
Question 18
The heat time loop cannot be collapsed into a single NumPy array expression because: - A. NumPy has no arrays - B. each step depends on the field the previous step produced (a loop-carried dependency) - C. matplotlib forbids it - D. the field is too small
Question 19
What does the !f2py intent(hide), depend(x) :: n = shape(x,0) directive accomplish?
- A. it deletes the argument x
- B. it hides n from the Python signature and computes it from x's shape
- C. it makes n a return value
- D. it transposes x
Question 20
A subroutine foo inside Fortran module bar, wrapped as extension module lib, is called from Python as:
- A. lib.foo(...)
- B. bar.foo(...)
- C. lib.bar.foo(...)
- D. foo.lib.bar(...)
Answer Key
| Q | Ans | Why |
|---|---|---|
| 1 | C | -m NAME names the importable extension module; the filename and Fortran module name are independent. |
| 2 | B | A compiled shared library Python imports like a .py — NumPy and SciPy are examples. |
| 3 | B | intent(out) becomes a Python return value. |
| 4 | C | real64 ↔ float64 (8-byte IEEE double). |
| 5 | B | NumPy defaults to C-contiguous (row-major). |
| 6 | C | For intent(in), f2py copies to F-order if needed — right answer, per-call cost. |
| 7 | B | order='F' makes it F-contiguous from birth, so no copy. |
| 8 | False | f2py preserves indexing: a[i,j] → u(i+1,j+1); it reconciles layout by copying, not transposing. |
| 9 | C | A 1-D array is both C- and F-contiguous, so 1-D never hits the layout issue. |
| 10 | B | intent(inout) cannot be faked with a copy, so f2py rejects a non-F-contiguous array. |
| 11 | C | bind(c) gives the C name and calling convention the C-ABI FFIs need. |
| 12 | C | gfortran lowercases and appends an underscore: foo_. |
| 13 | B | The hot kernel is Fortran's job; the rest is Python's. |
| 14 | B | The hot loop's language dominates; the bulk runs rarely. |
| 15 | B | No code is executed here, and the real figure is hardware/compiler/size dependent — only the order of magnitude is robust. |
| 16 | B | Interpreter overhead per element, not the arithmetic, is the gap. |
| 17 | B | Vectorized NumPy is compiled C already — use it; reach for Fortran when you can't vectorize. |
| 18 | B | A loop-carried dependency across steps blocks a single array expression. |
| 19 | B | It hides n and infers it from x's shape. |
| 20 | C | Module procedures live under lib.<fortran_module>: lib.bar.foo. |
Topics to review by question
- Q1–4, 19–20 → §15.2 (f2py, the build command,
intent→signature, module namespacing). - Q5–10 → §15.3 (dtype, C- vs F-contiguous,
order='F', the copy,intent(inout)). - Q11–12 → §15.4 (ctypes/cffi and
bind(c)). - Q13–14, 17–18 → §15.1 (the two-language workflow; when NumPy suffices, when Fortran wins).
- Q15–16 → §15.5 (honest benchmarking; where the gap comes from).
Scored below 16? Reread the flagged sections. If you missed Q6–Q10, build example-02 and pass it both a
default and an order='F' array — seeing the difference fixes the idea faster than rereading.