> ## Documentation Index
> Fetch the complete documentation index at: https://docs.realtimelca.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Compare EPDs to reduce impacts

> Use the MCP to shortlist relevant EPDs for a material and measure what each one does to your project GWP

export const ChatAgent = ({children, label = 'Agent'}) => <div style={{
  display: 'flex',
  justifyContent: 'flex-start'
}}>
    <div style={{
  maxWidth: '95%',
  minWidth: '40%'
}}>
      <div style={{
  fontSize: '0.7rem',
  textTransform: 'uppercase',
  letterSpacing: '0.06em',
  opacity: 0.55,
  marginBottom: '0.3rem'
}}>
        {label}
      </div>
      <div style={{
  background: 'rgba(128, 128, 128, 0.09)',
  border: '1px solid rgba(128, 128, 128, 0.25)',
  borderRadius: '12px 12px 12px 2px',
  padding: '0.4rem 1rem',
  overflowX: 'auto'
}}>
        {children}
      </div>
    </div>
  </div>;

export const ChatUser = ({children, label = 'You'}) => <div data-chat-turn="user" style={{
  display: 'flex',
  justifyContent: 'flex-end'
}}>
    <div style={{
  maxWidth: '88%',
  minWidth: '40%'
}}>
      <div style={{
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'flex-end',
  gap: '0.5rem',
  marginBottom: '0.3rem'
}}>
        <span style={{
  fontSize: '0.7rem',
  textTransform: 'uppercase',
  letterSpacing: '0.06em',
  opacity: 0.55
}}>
          {label}
        </span>
        <button type="button" aria-label="Copy prompt" className="px-2 py-1 flex items-center gap-1 justify-center rounded-lg border cursor-pointer transition-colors duration-70 outline-offset-4 text-xs/[16px] whitespace-nowrap font-medium border-transparent bg-primary text-white hover:bg-primary-dark" onClick={event => {
  const button = event.currentTarget;
  const turn = button.closest('[data-chat-turn]');
  const body = turn && turn.querySelector('[data-chat-body]');
  if (!body) return;
  navigator.clipboard.writeText(body.innerText.trim());
  button.textContent = 'Copied';
  setTimeout(() => {
    button.textContent = 'Copy';
  }, 1500);
}}>
          Copy
        </button>
      </div>
      <div data-chat-body="true" style={{
  background: 'rgba(99, 102, 241, 0.12)',
  border: '1px solid rgba(99, 102, 241, 0.35)',
  borderRadius: '12px 12px 2px 12px',
  padding: '0.4rem 1rem',
  overflowX: 'auto'
}}>
        {children}
      </div>
    </div>
  </div>;

export const Chat = ({children}) => <div style={{
  display: 'flex',
  flexDirection: 'column',
  gap: '0.75rem',
  margin: '1.25rem 0'
}}>
    {children}
  </div>;

Two EPDs for the same material can move a project's GWP by more than the
material itself is worth arguing about. Datasets differ in scope, in
end-of-life scenario, in density, and in how much of the life cycle they
declare. Picking one from a search result list is guesswork.

The MCP closes that gap with two tools. One shortlists EPDs that are
actually relevant to a material and declared in a unit it can be mapped
to. The other measures each candidate by putting it on the material and
reading the recalculated project dashboard, so you compare real project
numbers rather than per unit values on a datasheet.

<Warning>
  There is no what-if sandbox behind this. The comparison **temporarily
  maps** each candidate onto the target material, reads the dashboard,
  then **restores the original mapping** (or removes it, if the material
  started unmapped). Restoration always runs, including when a candidate
  fails. Your project is briefly written to during the run, so do not
  start one while a colleague is reading the same project's dashboard.
</Warning>

## Prerequisites

* An RTLCA project with materials in its inventory
  ([add materials](/use-cases/add-materials-to-inventory) first if not)
* An AI client connected to the [RTLCA MCP server](/ai-tools/rtlca-mcp)
* The target material needs a **quantity** and a **lifetime**. The tool
  checks both and stops before writing anything if either is missing

The walkthrough below uses the `Sample Lighthouse CPH` project. Substitute
your own names.

<Steps>
  <Step title="Pick the material worth comparing" icon="crosshairs">
    Comparison costs a full dashboard recalculation per candidate, so spend
    it where the project GWP actually sits. Let the agent find the
    contributors first.

    <Chat>
      <ChatUser>
        Which materials contribute most to GWP in Sample Lighthouse CPH?
      </ChatUser>

      <ChatAgent>
        Top five leaf materials by share of project GWP:

        | Material                  | Current EPD                     | Share |
        | ------------------------- | ------------------------------- | ----- |
        | In-situ concrete, slabs   | Ready-mix concrete, C30/37      | 31%   |
        | Reinforcement steel       | Armeringsstål, generisk         | 14%   |
        | Glulam 24h, roof          | Limtræ til konstruktion af gran | 9%    |
        | Mineral wool, facade      | Mineraluld, facadebatts         | 6%    |
        | Gypsum board, inner walls | Gipsplade 12,5 mm               | 4%    |

        The slab concrete is where the project GWP sits, and it's mapped to a
        generic `Ready-mix concrete, C30/37`. Shall I shortlist alternatives
        for it?
      </ChatAgent>
    </Chat>

    <Tip>
      Only **leaf** materials can be compared. Parent type rows in the
      inventory are aggregates, and the tool will tell you so if you pass
      one by mistake.
    </Tip>
  </Step>

  <Step title="Shortlist relevant EPDs" icon="list-check">
    Free-text search returns whatever matches the string. The shortlist
    tool ranks by relevance server side and filters to units the material
    can actually be mapped in, so you get candidates instead of results.

    <Chat>
      <ChatUser>
        Shortlist EPD candidates for the slab concrete.
      </ChatUser>

      <ChatAgent>
        I built the search from the row's identifier, type and category, and
        let the backend rank it. Nine candidates, all C30/37:

        1. Ready-mix concrete, C30/37, GWP.40
        2. Ready-mix concrete, C30/37, GWP.55
        3. Ready-mix concrete, C30/37, GWP.70
        4. Ready-mixed M concrete C30/37 CEM II/B-M(Q-LL) 52.5N (LA)
        5. RMC C30/37 M Vibfri CEM II/B-M 52,5 N
        6. UNI-GREEN BETON (C30/37 Passive Umweltexpositionsklasse)
        7. Transportbeton C30/37 CEM II/B-M
        8. Ready-mix concrete, C30/37, GWP.REF
        9. Fabriksbeton C30/37

        Shall I measure what each one does to the project total?
      </ChatAgent>
    </Chat>

    <Note>
      Mass and volume are not incompatible. A kg declared EPD maps fine to
      an m³ material and the other way round, because the backend aligns
      them via density. Do not filter a shortlist down to a single unit.
    </Note>
  </Step>

  <Step title="Run the comparison" icon="scale-balanced">
    Up to **10 candidates** per run. The agent should tell you it is about
    to write to the project and wait for your go ahead.

    <Chat>
      <ChatUser>
        Yes, compare all nine against the current mapping.
      </ChatUser>

      <ChatAgent>
        To measure these I have to map each candidate onto the slab row one at
        a time and read the dashboard, then put `Ready-mix concrete, C30/37`
        back. The project is written to during the run and restored at the
        end. Proceed?
      </ChatAgent>

      <ChatUser>
        Go ahead.
      </ChatUser>

      <ChatAgent>
        <img src="https://mintcdn.com/real-timelca/FIpEusQLvdTsGTPf/images/use-cases/compare-epds.png?fit=max&auto=format&n=FIpEusQLvdTsGTPf&q=85&s=6b7720c5b8eb52cd0a323fbc57f6a02e" alt="Comparison result headed 9 candidates compared, baseline Ready-mix concrete C30/37 = 6.045 kgCO2-eq/m2/year, with a green banner reading Restored: Restored original mapping. A ranked table lists ten rows with columns A1–A3, A4, A5, B, C, D, GWP total, delta vs current and delta percent: Ready-mix concrete C30/37 GWP.40 at 4.904 (-18.9%) down to Fabriksbeton C30/37 at 5.963 (-1.4%), with Ready-mix concrete C30/37 (current) last at 6.045. Below, a Project GWP by EPD bar chart shows the lowest candidate in green and the current EPD in red." style={{ display: 'block', width: '100%', margin: '0.5rem 0', borderRadius: '8px', border: '1px solid rgba(128, 128, 128, 0.25)' }} width="791" height="902" data-path="images/use-cases/compare-epds.png" />

        EPD comparison for **In-situ concrete, slabs**. Nine candidates, all
        better than current:

        * Baseline (current): `Ready-mix concrete, C30/37` =
          **6.045** kg CO₂-eq/m²/year — last of ten
        * Best: `Ready-mix concrete, C30/37, GWP.40` = **4.904**
          (-1.141, **-18.9%**)
        * Range across the nine: 4.904 to 5.963, so even the weakest
          candidate beats current by 1.4%
        * Original mapping restored ✅

        Nothing has been committed. Want me to map the slab to `GWP.40`?
      </ChatAgent>
    </Chat>

    The ranked table carries a column per life-cycle phase (A1-A3, A4, A5,
    B, C, D), the project GWP total, and the delta in absolute and
    percentage terms, with the current mapping marked `(current)`. Below it,
    **Project GWP by EPD** ranks the totals with the winner in green and the
    current mapping in red, and a **GWP by life-cycle phase** breakdown
    splits each candidate so a difference in end-of-life or in module D
    credits shows up as its own segment rather than disappearing into the
    total.

    <Tip>
      Read the candidate **names** before you celebrate the number. Four of
      the rows above — `GWP.40`, `.55`, `.70` and `.REF` — are the same base
      dataset at different carbon tiers. Picking `GWP.40` is therefore a
      commitment to procure a lower-carbon mix, not a free 18.9%: someone
      has to specify it and a supplier has to deliver it. The three
      product-specific rows (`UNI-GREEN BETON`, `RMC C30/37 M Vibfri`,
      `Transportbeton`) are actual mixes you can ask for by name.
    </Tip>

    <Warning>
      The green **Restored** banner is the thing to check first, before any
      number below it. If it shows a warning instead of a tick, the target
      material is still sitting on the last candidate that was tried. The
      agent reports what it should be restored to. Fix it before you read
      any other dashboard number from that project.
    </Warning>
  </Step>

  <Step title="Commit the choice" icon="circle-check">
    The comparison commits nothing. To keep a candidate, map it explicitly.

    <Chat>
      <ChatUser>
        Yes, map the slab to Ready-mix concrete, C30/37, GWP.40.
      </ChatUser>

      <ChatAgent>
        Mapped `In-situ concrete, slabs` to **Ready-mix concrete, C30/37,
        GWP.40**, lifetime unchanged.

        Project GWP for this row: 6.045 to **4.904** kg CO₂-eq/m²/year, a
        1.141 reduction. Since the slab was 31% of the project total, that's
        the largest single-row improvement available in this inventory.
      </ChatAgent>
    </Chat>

    <Note>
      Lowest GWP is not automatically the right answer, and neither is
      "current wins". A low carbon product still has to be specifiable,
      procurable and appropriate for the structural class — and a mapping
      has to describe the material that is actually there. See
      [map materials to EPDs](/use-cases/map-materials-to-epds) for the
      dataset selection rules that apply to a BR18 project.
    </Note>
  </Step>
</Steps>

## What the comparison actually measures

Each row is a **whole project** figure, not a per material one. The tool
maps the candidate, reads the project dashboard, and records the total.
That is what makes the delta meaningful: it is the effect on the project
of swapping this one EPD, with every other mapping held still.

**Phases are rolled up.** The dashboard returns granular stages, and the
table groups them into A1-A3, A4, A5, B, C and D. The B and C columns are
the sum of their sub-stages, so a candidate with a different end-of-life
scenario shows the difference in the C column rather than hiding it in the
total.

**The reference depends on the starting state.**

| Material started | Baseline row                        | Deltas measured against  |
| ---------------- | ----------------------------------- | ------------------------ |
| Mapped           | The current EPD, marked `(current)` | The current EPD          |
| Unmapped         | None                                | The lowest GWP candidate |

For an unmapped material every candidate adds impact that was not
previously counted, so absolute totals all rise. The ranking is still the
answer to the question you asked, but read the deltas as candidate versus
candidate.

**Skipped candidates do not stop the run.** If the backend refuses to map
one, it is recorded with the reason and the remaining candidates still
compare. The current EPD is skipped automatically when it appears in the
candidate list, since it is already the baseline row.

## When every candidate is worse

Sometimes the run comes back with the current mapping on top and every
alternative above it. That is a result, but it is rarely the answer to the
question you asked — so check the baseline before you accept it.

Look at **what the shortlist considered relevant**. If you compared a
concrete row and the candidates came back as galvanised steel, the
shortlist is telling you that the row does not describe concrete. A
current mapping can win simply because it is the wrong dataset for the
material, priced far too low. Report that separately from the ranking: the
number is right and the mapping is still wrong.

Watch for **outliers that are orders of magnitude out**. A candidate
landing 100× or more above the rest, especially with a large module D
credit, is usually a declared-unit or density mismatch rather than a
genuinely catastrophic product. Discard those rows instead of reporting
them as part of the range — one of them will otherwise dominate any
summary you write.

## Before you get a useful answer

Two preconditions are enforced before any write happens:

* **The material needs a non-zero quantity.** Otherwise every candidate
  multiplies by zero, every delta is 0, and the comparison says nothing.
* **The material needs a lifetime.** Mapping fails with
  `lifespan not set` otherwise. Look the value up in the BR18 table and
  apply it first.

Both stop the tool before it touches the project, so a run that fails on
either leaves nothing to clean up.

## One-shot prompt

Once you know the project and the material, the whole flow fits in a
single prompt.

<Prompt description="Shortlist and compare candidate EPDs for a project material" icon="scale-balanced">
  ```text theme={null}
  Use RTLCA MCP tools to identify relevant EPD candidates for a project material, then compare their project-level GWP impact against the current mapping.

  ## Workflow

  1. Resolve the project.
     - Use `rtlca_list_projects`.
     - Match the user's project name to a project ID.
     - If multiple projects match, ask the user to choose.

  2. Resolve matching inventory materials.
     - Use `rtlca_get_inventory_materials` with `projectId` and `search`.
     - If multiple rows match, compare each row unless the user specifies one.
     - Record `materialId`, `identifier`, `typeIdentifier`, `branchName`, `category`, preferred quantity/unit, `lifeTime`, and current `mappedEpd`.

  3. Build the EPD search description.
     - Combine `identifier`, `typeIdentifier`, `category`, and `branchName`.
     - Include useful English/Danish synonyms if obvious.
     - Prefer the material's preferred declared unit.
     - Use `rtlca_find_relevant_epd_materials` with `count: 10` by default.

  4. Check comparison preconditions.
     - Do not compare if quantity is zero.
     - Do not compare if `lifeTime` is unset.
     - Report blocked rows clearly.

  5. Confirm temporary-write comparison.
     - `rtlca_compare_epds` temporarily changes project mappings and restores them afterward.
     - If the user explicitly asks to compare, treat that as confirmation.
     - Otherwise ask before calling it.

  6. Compare EPDs serially.
     - Run `rtlca_compare_epds` once per matching material row.
     - Do not run comparisons in parallel because the tool temporarily changes project mapping state.
     - Pass up to 10 EPD IDs from `rtlca_find_relevant_epd_materials`.
     - After each run, check `restoration.status`.
     - If restoration is not `restored`, lead with that warning.

  7. Interpret results.
     - Use `rtlca_compare_epds.rows` as the authoritative project-impact ranking.
     - Remember the current mapping may appear in `rows` even if it was not in the candidate list.
     - Distinguish the overall best row, the best alternative excluding `isCurrent: true`, and whether alternatives improve, tie, or worsen the baseline.
     - Flag suspicious current mappings, such as a pure material mapped to a construction assembly.

  8. Present results.
     - Prefer showing the UI/resource returned by `rtlca_compare_epds` when available.
     - Also provide a concise text summary.
     - For multiple rows, use a table with inventory row/category, current mapping, best alternative, GWP total, delta, delta percent, and restoration status.

  ## Output Rules

  If the current mapping is best, say "current mapping remains best among the relevant shortlist."

  If an alternative is better, name the alternative, GWP total, delta, and delta percentage.

  If the current mapping looks semantically wrong, call it out separately, even if the GWP result is close.

  Always mention skipped candidates or blocked rows.
  ```
</Prompt>

## Next

Once your mappings are settled, run a
[quality-assurance pass](/use-cases/quality-assurance) before generating
reports.
