Deep Dive: Nutritional Estimation
The @gram/analyzer is capable of automatically computing the macronutrient and micronutrient profile of any recipe.
However, because nutritional data is highly sensitive and impacts dietary choices, Gram takes a strict, mathematically conservative approach to estimating nutrition. This page explains that logic.
The Calculation Flow
- Mass Standardization: Before any nutritional calculation can occur, the Analyzer must first convert every single ingredient in the recipe into a standard mass in grams. (See Mass Standardization).
- Database Lookup: The Analyzer queries your
ingredients.yamldatabase for thenutritionblock of each ingredient. The database values must always represent the nutrients per 100g of the raw ingredient. - Proportional Scaling: The Analyzer scales the per-100g database values to match the actual mass used in the recipe.
- Aggregation: The scaled values for all ingredients are summed to calculate the Total Recipe Nutrition.
- Portion Division: If the recipe's frontmatter defines
portions: 4, the Total Recipe Nutrition is divided by 4 to provide Per-Portion data.
Transparent Partial Data Reporting
The most important philosophy of Gram's nutritional engine is: never hide the estimate, but never let the user mistake it for the truth.
If you are cooking a meal with 10 ingredients, and only 9 of them have nutritional data in your database, silently displaying the sum of those 9 ingredients would risk being read as a complete, precise total when it actually understates the real caloric content.
Rather than withholding the total outright, Gram surfaces it alongside the information needed to judge its reliability:
- The
total(andperPortion, ifportionsis set) is always computed and returned from whatever data is available — it is never blanked out. - A
coverageratio is always included (e.g.0.9for "90% of ingredients have data"), computed as the share of mass-bearing ingredients that had anutritionblock. - A
warningslist flags exactly which ingredients are missing from the database, missing theirnutritionblock, or have an unresolvable mass — so the gap is attributable, not just a vague caveat.
The reference HTML renderer reflects this directly: it always shows the nutrition panel whenever there is any data (or any warning) to show, with a Coverage: X% badge next to it. This gives the cook a genuinely useful number immediately, while making it clear at a glance how much of the recipe that number actually accounts for — rather than replacing a "90% accurate estimate" with nothing at all.
Handling Modifiers
Gram's syntax modifiers impact nutritional calculations in specific ways:
- Optional Ingredients (
?): Ingredients marked as optional (e.g.,@?whipped cream) are excluded from the base nutritional totals. The Analyzer assumes the most conservative dietary baseline. - Alternatives (
|): When a recipe provides alternatives (e.g.,@butter{50g} | @oil{40g}), the Analyzer only calculates the nutrition for the first (preferred) option. It does not average them. - Composite Ingredients (
<@): The nutrition of a composite ingredient is calculated purely from its children, each looked up independently in the database by its own id (e.g.,lemon juiceandlemon zestare two distinct entries with different macros). There is no fallback to the parent's own nutritional data — if a child ingredient has nonutritionblock, it is treated the same as any other missing ingredient: it contributes to theMISSING_MACROSwarning and lowerscoverage, rather than being estimated from the parent.