The giants vibe-qc stands on: a guided tour of the stack

In my previous post I wrote about vibe-coding a quantum-chemical program in one day. The code lives at vibe-qc.com. That post closed with a compact acknowledgment that none of it would have been possible without decades of prior work by a lot of people. This post is where I pay that debt properly: every library we used, what it does, why it matters, and how to cite it.

If you are building anything similar, treat this as a reading list. If you are just curious what sits underneath a modern quantum chemistry code, keep scrolling. I have organized things by role rather than alphabetically, because the interesting story is the layered structure of the stack.

The numerical core

libint

This is the beating heart of vibe-qc. Every one- and two-electron Gaussian integral, and every analytic derivative of those integrals, is computed by libint. It is a C++ library whose source is partly handwritten and partly emitted by its own code generator (which is itself a substantial piece of C++). We build version 2.13.1 from source with max_am=5 (angular momentum up to g-functions) and first-order derivatives enabled. The source build takes about 10 minutes on an M1, and you only do it once per checkout.

Without libint, implementing two-electron repulsion integrals with the right recurrence relations would consume weeks on its own. With libint, it is a function call.

Valeev, E. F. Libint: A library for the evaluation of molecular integrals of many-body operators over Gaussian functions, Version 2.13.1, 2024. https://libint.valeyev.net/

libxc

libxc is the library that turns vibe-qc’s DFT layer from “LDA only” into a proper general-purpose DFT code with 500+ functionals available via aliases like "PBE", "B3LYP", or "BLYP". Under the hood it exposes a uniform C API for evaluating exchange-correlation energy densities and their derivatives with respect to density and density gradient. Hybrid functionals report their exact-exchange fraction through xc_hyb_exx_coef, which is exactly what a SCF driver needs to know.

The “right” way to expose functionals in a new DFT code is to wrap libxc, not to reimplement functionals yourself. We did the right thing here.

Lehtola, S.; Steigemann, C.; Oliveira, M. J. T.; Marques, M. A. L. Recent developments in libxc, a comprehensive library of functionals for density functional theory. SoftwareX 7, 1-5 (2018). https://doi.org/10.1016/j.softx.2017.11.002

Marques, M. A. L.; Oliveira, M. J. T.; Burnus, T. Libxc: a library of exchange and correlation functionals for density functional theory. Comput. Phys. Commun. 183, 2272-2281 (2012). https://doi.org/10.1016/j.cpc.2012.05.007

Eigen

All dense linear algebra in vibe-qc goes through Eigen: matrix products, eigendecompositions (for the Fock diagonalization), linear solves (for the DIIS coefficient equation), and the energy-weighted density matrix construction in the gradient code. Eigen is header-only, which keeps the build simple, and the expression-template design means the code reads like math.

Guennebaud, G.; Jacob, B.; et al. Eigen v3. 2010. https://eigen.tuxfamily.org

OpenMP

Included in the library list because the infrastructure is wired, although the hot loops in vibe-qc are currently serial. Adding OpenMP to the Fock build and the ERI-gradient loops is on the day-two todo list. OpenMP is the de-facto standard for shared-memory parallelism in scientific C++ and it is what we will use.

Bindings and build

pybind11

pybind11 is what makes vibe-qc feel like a Python library even though all the numerical work is in C++. The bindings expose the C++ classes (Molecule, BasisSet, RHFResult, etc.) to Python with zero-copy NumPy interop for the heavy arrays, and the native work releases the GIL via py::gil_scoped_release, so the Python frontend is not a scaling bottleneck.

Jakob, W.; Rhinelander, J.; Moldovan, D. pybind11: Seamless operability between C++11 and Python. 2017. https://github.com/pybind/pybind11

scikit-build-core

The modern Python build backend that lets pip install -e . drive a CMake build under the hood. This is the glue that makes vibe-qc feel like a normal Python package even though it compiles ~3500 lines of C++ and links against three native libraries.

CMake and Ninja

CMake generates the build, Ninja runs it. Ninja is considerably faster than make for projects with thousands of small files (libint’s code generator emits a lot of them).

Boost and GMP

These are libint’s build-time dependencies, not vibe-qc’s. Boost provides header-only utilities (MPL, Type Traits, Preprocessor) that libint’s code generator relies on. GMP provides arbitrary-precision rational arithmetic, which the generator needs to emit exact coefficients in the integral recurrence relations. You install them, libint builds, and you never think about them again.

Granlund, T.; the GMP development team. GNU MP: The GNU Multiple Precision Arithmetic Library, Edition 6.3.0, 2023. https://gmplib.org

Python runtime

NumPy

Every matrix and vector that crosses the C++/Python boundary becomes a NumPy array, usually as a zero-copy view of the underlying Eigen data. NumPy’s array protocol is the reason pybind11 can do that zero-copy handoff cleanly.

Harris, C. R. et al. Array programming with NumPy. Nature 585, 357-362 (2020). https://doi.org/10.1038/s41586-020-2649-2

ASE (Atomic Simulation Environment)

ASE deserves its own paragraph because it is the decision that turned vibe-qc from “a Python library with an SCF loop” into “a program chemists can actually use.” By subclassing ase.calculators.calculator.Calculator, vibe-qc inherits:

  • Structure file readers for XYZ, CIF, PDB, POSCAR, Gaussian inputs, and basically every format chemists touch.
  • Geometry optimizers (BFGS, FIRE, LBFGS, conjugate gradient, etc.) that just work once forces are implemented.
  • Consistent unit handling via ase.units.
  • Visualization through ase.visualize.
  • An interface that every other major code in the ecosystem (VASP, Quantum ESPRESSO, GPAW, etc.) also implements, so users who already know ASE know vibe-qc.

If I were starting another scientific code tomorrow, I would wire it into ASE on day one, same as we did here.

Larsen, A. H. et al. The atomic simulation environment, a Python library for working with atoms. J. Phys.: Condens. Matter 29, 273002 (2017). https://doi.org/10.1088/1361-648X/aa680e

Reference and validation

PySCF

PySCF is the cross-check oracle for everything vibe-qc does. Every RHF, UHF, and RKS energy, every MO coefficient, every gradient component in the 131-test suite is compared against a PySCF calculation on the same system with the same basis set. HF agreement is at machine precision (around 1e-14 Ha), DFT at grid accuracy (down to 5e-11 Ha for LDA, 1.3e-7 Ha for B3LYP on the default medium grid).

If you are writing a new quantum chemistry code in 2026, you should be validating against PySCF. It is open, well-tested, and actively maintained. There is no excuse for shipping numbers that have not been cross-checked.

Sun, Q. et al. Recent developments in the PySCF program package. J. Chem. Phys. 153, 024109 (2020). https://doi.org/10.1063/5.0006074

Sun, Q. et al. PySCF: the Python-based simulations of chemistry framework. WIREs Comput. Mol. Sci. 8, e1340 (2018). https://doi.org/10.1002/wcms.1340

pytest

Methods we implemented from the literature

These are not libraries, they are the papers whose formulas are transcribed into vibe-qc’s source code. If you look at the gradient code and wonder where the energy-weighted density matrix came from, the answer is Pople, Krishnan, Schlegel and Binkley 1979. If you look at the Becke partitioning in the grid module, the answer is Becke 1988.

Hartree-Fock and Roothaan equations

Szabo, A.; Ostlund, N. S. Modern Quantum Chemistry: Introduction to Advanced Electronic Structure Theory. Dover, 1996.

The Szabo-Ostlund textbook is what most of us learned this from. It is the book that makes the Roothaan equations actually click.

DIIS (Direct Inversion of the Iterative Subspace)

Pulay, P. Convergence acceleration of iterative sequences. The case of SCF iteration. Chem. Phys. Lett. 73, 393-398 (1980).

Pulay, P. Improved SCF convergence acceleration. J. Comput. Chem. 3, 556-560 (1982).

The 5-to-6x speedup we measured on H₂O (52 iterations down to 9) is pure Pulay.

Analytic HF gradients (Pople-Binkley formulation)

Pople, J. A.; Krishnan, R.; Schlegel, H. B.; Binkley, J. S. Derivative studies in Hartree-Fock and Moller-Plesset theories. Int. J. Quantum Chem. Symp. 13, 225-241 (1979).

The dE/dR formula with the energy-weighted density W comes from this paper. If you have ever wondered why the gradient code wants tr(W·dS) instead of something involving orbital derivatives directly, this is why.

DFT integration grid

Treutler, O.; Ahlrichs, R. Efficient molecular numerical integration schemes. J. Chem. Phys. 102, 346-354 (1995). https://doi.org/10.1063/1.469408

The M4 radial mapping with Chebyshev-2nd-kind nodes. Treutler-Ahlrichs is the standard choice for a reason.

Becke, A. D. A multicenter numerical integration scheme for polyatomic molecules. J. Chem. Phys. 88, 2547-2553 (1988). https://doi.org/10.1063/1.454033

Becke’s fuzzy-cell partitioning with the iterated (3/2)μ − (1/2)μ³ switch function is what makes the grid work for polyatomics.

Basis sets

Pritchard, B. P.; Altarawy, D.; Didier, B.; Gibson, T. D.; Windus, T. L. New Basis Set Exchange: An Open, Up-to-date Resource for the Molecular Sciences Community. J. Chem. Inf. Model. 59, 4814-4820 (2019). https://doi.org/10.1021/acs.jcim.9b00725

libint ships a snapshot of the Basis Set Exchange collection. 90 sets including the STO-3G, 3-21G, 6-31G, 6-311G, cc-pVXZ, and def2 families, plus the JKFIT, JFIT, and CABS auxiliary sets.

vibe-qc also has infrastructure for dropping in the pob-TZVP family, which was developed by our group for periodic solid-state calculations but applies equally well on the molecular side:

Peintinger, M. F.; Oliveira, D. V.; Bredow, T. Consistent Gaussian basis sets of triple-zeta valence with polarization quality for solid-state calculations. J. Comput. Chem. 34, 451-459 (2013). https://doi.org/10.1002/jcc.23153

Once the raw .g94 files are dropped into basis_library/custom/, the setup script picks them up automatically.

Exchange-correlation functionals (exposed as named aliases)

  • Slater exchange: Slater, J. C. A Simplification of the Hartree-Fock Method. Phys. Rev. 81, 385 (1951).
  • VWN5 correlation: Vosko, S. H.; Wilk, L.; Nusair, M. Accurate spin-dependent electron liquid correlation energies for local spin density calculations. Can. J. Phys. 58, 1200 (1980).
  • PBE: Perdew, J. P.; Burke, K.; Ernzerhof, M. Generalized Gradient Approximation Made Simple. Phys. Rev. Lett. 77, 3865 (1996).
  • B88 exchange: Becke, A. D. Density-functional exchange-energy approximation with correct asymptotic behavior. Phys. Rev. A 38, 3098 (1988).
  • LYP correlation: Lee, C.; Yang, W.; Parr, R. G. Development of the Colle-Salvetti correlation-energy formula into a functional of the electron density. Phys. Rev. B 37, 785 (1988).
  • B3LYP: Stephens, P. J.; Devlin, F. J.; Chabalowski, C. F.; Frisch, M. J. Ab Initio Calculation of Vibrational Absorption and Circular Dichroism Spectra Using Density Functional Force Fields. J. Phys. Chem. 98, 11623 (1994).

Development tooling

  • Claude Code (Anthropic). The coding agent that paired with me on this. https://www.anthropic.com/claude-code
  • GitLab (self-hosted at gitlab.peintinger.com). Version control host.
  • Homebrew. macOS package manager used for libxc, Eigen, Boost, GMP, CMake, and Ninja.

Closing thought

The list above is long, and that is the point. “Vibe-coding a quantum chemistry program in one day” is a true description of what happened, but it is a misleading one if you read it as “a quantum chemistry program was created from nothing in one day.” What actually happened is that a coding agent (pairing with someone who knows the field) threaded together the work of hundreds of authors spanning seventy-five years of physics, chemistry, numerical analysis, and software engineering.

The compressible part was the glue. The giants were already there, standing still, waiting to be stood on.

I vibe-coded a quantum-chemical program in one day

I vibe-coded a quantum-chemical program in one day

During my PhD at the Mulliken Center for Theoretical Chemistry in Bonn, I wrote a quantum-chemical code called AICCM, an object-oriented educational implementation of the Cyclic Cluster Model (CCM) at the Hartree-Fock level. If you want the formal reference, it is described in:

M. F. Peintinger, T. Bredow, The cyclic cluster model at Hartree-Fock level, Journal of Computational Chemistry 35 (11), 839-846 (2014). DOI: 10.1002/jcc.23550

That work took a good chunk of my PhD. Years of implementation, debugging, validating against CRYSTAL and other periodic codes, reading obscure papers on Wigner-Seitz integration and boundary handling of three- and four-center integrals. Real work. The kind that leaves scars.

Alongside AICCM I also developed the pob-TZVP basis sets, a consistent triple-zeta valence family tuned specifically for periodic solid-state calculations. They were built for CRYSTAL, but from the start I had AICCM in mind as a second home for them:

M. F. Peintinger, D. V. Oliveira, T. Bredow, Consistent Gaussian basis sets of triple-zeta valence with polarization quality for solid-state calculations, Journal of Computational Chemistry 34 (6), 451-459 (2013). DOI: 10.1002/jcc.23153

These basis sets matter for the story later in this post, because vibe-qc already has a slot in its basis library waiting for them.

This week I listened to a podcast on vibe-coding, and the thought stuck in my head: what happens if I hand a modern coding agent a task I genuinely understand, something deeply non-trivial, and then resist the urge to help code? Not a toy CRUD app. Not a weekend script. A quantum chemistry program. The kind of thing where a single wrong sign in a density matrix silently gives you garbage for three hours of debugging before you notice.

So I created a blank git repo on my GitLab server, called it vibe-qc, and gave Claude the task. Just the task. No starter code, no reference implementation to crib from, no hand-holding on the math. I steered at the architecture level and made scoping decisions, but I did not write code. Not one line.

What I expected vs. what happened

I honestly expected it to crash out somewhere around the two-electron integrals, or to produce a plausible-looking SCF loop that silently disagreed with the reference by a few millihartree. That is the usual failure mode when someone writes this code for the first time: it runs, it looks right, and the total energy is wrong in the fourth digit.

Instead, by the end of the day we had a working program that agreed with PySCF to machine precision. It did fall into one trap I found amusing: at one point it tried to run restricted Hartree-Fock on lithium. Lithium has three electrons. You cannot do closed-shell RHF on an odd number of electrons. You need an open-shell method. We added UHF a few commits later and the problem went away, but it is telling that an AI coding agent can reproduce the exact kind of mistake a first-year grad student makes.

What got built in one day

16 commits. Here is the inventory.

Architecture

Python frontend with a C++17 numerical core, bridged by pybind11. CMake plus scikit-build-core for the build system, so pip install -e . just works. About 3500 lines of C++ for integrals, SCF drivers, gradients, and DFT machinery. libint2 v2.13.1 built from source under third_party/libint/ with max_am=5 and first-order derivatives enabled. Homebrew libxc 7.0 for the 500+ exchange-correlation functionals.

The GIL is released during native work, so the Python frontend is not a bottleneck. I had asked about this up front and was reassured that Python would not become a restriction for multi-core scaling later.

Hartree-Fock, closed and open shell

  • RHF with Hcore or SAD initial guess, symmetric orthogonalization via S to the minus one-half, optional density damping.
  • UHF with separate alpha and beta densities, reporting <S²> as a spin-contamination diagnostic. Closed-shell UHF collapses to RHF at 1e-14.
  • Pulay DIIS convergence accelerator. On H₂O we measured 52 iterations (with damping 0.5) collapsing to 9 iterations (with DIIS). Per-spin DIIS for UHF.
  • SAD initial guess (Superposition of Atomic Densities) that runs a fractional-occupation atomic SCF per unique element. This was added specifically after UHF hit a wrong local minimum on OH in 6-31G*. The fix was immediate.
  • Analytic nuclear gradients for both RHF and UHF via the Pople-Binkley formula with an energy-weighted density matrix. Verified against PySCF at 1e-8 Ha/bohr.

Density Functional Theory

  • Numerical integration grid: Treutler-Ahlrichs M4 radial (Chebyshev-2nd-kind nodes) combined with a Gauss-Legendre-in-cos(θ) and uniform-φ angular product, partitioned across atoms by Becke fuzzy cells with the iterated switch function. The default medium grid integrates the H-1s density to 1e-10.
  • AO evaluation on the grid: both χ and ∇χ at every grid point, supporting Cartesian or pure spherical shells. Analytic AO gradients verified against finite differences at 1e-10.
  • libxc wrapper accepting alias names (“LDA”, “PBE”, “B3LYP”) or explicit comma-separated XC_… IDs, with hybrid fractions detected automatically.
  • RKS SCF driver with full energy decomposition (E_core, E_J, α·E_K, E_xc, E_nuc). Matches PySCF to 5e-11 Ha on LDA and to grid accuracy (~1e-6 Ha) on B3LYP and PBE.

ASE integration

This was the decision I am most happy about in retrospect. Rather than write a custom input parser, we wired the code into ASE (the Atomic Simulation Environment) as a proper Calculator subclass. That gave us, for free:

  • Input readers for XYZ, CIF, PDB, POSCAR, Gaussian inputs, and basically every format chemists actually use.
  • Geometry optimization via ase.optimize.BFGS. H₂O in HF/STO-3G with a distorted starting geometry converges in 7 BFGS steps to r(OH) = 0.989 Å and angle HOH = 100.0°, exactly the known literature value.
  • Implicit method routing: HF with multiplicity 1 goes to RHF with forces, HF with multiplicity > 1 goes to UHF with forces, DFT with multiplicity 1 goes to RKS (energy-only at day’s end).
  • Clean unit handling via ase.units.Bohr and ase.units.Hartree.

Basis set library

90 standard Gaussian .g94 basis sets shipped by libint are exposed automatically (STO-3G through aug-cc-pVQZ, the def2 family, plus the JKFIT, JFIT, and CABS auxiliary sets). On top of that there is a basis_library/custom/ directory where a user can drop their own .g94 files. A setup script assembles the union, with custom files overriding standard ones by name. This path is ready for dropping in my own pob-TZVP and pob-TZVP-D basis sets (the ones I developed for solid-state calculations) once I dig up the files.

Tests

131 tests across 13 files, full suite runs in about 10 seconds. Every numerical feature is cross-checked against PySCF as the reference. A representative slice of what actually passes:

  • H₂ / STO-3G at R = 1.4 bohr: E_HF = −1.116714325063 Ha, difference from PySCF = −1.87e-14.
  • H₂O / STO-3G at experimental geometry: E_HF = −74.964453863067 Ha, difference from PySCF = −2.84e-14.
  • OH doublet / STO-3G UHF: <S²> = 0.7533 (ideal 0.75, about 0.3% spin contamination).
  • H₂O / STO-3G / LDA: 5e-11 Ha vs PySCF.
  • H₂O / STO-3G / B3LYP: 1.3e-7 Ha (grid-accuracy limited).
  • CH₄ / 6-31G* / PBE: under 3e-6 Ha.

Documentation

A README with quick-start examples for both macOS and Linux, a detailed installation doc covering Homebrew, Debian/Ubuntu, Fedora/RHEL, and Conda paths, a quickstart tour, and a basis-library README. All written by the same agent, in one day, alongside the code.

What I deliberately did not add yet

UKS (unrestricted Kohn-Sham), analytic DFT gradients, ROHF, OpenMP inside the Fock and ERI-gradient loops, MPI for multi-node, post-HF correlation methods like MP2 and CCSD, and of course the actual cyclic cluster model. Every foundation for CCM is now in place: libint with periodicity hooks available, ASE’s CIF reader already wired in, and a validated HF/DFT SCF stack on finite molecules that I trust.

How I steered it

If you are thinking about trying this yourself, the interesting part was not the code, it was the scoping. A few patterns I noticed in my own behavior over the day:

Anticipatory infrastructure. Early in the morning, when the code was still just producing one-electron integrals, I made the decision to build libint from source with first-order derivatives enabled. Homebrew’s libint would have been faster to set up, but it did not include the derivative integrals. I knew we would need gradients for geometry optimization later, and I did not want to tear down and rebuild the install halfway through the day. That single 10-minute decision paid off twice: once for HF gradients, once for UHF gradients.

Validation before features. After RHF produced a plausible number, I pushed for formal pytest coverage against PySCF before we added DIIS. That caught two real bugs immediately: a Cartesian-vs-spherical d-orbital basis-purity issue, and a last-iteration MO self-consistency bug. Both were silent errors. Neither would have shown up if I had just eyeballed the total energy.

End-user surface before internals. We wired in the ASE Calculator and the logging integration before adding forces, before DFT, before SAD. The moment I caught the calculator running silently without emitting any trace, I paused the next phase and inserted a logging phase. It is much easier to debug a 50-iteration SCF when you can see the DIIS error norm falling.

Realistic descoping. My opening plan was “UHF and ROHF, then DFT.” By mid-afternoon it was “UHF only, ROHF can wait.” On the DFT grid, I accepted a Gauss-Legendre product angular grid instead of a proper Lebedev grid for the MVP. These were not compromises on correctness, they were compromises on sophistication, and they let us ship a working DFT implementation the same day.

Commit cadence. Every phase got its own focused commit, with a descriptive message, passing the full test suite. No force-pushes, no amends. 16 clean revertable checkpoints.

Standing on shoulders

None of this would have been possible in a day if the hard parts had not already been solved by other people. The integral engine is libint (Ed Valeev), the 500+ DFT functionals come from libxc (Susi Lehtola and co.), linear algebra is Eigen, the Python bindings are pybind11, the user-facing surface is ASE (Larsen et al.), and every single number in the test suite is cross-checked against PySCF (Qiming Sun et al.) as the reference oracle. On the methods side we are implementing formulas from Pulay (DIIS), Pople, Krishnan, Schlegel and Binkley (analytic HF gradients), Treutler and Ahlrichs (radial grid), and Becke (atomic partitioning). Full citations and a guided tour of the stack are in the companion post.

Watching it work was the best part

I need to say this plainly: I had a blast. I spent the day with Anthropic’s Claude Opus 4.7 as a pair-programmer, and watching the model work through this problem was genuinely thrilling. Every time I came back from a coffee or a meeting, there were new commits, clean ones, with descriptive messages, and a test suite that had grown and still passed. The SCF converged on H₂O. Then DIIS landed and the iteration count dropped from 52 to 9. Then gradients showed up and BFGS walked the water molecule to its equilibrium geometry. Then DFT arrived and B3LYP matched PySCF to grid accuracy. Each of those moments felt like a little gift.

What took me multiple years during my PhD, Opus 4.7 produced in a day. Not because vibe-qc is AICCM, it is not. AICCM implements real periodic CCM with four-center integrals at Wigner-Seitz boundaries, which is a genuinely hard problem and is not in vibe-qc yet. But the molecular HF + DFT + gradients + ASE integration layer, which in a traditional PhD timeline is itself a one- or two-year project, collapsed into a single afternoon. That is a kind of leverage I have never experienced before, and I am grinning about it.

The PhD was not wasted, quite the opposite. The reason I could steer this build effectively, and the reason I knew to demand a SAD guess the moment UHF stalled, and the reason I caught the lithium-on-RHF mistake instantly, is because I spent those years learning quantum chemistry from the inside. The expertise did not become obsolete. The expertise became the steering wheel, and it turns out steering is a deeply satisfying way to use what you know.

The really exciting part is what this unlocks. Ideas that used to be “interesting but I cannot justify six months to try it” suddenly become “let us see what happens this weekend.” The research questions I filed away after my PhD, the ones I always meant to come back to, are now within reach in a way they were not last year. That is a good day.

The next step, when I find another day, is the cyclic cluster model. That is the part where I find out whether the model can actually extend beyond well-trodden textbook ground into the corner of the literature that I personally carved out a decade ago. I am genuinely curious. I will report back.

The code lives at vibe-qc.com. If you want to reproduce the experiment yourself, the recipe is: a blank repo, a clear scoping brief, and the discipline to let the model cook.

Wine (Crossover Office 18) and Microsoft Office 2016

I use Ubuntu (18.10) for work. For RDP I use a Virtual Machine running Windows 10. While LibreOffice is great for many things, its Microsoft Office comparability is limited. Especially PowerPoint presentations do not look great. Google Slides does a great job here. As long as I have an internet connection it is my preferred choice for office, also due to its collaboration features. Sometimes I still need to use original Microsoft Office applications. Running them in a VM is one option, but the integration into the filesystem, especially with my Nextcloud sucks though.

Years ago I was running an older version of office with WINE. I was not in the mood to play around with the config so I bought Crossover Office 2018 and give Microsoft Office 2016 (32 bit) a try. The good thing is the money will find its way towards the WINE project.

Out of the box Word, Excel, Access, Outlook and Publisher seem to work. PowerPoint did not. It took me a while to figure it out and Google did not help much. But after disabling ‘Performance Enhanced Graphics’ for the bottle it launches.

Rye Sourdough

In addition to the three-flour sourdough I always keep a standard Rye sourdough for all kind of wheat/rye based breads. The instructions to prepare and feed it are pretty much identical.

In a high plastic container or a sourdough or pickling crock add 60g of rye flour, a knife tip of active dry yeast and 100g of water.

Stir and put the lid on top leaving a small gap for air.

To feed it add 30g of rye flour 30g of water every 24 hours.

After day one you should observe small bubbles on top of the dough and a light sour smell.

Starting from day four you are ready to use the dough. It should have a nice sour smell and a creamy consistency.

Over time you’ll also develop a good feeling for the water-flour proportion. So add more water in case you think the dough is too dry.