Deep Dive: Mass Standardization & Yield
Gram is deeply rooted in physical reality. Unlike basic markdown parsers that just bold text, the @gram/analyzer understands that a "cup of flour" and a "cup of water" have completely different weights.
This page explains the physics engine behind Gram's mass and yield calculations.
The Three Pillars of Physics
Gram separates the physical properties of ingredients into three distinct concepts within your database. This strict separation ensures the math is always perfect, whether you write in volumes, counts, or weights.
- Density (
density): Used strictly to convert Volumes to Mass.- Example:
1 cup milk -> 240g(using a density of 1.03 g/ml).
- Example:
- Unit Weight (
unit_weight): Used strictly to convert Counts to Mass.- Example:
1 avocado -> 150g(the average weight of one whole avocado).
- Example:
- Yield (
yield): The Waste Factor. Used strictly to calculate the ratio of edible Net Mass to purchasing Gross Mass (due to peels, seeds, bones).- Example: An avocado has a yield of 0.70 (30% is the pit and skin).
The Goal of Mass Standardization
The primary goal of the Analyzer is to calculate the Total Mass of a recipe. This is essential for nutritional estimation, cost calculation, and baking ratios.
To do this, the Analyzer must convert every ingredient quantity into a unified baseline: grams (g).
The Conversion Algorithm
When the Analyzer encounters an ingredient, it runs it through a strict priority algorithm to find its mass:
- Precise Mass: If you wrote
@flour{200g}or@beef{1kg}, the Analyzer does no work. It simply converts it to grams. - User Override: If you provided a specific density override in the recipe's YAML frontmatter (e.g.,
densities: { flour: 0.55 }), it uses this explicit value to convert the volume you requested into mass. - Database Density: If you requested a known volume (
ml,cup,tbsp,tsp) and youringredients.yamlhas adensityfield for that ingredient, it calculatesVolume * Density = Mass. - Database Unit Weight: If you requested a count (
@egg{3}or@garlic{2 cloves}) and your database has aunit_weightfield, it calculatesCount * Unit Weight = Mass. - Incomplete: If a volume has no density available (no override, no database entry) or a count has no
unit_weight, the Analyzer refuses to guess. It will not assume a default density (e.g. treating everything as water) — doing so would silently corrupt shopping-list totals. The mass calculation fails, the ingredient is marked as "incomplete", and it is listed in its raw, unconverted unit.
Yield Calculation (The Waste Factor)
In professional kitchens, there is a fundamental difference between what goes into the pot and what you buy at the store.
- Net Mass (Edible Portion): The weight of the ingredient after peeling, coring, or trimming.
- Gross Mass (Purchasing Weight): The weight of the ingredient as purchased.
The ratio between these two is the Yield Factor.
How Gram handles Yield
By default, Gram assumes that any ingredient written in your recipe refers to the Net Mass. The shopping list generator then works backwards using the yield and unit_weight to tell you exactly how much to buy.
Let's look at two scenarios for an avocado (unit_weight: 150g, yield: 0.70):
Scenario A: Writing in Counts (@avocado{1})
- The Analyzer reads "1 unit".
- It applies the
unit_weight:1 * 150g = 150g. This is the Gross Mass (the whole purchased avocado) — stored as the ingredient'spurchasingMass. - It applies the
yieldforward to find what actually goes into the recipe:150g * 0.70 = 105g. This is the Net Mass — the value used for the recipe's total mass and nutrition calculation.
Scenario B: Writing in Mass (@avocado{200g})
- The Analyzer reads "200g". It assumes this is the Net Mass you need in the bowl.
- It applies the
yieldbackwards to find the shopping list weight:200g / 0.70 = 285g. This is the Gross Mass. - It can use the
unit_weightto tell you how many to buy:285g / 150g = 1.9 avocados(so, buy 2).
This ensures you never end up short on ingredients after prepping them.