Skip to main content

VitaScore GFM

The VitaScore Gap Filling Module calculates human health impacts of food products based on dietary risk factors from the Global Burden of Disease (GBD) study. It converts food composition into Disability-Adjusted Life Years (DALYs), enabling health impact comparisons alongside environmental metrics.

Quick Reference

PropertyDescription
Runs onFoodProductFlowNode
DependenciesAggregationGapFillingWorker, DailyFoodUnitGapFillingWorker
Key InputFood category amounts, nutrient values, daily food unit
OutputVitaScore in DALYs per dietary risk factor
TriggerProduct has aggregated food categories and daily food unit

When It Runs

The module triggers when:

  1. Aggregation of food categories is complete
  2. Daily Food Unit (DFU) calculation is complete
  3. The node has valid amount_per_category_in_flow data
  4. The node has a valid daily_food_unit value

Key Output

The module produces:

  • Total VitaScore: Aggregated health impact in DALYs
  • Per-factor scores: Individual DALY contributions from each dietary risk factor
  • Both current and legacy versions: Two calculation methods for comparison

Scientific Methodology

VitaScore quantifies the health impact of food consumption using Disability-Adjusted Life Years (DALYs) based on the Global Burden of Disease study. One DALY represents the loss of one year of full health.

Global Burden of Disease Framework

The GBD study identifies dietary risk factors that contribute to disease burden. The VitaScore uses 13 risk factors grouped into two categories:

Positive Risk Factors (higher consumption is beneficial):

  • Diet low in fruits
  • Diet low in vegetables
  • Diet low in whole grains
  • Diet low in nuts and seeds
  • Diet low in milk

Negative Risk Factors (higher consumption is harmful):

  • Diet high in sodium
  • Diet high in processed meat
  • Diet high in red meat
  • Diet high in energy
  • Diet high in fat
  • Diet low in fat
  • Diet high in protein
  • Diet low in protein

Theoretical Minimum Risk Exposure Level (TMREL)

Each dietary risk factor has an optimal consumption range called the TMREL - the exposure level that minimizes health risk. The VitaScore calculates how far actual consumption deviates from this optimal range.

The TMREL is defined by two bounds:

  • t_1: Lower bound of optimal range
  • t_2: Upper bound of optimal range

The VitaScore Formula

The core formula calculates a risk fraction (f_r) for each dietary risk factor, then multiplies by the associated DALY burden:

For positive factors (where consuming more is better):

f_r = (t_2 / (t_2 - t_1)) - x_r / (scaling * (t_2 - t_1))

For negative factors (where consuming less is better):

f_r = x_r / (scaling * (t_2 - t_1)) - (t_1 / (t_2 - t_1))

Where:

  • x_r: Amount of the risk factor in the food product
  • t_1, t_2: TMREL lower and upper bounds
  • scaling: Normalization factor (see below)
  • f_r: Risk fraction (clamped to 0-1 range)

Final calculation:

VitaScore[r] = DALY_r * f_r
Total VitaScore = sum(VitaScore[r] for all risk factors)

Normalization Methods

The VitaScore supports three normalization methods for different risk factors:

MethodDescriptionUsage
per_dfuScale by Daily Food UnitMost risk factors (fruits, vegetables, meat, etc.)
per_total_energyScale by total energy contentFat and protein as percentage of calories
per_selfNo scaling (factor = 1.0)Direct measurements

Daily Food Unit (DFU)

The Daily Food Unit represents the typical daily consumption amount of a food category. It normalizes the calculation so that a food's health impact is proportional to how much of the daily diet it represents.


Implementation Details

Covered Dietary Risk Factors

The module calculates health impacts for 13 dietary risk factors. Each factor has a Theoretical Minimum Risk Exposure Level (TMREL) defining the optimal consumption range.

Positive Risk Factors (Higher Consumption is Beneficial)

Risk FactorTMREL LowerTMREL UpperUnitNormalizationDALYDietary Guideline Reference
Diet low in fruits200300g/dayper_dfu61.24WHO recommends ≥400 g/day fruits and vegetables combined1
Diet low in vegetables340500g/dayper_dfu30.57WHO recommends ≥400 g/day fruits and vegetables combined1
Diet low in whole grains100150g/dayper_dfu82.86US guidelines: 3-4 servings/day (~48-64 g)2
Diet low in nuts and seeds1625g/dayper_dfu13.98EAT-Lancet: ~50 g/day nuts3
Diet low in milk350520g/dayper_dfu9.422-3 servings dairy/day (~400-600 ml)4

Negative Risk Factors (Higher Consumption is Harmful)

Risk FactorTMREL LowerTMREL UpperUnitNormalizationDALYDietary Guideline Reference
Diet high in sodium (salt)15g/dayper_dfu29.43WHO recommends <5 g salt/day (<2 g sodium)5
Diet high in processed meat04g/dayper_dfu96.94EAT-Lancet: avoid or minimize processed meat3
Diet high in red meat1827g/dayper_dfu74.25EAT-Lancet: ~14 g/day (~100 g/week)3

Macronutrient Risk Factors (Energy and Percentage of Total Energy)

Risk FactorTMREL LowerTMREL UpperUnitNormalizationDALYDietary Guideline Reference
Diet high in energy6501250kcalper_self88.66A balanced meal: 450-850 kcal (~⅓ of 2200 kcal/day)4
Diet high in fat27.5%50%% of kcalper_total_energy88.66EFSA/US guidelines: 20-35% of energy from fat46
Diet low in fat5%27.5%% of kcalper_total_energy88.66EFSA/US guidelines: 20-35% of energy from fat46
Diet high in protein22.5%60%% of kcalper_total_energy88.66US AMDR: 10-35% of energy from protein6
Diet low in protein0%22.5%% of kcalper_total_energy88.66US AMDR: 10-35% of energy from protein6

DALY values are expressed as DALYs per 100,000 person-years and represent the disease burden attributable to each dietary risk factor.

Sodium to Salt Conversion

Since dietary guidelines often refer to salt (sodium chloride) rather than sodium, the module converts sodium values:

# Sodium is approximately 39.3% of salt by mass
NA_PERCENT_IN_NACL = 39.337 # %

# Convert sodium to salt equivalent
salt_quantity = sodium_quantity * (100 / NA_PERCENT_IN_NACL)

Macronutrient Energy Conversion

Fat and protein are converted from mass to energy for percentage-of-calories calculations:

FAT_CALORIES_PER_GRAM = 9.0      # kcal/g
PROTEIN_CALORIES_PER_GRAM = 4.0 # kcal/g

fat_kcal = fat_grams * FAT_CALORIES_PER_GRAM
protein_kcal = protein_grams * PROTEIN_CALORIES_PER_GRAM

Version Support

The module maintains two calculation versions:

VersionDescription
currentLatest GBD 2021 DALY values
legacyOriginal DALY values for backward compatibility

Both versions are calculated and stored, allowing comparison of results across methodology updates.


Full Code Reference

Main Calculation Flow

async def run(self, calc_graph: CalcGraph) -> None:
"""Perform vitascore calculation."""
# Get aggregated food categories and daily food unit
amount_per_category_in_flow_prop = self.node.amount_per_category_in_flow
dfu_prop = self.node.daily_food_unit

# Add salt and nutrition-derived risk factors
salt_qty, energy_qty, fat_qty, protein_qty = (
self.retrieve_salt_and_nutrition_amounts_for_activity_production_amount()
)

# Compute vitascore for both versions
for version in (VitascoreVersionEnum.legacy, VitascoreVersionEnum.current):
vitascore_for_local = self.compute_vitascore(
amount_per_category_in_flow_prop,
dfu_prop,
version,
ReferenceAmountEnum.amount_for_activity_production_amount,
)
# ... store results

Core VitaScore Formula Implementation

def vitascore_formula(
self,
food_category: QuantityPackageProp,
dfu: float,
legacy_or_current: str,
for_reference_amount: ReferenceAmountEnum,
) -> dict[UuidStr, ReferencelessQuantityProp]:
"""Formula for computing vitascore."""
vitascore = {
self.gfm_factory.vitascore_term.uid: ReferencelessQuantityProp(
value=0.0, unit_term_uid=self.gfm_factory.daly_unit_term.uid
)
}

for idx, term in enumerate(
self.gfm_factory.positive_terms + self.gfm_factory.negative_terms
):
if term.xid in self.gfm_factory.used_categories_xid:
# Get TMREL bounds from term data
t_1 = float(term.data.get("tmrel").get("lower"))
t_2 = float(term.data.get("tmrel").get("upper"))
normalization = TmrelNormalizationEnum(
term.data.get("tmrel").get("tmrel_normalization")
)

# Get amount from food categories
if term.uid in amount_per_category_in_flow_present:
x_r = float(amount_per_category_in_flow_present[term.uid].value)
else:
x_r = 0.0

# Get DALY burden for this risk factor
daly_r = float(term.data.get(legacy_or_current).get("amount"))

# Determine scaling factor
if normalization == TmrelNormalizationEnum.per_dfu:
scaling = dfu
elif normalization == TmrelNormalizationEnum.per_total_energy:
scaling = energy_kcal_value
elif normalization == TmrelNormalizationEnum.per_self:
scaling = 1.0

# Calculate risk fraction
if scaling <= 0.0:
f_r = 0.0
else:
if idx < len(self.gfm_factory.positive_terms):
# Positive factors: more is better
f_r = (t_2 / (t_2 - t_1)) - x_r / (scaling * (t_2 - t_1))
else:
# Negative factors: less is better
f_r = x_r / (scaling * (t_2 - t_1)) - (t_1 / (t_2 - t_1))

# Clamp risk fraction to [0, 1]
f_r = max(0, min(1, f_r))

# Calculate and accumulate vitascore
vitascore[term.uid] = ReferencelessQuantityProp(
value=daly_r * f_r,
unit_term_uid=self.gfm_factory.daly_unit_term.uid
)
vitascore[self.gfm_factory.vitascore_term.uid].value += daly_r * f_r

return vitascore

Salt and Nutrition Retrieval

def retrieve_salt_and_nutrition_amounts_for_activity_production_amount(
self,
) -> Tuple[Optional[ReferencelessQuantityProp], ...]:
"""Extract salt and macronutrient quantities from nutrition data."""

# Check aggregated nutrients first, then nutrient values
if aggregated_nutrients := self.node.aggregated_nutrients:
nutrition_pkg = aggregated_nutrients.amount_for_activity_production_amount()
elif nutrient_values := self.node.nutrient_values:
nutrition_pkg = nutrient_values.amount_for_activity_production_amount()

# Handle sodium chloride splitting
if nutrition_pkg and (sodium_chloride_term.uid in nutrition_pkg.quantities):
nutrition_pkg = split_sodium_chloride(nutrition_pkg)

# Convert sodium to salt
if sodium_term.uid in nutrition_pkg.quantities:
salt_qty = nutrition_pkg.quantities[sodium_term.uid].duplicate()
salt_qty.value *= 100 / NA_PERCENT_IN_NACL

# Get energy and convert fat/protein to kcal
energy_kcal_qty = nutrition_pkg.quantities.get(energy_term.uid)

if fat_mass_qty := nutrition_pkg.quantities.get(fat_term.uid):
fat_in_grams = convert_to_grams(fat_mass_qty)
fat_qty = ReferencelessQuantityProp(
value=fat_in_grams * FAT_CALORIES_PER_GRAM,
unit_term_uid=kcal_term.uid
)

if protein_mass_qty := nutrition_pkg.quantities.get(protein_term.uid):
protein_in_grams = convert_to_grams(protein_mass_qty)
protein_qty = ReferencelessQuantityProp(
value=protein_in_grams * PROTEIN_CALORIES_PER_GRAM,
unit_term_uid=kcal_term.uid
)

return salt_qty, energy_kcal_qty, fat_qty, protein_qty

Calculation Example

Scenario: Calculate VitaScore for 100g of processed ham

Step 1: Identify Risk Factor Amounts

From food category aggregation:

  • Processed meat: 100g
  • Red meat: 0g (counted separately from processed)
  • Fruits: 0g
  • Vegetables: 0g

From nutrient values:

  • Sodium: 1200mg (converts to 3050mg salt)
  • Energy: 145 kcal
  • Fat: 8g (72 kcal)
  • Protein: 18g (72 kcal)

Step 2: Calculate Risk Fractions

Diet high in processed meat (negative factor):

  • TMREL: 0-0g per day (optimal is zero consumption)
  • Scaling: Daily Food Unit (assume 2000g daily food intake)
  • Normalized amount: 100g / 2000g = 0.05 (5% of daily intake)
  • Risk fraction: Calculated based on TMREL deviation

Diet high in sodium:

  • TMREL: 1000-5000mg per day
  • Salt amount: 3050mg
  • Risk fraction: Based on where 3050mg falls in TMREL range

Diet low in fruits (positive factor):

  • Amount: 0g
  • Risk fraction: Maximum (no fruit content)

Step 3: Apply DALY Values

Each risk fraction is multiplied by the associated DALY burden from GBD data:

  • Processed meat DALY burden: ~40 DALYs per 100,000 person-years
  • Sodium DALY burden: ~30 DALYs per 100,000 person-years

Step 4: Sum Total VitaScore

Total VitaScore = sum of (DALY_r * f_r) for all 13 risk factors

The result is expressed in micro-DALYs per serving, representing the proportional health burden.


Data Sources

Global Burden of Disease Study

The VitaScore is based on the GBD 2021 study:

GBD 2021 Risk Factors Collaborators (2024). Global burden and strength of evidence for 88 risk factors in 204 countries and 811 subnational locations, 1990-2021: a systematic analysis for the Global Burden of Disease Study 2021. The Lancet.

Key data extracted:

  • DALY burdens for each dietary risk factor
  • TMREL ranges for optimal consumption
  • Relative risk estimates

TMREL Values

The Theoretical Minimum Risk Exposure Levels are sourced from:

  1. GBD 2021 Appendix 1: Detailed TMREL specifications for each risk factor
  2. Lancet Supplementary Materials: Supporting data for TMREL derivation

Mediation Factors

The GBD uses mediation factors to account for overlapping risk pathways. For example, red meat impacts health both directly and through its effect on body mass index (BMI).


Known Limitations

Methodological Constraints

  • Population averages: DALY values represent global or regional averages, not individual risk
  • Age/gender specificity: Current implementation uses population-wide values (age/gender-specific values available but not implemented)
  • Risk factor overlap: Mediation factors partially but not fully address correlated risks

Data Limitations

  • Food matching: Requires accurate food category classification
  • Nutrient completeness: Sodium/fat/protein calculations depend on complete nutrition data
  • TMREL uncertainty: Optimal consumption ranges have inherent scientific uncertainty

Coverage Gaps

  • Country specificity: DALY values can be made country-specific but currently use global estimates
  • Processed foods: Complex processed foods may have incomplete category mapping
  • Novel foods: New food categories may lack established risk factor associations

References

  1. GBD 2021 Risk Factors Collaborators (2024). Global burden and strength of evidence for 88 risk factors in 204 countries and 811 subnational locations, 1990-2021. The Lancet, 403(10440), 2162-2203.

  2. Eaternity (2017). VitaScore Documentation. https://eaternity.org/assets/smart-chefs/2017-12-12_VitaScore_Documentation_web.pdf

  3. Ernstoff, A., et al. (2020). Towards Win-Win Policies for Healthy and Sustainable Diets. Nutrients, 12(9), 2745.

  4. Murray, C.J.L., et al. (2020). Global burden of 87 risk factors in 204 countries and territories, 1990-2019: a systematic analysis for the Global Burden of Disease Study 2019. The Lancet, 396(10258), 1223-1249.

  5. GBD Results Tool. Institute for Health Metrics and Evaluation. https://vizhub.healthdata.org/

Footnotes

  1. WHO Healthy Diet Fact Sheet 2

  2. US Dietary Guidelines and Whole Grains

  3. EAT-Lancet Planetary Health Diet 2 3

  4. EFSA Dietary Reference Values 2 3 4

  5. WHO Sodium Reduction

  6. US Dietary Guidelines - Acceptable Macronutrient Distribution Ranges 2 3 4