Differentiable Fortran with LFortran and Enzyme

dionhaefner1 pts1 comments

Differentiable Fortran with LFortran and Enzyme - Tesseract Blog

Contents

Menu

Expand

Light mode

Dark mode

Auto light/dark, in light mode

Auto light/dark, in dark mode

Skip to content

Introduction

Installation

Get Started

Tesseract User Forums

Changelog

Creating Tesseracts

Creating Tesseracts

Design Patterns

Tips for Defining Tesseract APIs

Using LLMs to Create Tesseracts

Deploying Tesseracts

Using Tesseracts

Interacting with Tesseracts

Array Encodings

Advanced Usage

Demos & Tutorials

Demos & Tutorials<br>4D-Variational Data Assimilation for a Chaotic Dynamical System

Building the JAX Solver Tesseract for Lorenz-96

Gradient-Based Optimization of Fluid Flows

Parametric Shape Optimization with Differentiable FEM Simulation

Inverse Heat Transfer with Automatic Differentiation

Learned Closure: Training a Neural Viscosity Model Through a PDE Solver (PyTorch)

JAX Rosenbrock Minimization

PyTorch Rosenbrock Minimization

JAX RBF Fitting

Example Gallery: Building Blocks<br>HelloWorld

VectorAdd

Univariate Rosenbrock function

Wrapping Compiled Code (Fortran Example)

Differentiating Compiled Code (Enzyme + Fortran)

Wrapping MATLAB Code

Packaging local files into a Tesseract

Custom build steps: PyVista on ARM64

Installing local Python modules into a Tesseract

Out-of-core data loading

File IO with InputPath / OutputPath

Finite Difference Gradients

Deriving Gradient Endpoints from Each Other

Example Gallery: Integrating with Ansys Products<br>Wrapping SpaceClaim as a Tesseract

Wrapping MAPDL as a Tesseract

Guides

Differentiable Programming Basics

Debugging Guide

Performance Trade-offs & Optimization

API Reference — SDK

CLI Reference (tesseract)

Python API (tesseract_core)

Configuration (tesseract_config.yaml)

API Reference — Runtime

Endpoints (tesseract_api.py)

CLI Reference (tesseract-runtime)

Python API (tesseract_core.runtime)

Back to top

View this page

&larr; Tesseract Blog

2026-07-09 &middot; @dionhaefner

Differentiable Fortran with LFortran and Enzyme¶

What if you could backpropagate through existing Fortran, C, or C++ simulation code, embed it into JAX and torch, and use it as a high-performance differentiable physics engine? Turns out, you can — if you’re brave enough…

Decades of validated physics code in CFD, climate, aerospace, and nuclear sit behind a wall that modern ML pipelines can’t cross, because they don’t expose gradients. The usual answer is to rewrite it all in JAX or PyTorch. The alternative we explore here is to leave the code where it is and get exact gradients out anyway, thanks to some LLVM-level magic. This is possible because Enzyme applies autodiff at the LLVM IR level, so we can differentiate any code that compiles to LLVM!

All we need is to duct-tape LFortran, LLVM, and Enzyme together, point the result at a Fortran thermal solver, and get exact gradients out the other end. From there, Tesseract wraps the result as a custom JAX primitive, so a Fortran solver becomes a differentiable layer in arbitrary JAX code.

This is all pretty experimental, so be prepared to spend some time chasing a gradient that returned NaN and manually compare LLVM IR diffs to make it work. But if you put in the labor, you’ll get gradients through the entire multi-step time loop that match an analytic answer, and it’s amazing to see that a stack combining some of the oldest and newest technologies can work together at all.

But let’s start at the beginning. What follows is the full story, including the compilation pipeline, the sharp edges we hit, and application on a challenging inverse problem (that wouldn’t be possible without AD).

The problem with getting gradients out of legacy code¶

If you work in scientific computing, you might have run into this: you have a simulation written in Fortran (or C, or C++), and now someone needs gradients (the derivatives of the simulation’s outputs with respect to its inputs). Maybe it’s for optimization, maybe inverse problems, maybe plugging the sim into an ML pipeline. Now your options are:

Hand-written adjoints. Boils down to hand-implementing the derivative of every line of code, which means months of expert effort. Error-prone and a maintenance nightmare that slowly drifts out of sync with the forward code.

Finite differences. Perturb every input and difference the outputs. Slow (\(O(n)\) evaluations for n parameters), inaccurate, and poorly conditioned for stiff problems.

Rewrite in JAX or PyTorch and use autodiff. Sure, if you want to rewrite tens of thousands of lines of validated physics.

What if you could just compile the derivatives automatically, from the existing source? This is what it actually looks like when you try.

An example Fortran heat solver¶

Our test subject is thermal_2d.f90, about 220 lines of vanilla Fortran 90 that we wrote for this experiment. It solves 2D transient heat conduction with temperature-dependent conductivity:

\[\rho \, c_p \frac{\partial T}{\partial t} = \nabla \cdot...

tesseract fortran code differentiable gradients enzyme

Related Articles