{"id":22752408,"url":"https://github.com/perrette/python-fortran-cpp-template","last_synced_at":"2025-10-17T09:15:53.076Z","repository":{"id":144978201,"uuid":"46051770","full_name":"perrette/python-fortran-cpp-template","owner":"perrette","description":"Minimal example template to build a python package that uses c++ and fortran","archived":false,"fork":false,"pushed_at":"2015-12-18T17:20:54.000Z","size":29,"stargazers_count":12,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-14T14:15:05.305Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/perrette.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-12T12:34:31.000Z","updated_at":"2021-05-19T16:43:16.000Z","dependencies_parsed_at":"2023-04-16T08:05:53.486Z","dependency_job_id":null,"html_url":"https://github.com/perrette/python-fortran-cpp-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perrette%2Fpython-fortran-cpp-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perrette%2Fpython-fortran-cpp-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perrette%2Fpython-fortran-cpp-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perrette%2Fpython-fortran-cpp-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/perrette","download_url":"https://codeload.github.com/perrette/python-fortran-cpp-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248894943,"owners_count":21179153,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-11T05:11:41.463Z","updated_at":"2025-10-17T09:15:48.031Z","avatar_url":"https://github.com/perrette.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"python-fortran-cpp template\n===========================\n\nThis repository is a minimal example to make a python package \nthat makes use of fortran (f2py) and C++ (cython) extensions\nwith a setup.py file.\n\nIt also contains other patterns such as script installation, \nwith the excellent argparse, and ipython notebooks, as example \nof user interaction.\n\nWith these various tools at hand, jungling between git-managed packages\nand normal work folders (NOT managaed by git, but keeping track of the \ngit-version used), the work becomes very productive and reproducible.\nAnd excellent to share work in a team.\n\nDependencies\n------------\n\nThis template was tested in python 2.7 with:\n\n- numpy : 1.9.2 (f2py)\n- cython : 0.22 (c++ extension)\n- ipython : 3.1.0 (ipython notebook)\n\nInstall\n-------\n\nInstall the pacakge on your system:\n\n    python setup.py install\n\nUsage\n-----\n\nThen you may:\n\n- execute myscript.py from anywhere on your system (to see excellent argparse at work)\n\n        myscript.py --help\n        myscript.py --version\n        myscript.py -a 1 2 3 -b 2 2 2\n        myscript.py -a 1 2 3 -b 2 2 2 -o c_div\n        myscript.py -a 1 2 3 -b 2 2 2 -o f_mult\n\n- copy the notebook template in your work directory, and start working...\n\nThen feel free to use these pattern for your real work by editing these files\nat your convenience. Do not forget:\n\n- `setup.py` \n- `mypackage/__init__.py`\n\n\nWorkflow\n--------\n\nYou need to install everytime you want the changes to be accessible from the \nnotebook or other components that depend on mypackage. For that reason, it is \nbetter to only add functions and pieces of code that you consider stable.\n\nIn the development process, it is more convenient to work in the notebook \nand only add the functions to the actual package when you think you won't \nedit them every 5 min. Then your next notebook will be thinner, as it only \nneed to `from mypackage import my_func` instead of having the whole body inside.\n\nNote, if you need to modify / update some piece of packaged code later, you can always\nuse the magicc command `%load mymodule.py` to have the content of the file\nloaded in the notebook cell, then edit out the unnecessary part, and expand / debug \nthe bit of code you are interested in from within the notebook, without having\nto re-install the whole package at every new change. When you are happy with the changes, \ncopy back into the actual package, make a git commit etc..(of course, this assumes the \nbit you want to edit is not a dependency for other parts of the code)\n\nNotes on packaging\n------------------\n\nPackaging is useful to have your code importable from everywhere (as any package installed with pip)\nand to cleanly separate base functionality that you do not modify too often from daily work that \nwill be done preferentially in the notebook, or anywhere on the disk with various input data, \nnotes, output figures etc..., which you do not want to have tracked in git but instead archived.\n\n\nThe built-in packaging in python 2.7 is distutil:\n\n    setup(name = 'mypackage',\n      description       = \"example package using c++ and fortran\",\n      author            = \"Your Name\",\n      packages = [\"mypackage\"],  # also add subpackages !!\n      scripts = [\"scripts/myscript.py\"],  # add scripts to be called globally\n      )\n\nExtensions written are added via a `ext_modules` parameter. \nf2py and cython have highly simplified the way of programming extensions, \nby providing their own `setup` function and an `Extension` subclass (for f2py) \nor by providing a user-defined `build_ext` parameter (cython). \n\nThe way they do that does not seem to be compatible, so if both are to be\nused in the same pacakge, this needs to be done with two separate `setup` calls.\n\nSo in this example, we first install the\npython package without any extension (first) setup, then install the \ncython and f2py extensions as subpackages, with two additional setup calls.\n\n... extension : fortran + f2py\n------------------------------------\nThe simplest to use (if you know fortran). Basically, as long as your fortan \ncode only have simple types as input/output (scalars, arrays, strings) (no derived types!!), \nand make use of the intent(in) / intent(out) qualifiers, you do not need to \ndo anything more than use numpy-extended Extension class and setup function:\n\n    from numpy.distutils.core import Extension\n    from numpy.distutils.core import setup\n\n    flib = Extension(name = 'mypackage.flib',\n                     extra_compile_args = ['-O3'],\n                     sources = ['src_fortran/mymodule.f90'], # you may add several module files under the same extension\n                     )\n\n    setup(\n        ext_modules = [flib]\n        )\n\n... extension : c/c++ + cython\n-------------------------------------\nIn addition to c/c++ source files, it is necessary to add a definition \nindicating the c++ header:\n\n    cdef extern from \"path/to/my_header.h\":\n        int array_div(double *a, double *b, double *c, int n);\n\n(yes, it is redundant since this info is already present in my_header.h\nI am happy to hear your suggestion for less redundant alternatives)\n\nAnd a wrapper in cython (see `cython/*pyx` file)\n\nWhen this is done, the setup.py part is not more difficult than f2py:\n\n    from distutils.extension import Extension\n    from Cython.Distutils import build_ext\n\n    clib = Extension(\"mypackage.clib\",  # indicate where it should be available !\n                          sources=[\"cython/my_func.pyx\",\n                                   \"src_cpp/my_func.cpp\",\n                                   ],\n                          extra_compile_args=[\"-O3\", \"--std=c++11\", \"-ffast-math\", \"-Wall\"],\n                          language=\"c++\")\n\n    setup(\n        cmdclass = {'build_ext': build_ext},\n        ext_modules = [clib]\n        )\n\n\nLimitations of f2py fortran\n---------------------------\nJust a few things out of my own experience with f2py (please refer to [the official doc](http://docs.scipy.org/doc/numpy-dev/f2py) for more exhaustive information).\n- use ìntent(in/out/inout) mentions\n- use *simple* input/output arguments in subroutines and functions. This means in particular, no derived type, no allocatable arrays. [f90wrap](https://github.com/jameskermode/f90wrap) seem to relax this constraint, but not sure this will work for packaging.\n- the approach of globally defining `integer, parameter :: dp = kind(0.d0)` and then using `real(dp)` \ninstead of `double precision`, as encouraged [on fortran90.org](http://www.fortran90.org/src/best-practices.html#floating-point-numbers), \ndoes *not* work with f2py. You should use old-fashioned `double precision`\n(or plain `real(8)`, which is more confusing to me than just `double precision`).\n- using `private` module with only a few `public` methods/functions does *not* work when wrapping `f2py`. `f2py` blindly attemps to wrap everything it finds in the module, and a subsequent `use moodule, only: my_private_func` \nwill fail... So keep everything public.\n- *Avoid optional arguments*. `present` function for optional arguments does not work properly. An optional floating point argument will have the value 0 even though it is not actually provided. Again [f90wrap](https://github.com/jameskermode/f90wrap) provides a work-around, but I do not see clearly how to write a custom setup.py file using f90wrap, so I will leave it for now.\n- A work around for some of these limitations is to create a new module that import only the relevant\nfunctions that you want to see wrap, and write a new, f2py-compliant fortran function/subroutine...\nIf you are writing a wrapper anyway, you may alternatively use `iso_c_binding` as described below. Otherwise, again the [f90wrap](https://github.com/jameskermode/f90wrap) should be tested as a command-line tool to re-write your sources into something compatible.\n\niso_c_binding for fortran : cython and ctypes\n---------------------------------------------\nIt is also possible to make your fortran code c-compatible, using the `iso_c_binding` module in fortran, by \nfollowing instructions on [fortran90.org](http://www.fortran90.org/src/best-practices.html#interfacing-with-c). \nThis involves more changes to your source code than f2py would need, but then your code should also be callable\nfrom C++. And the C++ wrapping techniques (such as `cython` or `ctypes`) become available, as described [there](http://www.fortran90.org/src/best-practices.html#interfacing-with-python).\n\nCredits\n-------\nMuch of the cython part was inspired by the [dbg](https://github.com/pism/regional-tools) package.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperrette%2Fpython-fortran-cpp-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperrette%2Fpython-fortran-cpp-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperrette%2Fpython-fortran-cpp-template/lists"}