GSoC 2025 – Improving Fortran Interfacing in PETSc

Hello everyone,

I’m contributing to PETSc as part of GSoC 2025 under mentorship from @MarDie and @ivanpribec. I’m starting this thread to post weekly updates throughout the summer with progress, open questions and implementation notes here as the work evolves.
I usually post technical questions and discussions on PETSc’s official discord server under fortran-gsoc-2025 channel but I will keep this thread up to date for broader community visibility.
Looking forward to learning more and getting your feedback along the way.

Best regards,
Tapashree

7 Likes

Hi @tapashreepradhan, thanks for the introduction and welcome to the Forum!

Just a quick note: please make sure to keep the broader Fortran-lang community in the loop by posting regular updates and discussions here on Discourse. It’s of course ok to go to PETSc’s Discord for in-depth technical coordination, but Discourse is our main platform for community engagement, visibility, and archival - it helps us all follow, interact, and learn from your work too.

Looking forward to your progress!

1 Like

Weekly Progress Update: As of 16th June 2025

Context and Motivation

This week’s work continues the ongoing effort to modernize and automate the Fortran interface within PETSc (Portable, Extensible Toolkit for Scientific Computation). The primary motivation is to improve the usability and maintainability of PETSc for Fortran users, leverage modern Fortran interoperability features (ISO_C_BINDING, BIND(C)), and reduce the need for manual binding creation.

Readings Done and Concepts Learnt

I have focused on a deeper understanding of Fortran-C interoperability, which is foundational to modernizing PETSc’s Fortran interfaces. Key concepts reviewed include:

  • Standard Fortran and C Interoperability: This core principle dictates that compiled Fortran and C code can interact within the same program, provided one language serves as the main entry point. The goal is to facilitate the use of scientific kernels written in modern Fortran alongside existing C/C++ libraries.

  • ISO_C_BINDING Module: Introduced in Fortran 2003, this standard module is critical for C interoperability. It defines:

    • Kind Constants: Such as C_INT, C_DOUBLE, C_CHAR, which ensure type matching between Fortran and C (e.g., INTEGER(C_INT) in Fortran guarantees it maps to int in C, preventing size/representation mismatches).

    • C-style Pointers: C_PTR for generic C pointers (void *) and C_FUNPTR for C function pointers.

    • Helper Procedures: C_LOC (to get the C address of a Fortran variable) and C_F_POINTER (to associate a Fortran pointer with a C pointer).

  • BIND(C) Attribute: This attribute, applied to Fortran procedures or derived types, is essential for C compatibility. It:

    • Prevents Name Mangling: Ensures the symbol name in the compiled binary is exactly what C expects (e.g., a Fortran SUBROUTINE mysub becomes mysub in the binary, not mysub_).

    • Uses C Calling Conventions: Arguments are typically passed by reference, but the VALUE attribute can enforce pass-by-value, matching C’s default behavior for scalar types.

    • Enables Interoperable Derived Types: Allows Fortran TYPE, BIND(C) to precisely match C struct definitions, provided they contain only interoperable types and no allocatables or pointers.

  • CFI_cdesc_t: A C struct defined by Fortran 2018’s ISO_Fortran_binding.h. This descriptor allows for passing generic Fortran arrays or strings to C in a standard and portable way, simplifying array and string handling.

  • Opaque Handle Management: PETSc frequently uses opaque handles (e.g., Vec, Mat) which are typically passed as INTEGER(KIND=PETSC_FORTRAN_TYPE) in Fortran, corresponding to C pointers. This pattern hides internal C object structures from Fortran, providing encapsulation. While BIND(C) helps with calling conventions, custom handling is still often required for these opaque types and error codes.

  • Fortran-C String Passing: A recurring challenge is how Fortran handles string lengths (often passed as hidden arguments) and how C expects null-terminated strings. Manual conversion and wrapper functions are frequently needed for this.

Merge Requests

Progress this week has been concretized through several Merge Requests (MRs).

MR !8465: PetscObjectNullify Macro (Reviewer)

  • Description: This MR introduced a PetscObjectNullify macro, a single-line preprocessor directive for Fortran users. Its purpose is to reset PETSc object handles (such as Vec, Mat, KSP) back to their initial, unassigned state, which is a specific fix requested by a user.

  • Technical Details: The macro is defined as #define PetscObjectNullify(o) o%v = PETSC_FORTRAN_TYPE_INITIALIZE. This means that PETScObjectNullify(myvec) automatically expands to myvec%vv = PETSC_FORTRAN_TYPE_INITIALIZE, effectively nullifying the Fortran-side handle. This addresses a common need for Fortran clients to manage the lifecycle of PETSc objects more cleanly.

MR !8437: Fortran: Fix HDF5 Attribute Writing Interface (Author)

  • Description: This is a new MR that replaces a previous attempt (#8289) that failed due to a branch naming convention issue. It addresses the missing Fortran binding for writing HDF5 attributes in PETSc, specifically fixing issue #194.

  • Technical Details: The MR focuses on implementing the necessary Fortran interface to allow PETSc users to write HDF5 attributes. A key point of discussion during its development was a suggestion from Barry Smith that these functions could potentially be generated automatically using the existing generatefortranbindings.py infrastructure, rather than requiring manual binding. This suggestion has become a crucial next step to explore.

  • Status: In testing phase, with an automation experiment planned.

MR !8422: Work on Generating Fortran Bindings (Reviewer)

  • Description: This MR is foundational, focusing on automating Fortran binding generation for specific types of C functions. It aims to:

    • Generate Fortran interfaces for C functions that take function pointers as arguments (callbacks) where parameter names end with “Fn” (e.g., createFn, destroyFn). These previously required manual binding creation.
    • Create “bodyless stubs” where feasible, providing the boilerplate C code but no implementation, simplifying subsequent manual completion.
  • Technical Details: The challenge addressed here is the complex handling of function pointers between Fortran and C due to different memory management and calling conventions. This MR is establishing the core automation infrastructure to bridge this gap, paving the way for more automated binding generation.

Impact: Forming a core part of the automation framework.

MR !8469: Automatically Generate Fortran C Stubs for Static Inline Functions in Include Directory (Reviewer)

  • Description: This MR solves the problem of Fortran users needing access to static inline functions (e.g., PetscTime()) that were previously inaccessible in the Fortran interface.

  • Technical Details:

    • Automated Stub Generation: Implemented a system for automatic generation of Fortran C stubs for static inline functions found in PETSc include directories.

    • MANSEC Integration: Added missing MANSEC (manual sections) to some include files. These sections are crucial for the getAPI() tool to correctly determine which PETSc library (e.g., sys, vec) the Fortran stubs belong to, ensuring proper organization.

    • PeNS Macro Introduction: Introduced the PeNS (PETSc Non-Standard) macro for functions that cannot currently be auto-generated due to non-standard arguments. This requires moving PeNS and related macros to public include files, as some static inline functions must be marked with them.

  • Impact: This MR extends the automation framework to a new category of functions. Barry’s work here solved the “easy” static inline functions, and the current task is to extend it to handle more complex cases, such as those with char arguments, that currently require PeNS.

MR !8436: Add Fortran interface for DMShellSetCreateFieldDecomposition (Author)

  • Description: This MR is a replica of a previously closed MR (!8265), which failed due to a # character in the branch name causing GitLab pipeline issues. It provides a Fortran interface for DMShellSetCreateFieldDecomposition.

  • Technical Details: DMShell is PETSc’s mechanism for users to create custom distributed mesh (DM) objects with their own operation implementations. The SetCreateFieldDecomposition function allows users to specify how fields are decomposed for multigrid or domain decomposition methods, which is vital for advanced solver configurations. The Fortran interface for this function enables Fortran users to define these custom decomposition strategies. The code in zdmshellf.c was also cleaned up for clarity in this version.

How the MRs are Building on Top of Each Other

The various Merge Requests discussed are not isolated efforts but are interconnected, progressively building a more robust and automated Fortran binding system in PETSc.

Foundation Layer:

  • !8422 (Barry’s Foundation): This MR is the cornerstone. It establishes the core automation infrastructure for generating Fortran bindings, with a particular focus on functions that accept function pointer arguments ending in “Fn”. This work is critical for handling callback mechanisms, a frequent pattern in PETSc.

Building on Foundation:

  • !8469 (Extending Automation to Static Inlines): This MR directly extends the automation approach introduced in !8422. It applies similar principles to automatically generate Fortran C stubs for static inline functions within PETSc’s include directories. This shows how the foundational work is being generalized to cover more function types.

  • !8437 (Automation Experimentation): While !8437 is currently a manual binding fix for HDF5 attribute writing, Barry’s suggestion to automate it links directly back to the automation capabilities being developed in !8422 and !8469. This MR serves as a potential test case to validate and apply the growing automation framework. The plan is to see if the new tools can replace manual work here, proving their effectiveness.

Independent but Related:

  • !8436 (Callback Interface): This MR manually handles a callback interface for DMShellSetCreateFieldDecomposition. However, it could directly benefit from the advanced function pointer automation being developed in !8422. As the automation capabilities mature, such manual implementations can potentially be replaced by generated code.

Hierarchical View of Interconnected MRs:

This hierarchical approach reflects a strategic effort to build a robust, maintainable, and increasingly automated Fortran interface for PETSc, starting with foundational components and iteratively expanding their scope and application.

8422 (Barry's core foundation for automated Fortran bindings)
   ↓
8469 (Extends automation to static inline functions)
   ↓
8437 automation experiment (to-do list: validate if automation can replace manual work here)
   ↓
Validate approach (confirm automation effectiveness)
   ↓
Apply to 8436 and future callback-based functions (leverage automation for manual interfaces)
   ↓
Reduce manual Fortran binding work across PETSc (overall project goal)

Next Steps:

The immediate next steps are focused on expanding the automated binding generation tooling:

  • Automating char argument function
  • Experiment with HDF5 attribute writing automation

Merge Request Links

5 Likes

Weekly Progress Update: As of 24th June 2025

This week I focused on understanding and automating character argument handling in PETSc’s Fortran bindings.

Concepts learnt

  • PeNS Functions and Character Arguments: PETSc uses PeNS macro to mark non-standard functions that bypass automatic Fortran binding generation. Many contain character arguments (char*, const char[], single char) requiring manual Fortran interfaces.
  • C-fortran String Interoperability: C uses null-terminated strings while Fortran uses space-padded strings with hidden length parameters. PETSc provides FIXCHAR/FREECHAR macros for conversion, handling memory allocation and null termination between languages.
  • PETSc Automation Tools: Went through getAPI.py (function signature parser) and generateFortranBindings.py (binding generator). Current limitations include incomplete character type detection and exclusion of PeNS-marked functions from automation.

Work Done

For automation of fortran bindings generation for functions with char arguments:

  • Extended the getAPI.py to recognize and categorize different character argument types, storing metadata about input/output direction and string vs single character use.
  • Modified the generateFortranBindings.py with templates to automatically generate appropriate Fortran interfaces and C stub functions for each character argument pattern.
  • As proof-of-concept, automated PetscTestFile() bindings that compile correctly and match PETSc’s Fortran conventions, eliminating need for manual binding.
  • Wrote tests verifying generated bindings work correctly for string input/output scenarios.

Merge Requests Status

Active MRs (as author)

  • !8492 - under review
  • !8437 - 1 job failed in stage 3
  • !8436 - 1 job failed in stage 3

Issues Assigned:

  • #1779 - Keep C variable names for Fortran interfaces

Next steps:

  • Resolve the pipeline issues
  • Implement review feedback
  • Work on fixing assigned issue
  • Meeting with mentors to discuss the next incremental step.
4 Likes

Progress update as of 16th July 2025

Most of the time was dedicated to planning with PETSc developers to solidify a concrete plan to enhance the Fortran support for PETSc in general.

Merge Requests:

Focus Area: Automating Fortran bindings for C functions with char arguments

Most of the time was spent working on a system that can automatically create Fortran interfaces for C functions that use strings (character arguments).

In C, there are different ways to write string arguments:

  • char *name - a single string
  • char **names - a list of strings
  • const char *name - immutable string

In Fortran, these need to be written as:

  • character(*), intent(in) :: name - for single strings you read
  • character(*), intent(inout) :: names(:) - for lists of strings you can change

Automation Logic

The code analyzes C functions and generates Fortran code by:

  • Counting pointer levels (* symbols) to determine single string vs. string array
  • Checking for const keyword to set appropriate intent
  • Generating correct Fortran syntax based on the pattern found

Why this work is taking longer than expected

PETSc has a lot of functions that take character arguments, and many of them use data types like PetscEnum (PetscEnum — PETSc 3.23.4 documentation). The first version of the automatic system was failing when it encountered functions using string enums as function arguments, so it was skipping these arguments and generating a partial Fortran interface for those C stubs.

Because of this, more time was needed to:

  • Go through the entire PETSc codebase to find all the different ways character arguments are used
  • Make a list of all functions that take character parameters as input or output
  • Figure out how to handle all these different cases properly

Upcoming work:

Next Automation Targets

After completing char argument handling:

a. Double Pointers (**):

  • Example: PetscViewerAndFormat **vf
  • Challenge: Fortran lacks direct equivalent to C’s double pointers

b. Void Pointers (void*):

  • Example: void *ctx in MatCreateShell

More discussion with PETSc developers to plan out their automation.

2 Likes

One minor comment: I believe (someone please correct me if I’m wrong) with the declaration above, Fortran would expect the strings to be of the same length and stored contiguously. It could also be a strided array of strings, due to the assumed-shape.

Fortran lacks equivalent functionality to the char **names in a strict sense. In C this could also be used as a jagged array (also known as a Iliffe vector).

Here’s a little program to demonstrate what I mean:

Example of accessing C array of strings
// demo.c
#include <stdio.h>

extern void print_names(const char *names[], int count); 
   // names could also be char **

int main(void) {
    const char *characters[] = {
        "Fry", "Leela", "Bender", "Zoidberg"
    };
    print_names(characters, 4);
    return 0;
}
! print_names.f90
subroutine print_names(names,count) bind(c)
use, intrinsic :: iso_c_binding
implicit none
integer(c_int), value :: count
type(c_ptr) :: names(count)  ! array of pointers to strings

integer :: i

write(*,'(A)') (ith_string(i),i=1,count)

contains

    function ith_string(i)
        integer, intent(in) :: i 
        character(len=:), pointer :: ith_string

        interface
            pure function strlen(str) bind(c)
                import c_ptr, c_size_t
                type(c_ptr), value :: str
                integer(c_size_t) :: strlen
            end function
        end interface

        block
            character(len=strlen(names(i))), pointer :: ptr
            call c_f_pointer(names(i),ptr)
            ith_string => ptr
        end block
    end function

end subroutine
$ gfortran -c -Og -Wall print_names.f90
$ gcc-15 -Wall demo.c print_names.o -lgfortran
$ ./a.out
Fry
Leela
Bender
Zoidberg
3 Likes

Progress update as of 28th July 2025

Char automation

Following Ivan’s suggestion, I incorporated the feedback into a new MR: add support for single char argument fortran bindings (!8580) · Merge requests · PETSc / petsc · GitLab
This MR is now open and has successfully passed all CI pipeline jobs.

Next: Handling void* Arguments in PETSc Fortran Bindings

The problem with void* arguments

  • in C programming, void* is a generic pointer type that can point to any kind of data, meaning “pointer to known type of data”.
  • used when we want to pass data around without carring about its specific type

Current Behavior in PETSc

  • The Fortran binding generator encounters a function with void* argument, it marks the function as “opaque” (cannot generate automatic bindings):
# cannot generate Fortran functions if any argument is void or PeCtx
          opaque = False
          for k in fi.arguments:
            if k.typename == 'void' or k.typename == 'PeCtx': opaque = True
          if opaque: continue
  • The function becomes unavailable to Fortran users and manual stub creation is required.

PeCtx:
PeCtx is a PETSc context type used internally for callback mechanisms. Like void*, functions with PeCtx arguments are also marked as opaque in Fortran bindings as of writing this update.

Example:

PetscErrorCode MatCreateShell(MPI_Comm comm, PetscInt m, PetscInt n, 
                              PetscInt M, PetscInt N, void *ctx, Mat *A)

Proposed Solution: Map void* to Fortran’s type(*)

type(*) in Fortran is a polymorphic type that can hold any data type, similar to void* in C. By mapping void* to type(*), we can allow Fortran users to pass any data type without losing the ability to call the function.
So the generated Fortran interface for MatCreateShell would look like this:

interface MatCreateShell
  subroutine MatCreateShell(comm, m, n, M, N, ctx, A, ierr)
    MPI_Comm :: comm
    PetscInt :: m, n, M, N
    type(*) :: ctx        ! Maps to C's void*
    Mat :: A
    PetscErrorCode :: ierr
  end subroutine
end interface

Open question on systematic detection of Fortran binding coverage:

How can we systematically detect which functions are now covered by the automated Fortran binding generator vs. those that still require manual stubs?

Current idea: Write a script with two functions:

  • Find C functions with specific parameter patterns (e.g., single char, void*)
  • Find existing Fortran interfaces
  • Take the intersection to see coverage

Hi everyone,

I wanted to provide an update on my last post about automating void* arguments. Unfortunately, I encountered some health issues towards the end of the project.
In light of these circumstances, after discussion, I’ve decided to contribute for two additional weeks beyond the official GSoC timeline (after GSoC organisers informed me that they can not extend the deadline) to compensate for the missed time. I’ve also mentioned this in the GSoC evaluation submission. During this period, my primary focus will be on completing the void* argument automation for PETSc.

I plan to post two more updates:

  • At the end of this week, focused on on the coverage of the Fortran bindings.
  • Next week, on the void* argument automation.

I understand that this may affect my GSoC evaluation, but my main goal is to finish the work I had started on these two objectives.

2 Likes

Update on Fortran Binding Automation Coverage

What was done:

  • Added a new script to systematically compare the coverage of functions with Fortran interfaces before and after automation changes. The script identifies newly generated interfaces and includes temporary logging to track which functions are now covered automatically.
  • Implemented minor updates addressing reviewer feedback.
  • MR: !8580

Next:

  • Automate functions with void parameters, which currently still require manual Fortran interfaces.
  • Ensure that all merge requests pass the CI pipeline.
2 Likes

Weekly Update #10:

  • Implemented automation of Fortran interface generation for PETSc functions that take void * arguments, mapping void *foo in C stubs to type(*) :: foo in Fortran stubs.
  • Applied this logic to both standalone functions and class methods in PETSc
  • Added checks to skip unsupported void argument types such as void **, void arrays and function pointers.
  • The feature is currently in a development branch: Commits · tapashree/voidautomation · PETSc / petsc · GitLab as I’m still fixing the compilation errors before opening a MR.

Current Challenge:

The build is failing due to duplicate Fortran interfaces being generated for functions that already have manual stubs. I’m currently working on the duplicate detection logic to ensure these cases are skipped consistently.

This automation will also cover functions like MatCreateShell , which currently require manually written Fortran interfaces.

Next Steps:

  • Resolve the build errors and open a merge request for feedback.

Based on discussions with mentor, the next areas of focus will be:

  • Documenting the type matches between C and Fortran in PETSc, consolidating this information into the Fortran interface generator script rather than maintaining a separate file.
  • Addressing Issue #1779: updating the generator to preserve the original variable names from C functions, instead of substituting them with a, b, ..., to improve debugging.
1 Like

Weekly Update #11:

After two weeks of work, the compilation errors related to automating Fortran interface generation for C functions with void * arguments (mapped to type(*) in Fortran) in the PETSc build have been resolved. A summary of issues encountered:

1. Initial fortran interface duplicate errors:
Duplicate Fortran interface definitions for functions with function pointer arguments (e.g., DMSNESSetFunctionLocal, DMSNESSetJacobianLocal, SNESSetConvergenceTest), because both manual and auto-generated Fortran interfaces existed for the same functions.

2. Function pointer detection issue: Functions such as CharacteristicSetFieldInterpolationLocal (with C function pointer arguments, e.g. PetscErrorCode (*interp)(...)) were still generating Fortran interfaces. The API parser in getAPI.py was skipping few of the functions.

3. Fortran Type Import/Scope Errors: Generated Fortran interface files were missing required type definitions.

The merge request for this feature is currently marked as draft: !8780. A new test and expected test output still need to be added before it can be marked ready.

In addition, the previous MR has now been merged: !8580

3 Likes