Notes

Teaching a puddle to dance

October 24, 2026 · 13 min read · Part 2, electrolytes: the molecules recognized and represented in Part 1 become the salt, solvents and additives of a liquid. Extending a polarizable force field, a fitting loop that learns from its own failures, three heads that compress 40,000 molecular movies into one latent space, and an electrolyte co-scientist trained on all of it

internshipmolecular-dynamicsforce-fieldsrepresentation-learning

In Part 1 molecules got recognized and turned into vectors; here they get mixed into electrolytes, and the question becomes how they move. I extended a polarizable force field to 627+ molecules and 20+ anions, turned the failures of more than 50 of 80 versions into a self-improving fitting loop, and taught a model to read about 40,000 molecular-dynamics movies through three heads and one shared 128-number space. That space predicts conductivity with R² 0.976, and everything I learned along the way became training material for an electrolyte co-scientist.

627+molecules and 20+ anions in the force field
50+ of 80force-field versions failed in the liquid
~40,000MD trajectories, roughly 200 million frames
R² 0.976conductivity from the shared 128-d space
18 of 21held-out chemistry groups improved by trajectory features

How do molecules move in a liquid?

The question I worked on for this part was how molecules move in their micro-environment, and it starts with what an electrolyte actually is.

A crowded dance floor

An electrolyte is the liquid inside a battery that ions swim through. The recipe is simple: salt + solvents + additives, and those are exactly the molecules from Part 1, now with jobs. The behavior is not simple. Zoom in and it’s a crowded dance floor: lithium ions grab solvent molecules as partners, anions try to cut in, and how fast everyone gets across the floor decides how well the battery charges and discharges.

Two electrodes marked plus and minus, with ions and solvent molecules between them and motion arrows
The dance floor: ions picking up and dropping partners as they go

A rulebook and a movie

To watch that dance on a computer you need two things. A force field is the rulebook for how atoms push and pull on each other. Molecular dynamics (MD) is playing the movie with that rulebook: put lots of molecules in a box, apply the rules, and watch. If the rulebook is wrong, the movie is fiction. Very confident, beautifully rendered fiction.


Writing a bigger rulebook

My first job was to grow the rulebook so that the molecules people actually want to try have rules to follow. The force field is polarizable (PFF), meaning electron clouds can squish in response to their neighbors, which matters a lot around small, charged ions like lithium. The goal was coverage: extend it to 627+ molecules and 20+ anions, for both lithium and sodium electrolytes.

Reference data from quantum chemistry

The reference data comes from quantum chemistry (DFT), and lots of it. Some clusters are binary: the ion with one partner. Some are ternary: a solvent and an anion competing for the same ion, like two people reaching for the last slice of pizza. Each pair or trio gets computed at many distances and orientations, so the rulebook sees close calls, near misses and awkward side-on approaches, not just the polite pose.

Li+, a solvent and an anion forming a three-body triangle, with an energy curve being fit
Fitting the rulebook: Li+, a solvent, an anion, and the three-body case where they all meet

What actually got fit

What I fit was mainly the exp-6 cross terms, keeping the charges and the polarization background fixed. All the while I was debugging, learning electrolyte science on the fly, and studying the properties that came out. Three jobs at once, which is roughly how every internship works.

Deep dive: what exp-6 means

Exp-6 is the part of the rulebook that covers short-range repulsion (“personal space!”) and dispersion, the weak, sticky attraction between everything. The “exp” is an exponential wall that keeps atoms from overlapping; the “6” is a gentle pull that falls off with the sixth power of distance.


The liquid gets a vote

The hardest lesson of the summer was that a fit isn’t done when the cluster error is low; it’s done when the liquid behaves. Density, radial distribution functions (who stands next to whom), diffusion, conductivity and viscosity all have to come out sensible in MD. My ledger ended up indexing 80 distinct force-field versions, and more than 50 of them failed. Here are a few of my favorite ways to fail.

The penguin huddle

One fit looked great on the clusters. Errors low, curves lined up, very satisfying. Then I ran the liquid and it clumped: the ions huddled together like penguins in a blizzard. I traced it to one ether-oxygen interaction the fit had effectively broken. Restoring it cut the ion aggregation fraction from 0.811 to 0.282, and from 0.415 to 0.044 in a second system. Conductivity moved closer to experiment, though it was still too low. Progress, not victory.

The wall that rounded to zero

A tiny coefficient, 0.00170, got rounded to zero on its way into the simulation. The builder treated zero as “missing” and helpfully swapped a big repulsive wall for a small one, and MD failed. Now a check reads the final built parameters, not just the ones I meant to build.

The helium incident

Lone-pair virtual sites stored with the label “He” were passed to DFT as real helium atoms. That quietly affected 27 of 61 clusters. Nothing says summer internship like accidentally inventing noble-gas electrolyte chemistry. Now templates, masses and site types are checked before anything goes out.

The failure that was a success

A salt-free solvent control came out at a viscosity of about 2.1–2.4 mPa·s, which told me the solvent was fine and the problem lived in the salt model.

An electrolyte has many jobs, and a rulebook that only nails small clusters can still flunk all of them in the liquid. What I learned is that a low DFT fitting error doesn’t protect the liquid, and MD validation is part of parameter development, not a final demo.


PFF RSI: turning failures into checks

Fifty-odd failures is a lot of pain, but it’s also a lot of data, so I distilled them into a recursive self-improvement system, PFF RSI.

PFF RSI

At its core it’s a simple cycle: fit the force field, validate in MD, record the failures, refine the next fit. When a brand-new molecule shows up, the loop runs on its own: sample, fit, guard, check in MD, learn, repeat.

Loop diagram: sample DFT clusters, fit exp-6 cross terms, guard against known failures, check in MD, learn by turning failures into checks, then back to sampling
Fit → validate in MD → record failures → refine the next fit, run on its own whenever a new molecule arrives.

Four layers under the cycle

Underneath the cycle, I built four layers.

Memory

A saved failure: the system, the version, what changed, what I expected versus what I measured, and what it cost, stored so it can be searched later by conditions, bad parameters, symptoms and useful checks.

Context

Loading the relevant failures before a new fit.

Code harness

Turning a failure into an automatic check, a new procedure written as code, so the same mistake gets caught by a machine instead of by me at 11 pm.

Weights

The fitted parameters that the whole loop keeps refining.

A new molecule arrives

When a brand-new molecule shows up, the loop samples a small starting set of clusters and expands only the channels that failed, fits, and checks. Parameters that match a known failure pattern get intercepted and sent to MD for a look before anyone uses them, so the wall that rounded to zero and the helium incident can’t sneak through twice. The honest caveat is that a fixed-budget test proving this memory actually makes research faster is still on my to-do list. It feels faster. Feelings aren’t data.


40,000 movies and three heads {#40000-movies-and-three-heads}

Once the rulebook works you can make movies, lots of them, and then you hit a new problem: none of it is text. Each trajectory is 5,000 frames, one every 4 picoseconds, for 20 nanoseconds in total. I ended up with about 40,000 trajectories, roughly 200 million frames.

A timeline of simulation frames from frame 1 to frame 5,000, 4 picoseconds apart, 20 nanoseconds in total
One movie: 5,000 frames, 4 ps apart, 20 ns in total. Now multiply by 40,000.

You can’t paste a trajectory into a chatbot and ask how it felt. It’s coordinates, boxes and time, and the interesting part (who solvates whom, how ions hop) is spread across thousands of frames. So I asked whether a model could learn what a liquid is by watching, and squeeze each one into something small enough to search, compare and predict with.

Three views of the same liquid

I looked at the same liquid through three views, each with its own encoder, or “head.”

1. Recipe

What’s in the bottle: the components, their amounts, and conditions like temperature, turned into 128 numbers.

2. Snapshot

One frozen frame of 3D atoms. It’s my favorite, so it gets its own subsection below.

3. Trajectory

Motion over time: the 5,000 frames are split into 50 chunks of 100, so the model sees how things move, not just a pose.

All three are compressed into one shared 128-d latent space and trained with three-way contrastive alignment: views of the same liquid are pulled together, views of different liquids pushed apart. A recipe should find its own movie, and a snapshot should find its own recipe. That one space then does three jobs: retrieval, alignment between views, and property prediction.

Recipe, snapshot and trajectory encoders feeding one shared 128-number latent space, which feeds retrieval, alignment and property prediction
Three heads, one 128-d space, three uses.

What the space can do

For retrieval, retrieval@10 (is the matching liquid in the top 10?) was 84.1% with the full representation versus 82.4% from the static snapshot alone, so a single frame already knows a lot. For conductivity, the model reached R² 0.976, close to a strong descriptor baseline, and adding trajectory features improved conductivity error in 18 of 21 held-out chemistry groups: motion helps, especially on chemistry the model hasn’t seen. Across the property heads, I got R² 0.906, 0.936 and 0.987 for conductivity, viscosity and solubility. The representation also carried over from lithium to sodium electrolytes with only a simple alignment step.

From there I took two more steps. Reverse design starts from the properties you want and looks for recipes that land there. I also tried trajectory generation, rolling the movie forward with GRU, TCN and causal-transformer models, though I won’t go into that here.

Inside the snapshot encoder

Snapshot encoder

The snapshot encoder is where the physics goes into the architecture, the same way rotation did in Part 1. Each frame becomes a periodic atom graph, and equivariant message passing turns it into 128 learned numbers plus 8 geometric ones, such as lithium coordination and ion contacts: 136 numbers per frame.

Every atom connects to neighbors within 6 Å, using the minimum-image rule across the box boundaries (the box wraps around like Pac-Man), and whole molecules are kept intact instead of getting sliced at the edge. Each atom then carries two kinds of luggage: plain numbers (scalars) and little arrows (vectors). Messages are built from distances and directions, so when you rotate the box, the arrows rotate right along with it and the final pooled numbers don’t change at all. The model doesn’t waste effort learning that a rotated liquid is the same liquid (the same “atom is atom” spirit as Part 1), while still keeping directional information like which way a solvent faces a lithium ion.

Deep dive: the unglamorous speedups

The unglamorous part made everything else possible. Neighbor search went from O(N²) to O(N): instead of every atom asking every other atom “are you close?”, each atom only checks its own neighborhood. Then I froze the encoder and cached about 192 million snapshots once, which took 395 H200 GPU-hours and a cache of about 52 GB. After that, a training step went from 12.6 s to 0.15 s. That’s the difference between “let’s try an idea” and “let’s try an idea next week.”


The electrolyte co-scientist

Everything above is how I came to understand formulation, and the last step was to hand that understanding to something that could use it.

Curated tasks, MD logs and bugs, and tool definitions feed a fine-tuned LLM inside a harness of tools, checks and a loop, producing an electrolyte co-scientist whose proposals a researcher reviews
Curated data + logs and bugs + tool definitions → fine-tuned LLM inside a harness → co-scientist, with a researcher reviewing.

Three kinds of training material

Task records

Formulation and MD cases, reading evidence, a public conductivity dataset (CALiSol), and policy examples: 254,527 rows in the model’s chat and tool format (rows are tasks, not independent formulations).

Logs and bugs

The MD engine’s simulation logs, bugs and failures. The helium incident lives on.

Tools

Tool-usage records plus the tool definitions themselves.

I fine-tuned an open-weight LLM with LoRA for about one epoch, 63,632 steps on one GPU. Fine-tuning teaches the model the task format, how to read evidence, and when and how to call tools. It does not turn the model into a calculator: the numbers still come from the data and the programs.

The harness does the rest

Electrolyte co-scientist

A tool registry (file reading, protocol checks, analysis programs, evidence records, and PFF RSI itself) and a loop: the researcher asks, the LLM plans, and the harness retrieves experiments, calls the explanation and generation models, executes approved simulations, and compares results with saved artifacts.

Deep dive: how a simulation request is handled

Preparing a run is kept separate from submitting it. A request carries the formulation, protocol, resources and a fingerprint, gets validated before a job is ever created, is recognized if it’s a repeat (no burning GPU time on the same run twice), and can recover after a failure. Raw trajectories stay on the tool side; the LLM sees summaries, not 5,000 frames of coordinates.

Project map: the molecule loop (VERDICT, paper mining, VLM SFT, multimodal RAG) and the electrolyte branches (DFT samplings for Li and Na, multi-fit, PFF RSI, DIY PFF; 40K MD, representation, generation, retrieval) feeding an electrolyte co-scientist
The project map: both electrolyte branches feed the co-scientist.

Three tool fixes I’m proud of

The tools had their own growing pains.

Units

An unsupported unit used to look like “no data,” so the model would conclude nothing existed; now the tool rejects it clearly.

Error messages

With short error messages, the model kept retrying with guessed arguments; now errors spell out the full argument contract.

Identity

One request passed every numeric check while “DMC” was carrying the SMILES of DMF, a different molecule with a confusingly similar name; now the checker verifies chemical identity, which is Part 1’s lesson coming back to collect.

What it can do

It can explain why a simulation failed, how a formulation performed, or how to prepare a run at a new temperature, and the researcher stays in charge by reviewing the next formulations it proposes. This was also when I noticed I was building a way of working more than a single model: represent the liquid, align its views, predict, then hand it to a co-scientist. I’d love to call that a method of my own, but the field moves so fast that I kept adding new pieces to it all summer, so for now it’s a living one.


What this got us

Electrolyte icon: a droplet with Li+ and solvent molecules
Electrolytes
Battery cell icon: a pouch cell with plus and minus tabs
go into cells

Part 2 gave us a rulebook that covers the molecules people want to try and that learns from its own mistakes, a 128-number description of each liquid learned from recipes, frames and motion, and a co-scientist that knows how to read all of it. Nobody buys a puddle, though. These formulations only matter once they’re sealed inside a real battery, next to electrodes, getting charged and discharged for months, and that’s where Part 3 takes them.

All notes