DEV Community

Cover image for Peptide calculator math: the water volume is the free variable
Mingye Yang
Mingye Yang

Posted on

Peptide calculator math: the water volume is the free variable

A 10 mg vial and a U-100 syringe tell you almost everything you need. The one number they don't give you is how much water to add, and every number after that depends on it.

This post is about that variable and the code that handles it.

Three units, two dimensions

Most of the bugs I hit came from treating one dimension as another.

  • mg / mcg is how much peptide. Before mixing, it's dry powder.
  • mL is how much liquid. That includes the water you add.
  • units is a mark on the syringe, not a volume you can hold. On a U-100 barrel, 1 unit = 0.01 mL, so 100 units = 1 mL.

A milligram is not a millilitre. Concentration is the only place they meet.

The two formulas

const UNITS_PER_ML = 100;

const concentration = (vialMg, waterMl) => vialMg / waterMl;        // mg per mL
const unitsToDraw  = (targetMg, conc) => (targetMg / conc) * UNITS_PER_ML;
Enter fullscreen mode Exit fullscreen mode

Worked example, the one I keep coming back to:

10 mg vial + 2 mL water   = 5 mg/mL
250 mcg target            = 0.25 mg
0.25 / 5                  = 0.05 mL
0.05 x 100                = 5 units
Enter fullscreen mode Exit fullscreen mode

Add 4 mL instead of 2 and the same 250 mcg becomes 10 units. Same vial, same target, different draw. That is why "I take 10 units" from someone else's post is not information you can use.

Inverting the question

Most calculators take the water volume as an input. But the water is the one thing you actually get to choose. Concentration is derived from it.

So the interesting direction is: given vial mg, a target amount and a syringe capacity, which water volumes produce a draw you can physically read?

function candidates(vialMg, targetMg, syringeMl, maxWaterMl, step = 0.1) {
  const rows = [];
  const steps = Math.round(maxWaterMl / step);

  for (let i = 1; i <= steps; i++) {
    const waterMl = +(i * step).toFixed(2);      // integer steps, formatted on output
    const conc = vialMg / waterMl;
    const units = (targetMg / conc) * UNITS_PER_ML;

    if (units > syringeMl * UNITS_PER_ML) continue;   // does not fit the barrel
    rows.push({ waterMl, conc, units });
  }

  return rows;
}
Enter fullscreen mode Exit fullscreen mode

The loop counts in integer steps and formats afterwards. Incrementing a float by 0.1 inside the loop gives you 0.30000000000000004, and that number eventually ends up in a UI.

Then rank by how close the draw lands to a graduation the barrel actually has:

function roundingGap(units, graduation = 1) {
  const nearest = Math.round(units / graduation) * graduation;
  return { nearest, errorUnits: +(nearest - units).toFixed(3) };
}
Enter fullscreen mode Exit fullscreen mode

If a candidate needs 7.5 units on a barrel marked in whole units, the arithmetic is correct and the measurement is still wrong. Small errorUnits first, then smaller water volume.

vial target water concentration draw units readable
5 mg 250 mcg 1 mL 5 mg/mL 0.05 mL 5 yes
5 mg 250 mcg 1.5 mL 3.33 mg/mL 0.075 mL 7.5 no, half a mark
5 mg 250 mcg 2 mL 2.5 mg/mL 0.1 mL 10 yes

The bugs that matter

mcg vs mg. 250 mcg is 0.25 mg. One dropped factor of 1000 is enough to be dangerous, so both units stay parseable and the conversion happens at the boundary, never mid-calculation.

Syringe capacity is a hard constraint. A 0.3 mL barrel tops out at 30 units. A suggested volume that produces 45 units is not a plan, it's an error message.

Vial capacity. "Add 2 mL" does not fit into a 3 mL vial that already holds powder and air. A suggestion that cannot be carried out is worse than no suggestion.

Floating point. Covered above, but it bit me twice: once in the loop, once when formatting to two decimals and dropping a trailing zero.

Already reconstituted vials. If it's already mixed, the water volume is unknown and the search above is meaningless. That has to be a separate mode where concentration comes from the user instead of being computed.

What it refuses to do

The tool never picks a dose, a schedule or a product. It converts numbers you supply and shows the steps. Reconstitution is a measurement problem. Choosing what to measure is a different problem, and not one a static page should answer.

If you want to poke at the implementation: peptide calculator — the reconstitution mode and the dosage view are the two entry points.

If you find a rounding case I got wrong, tell me.

Top comments (0)