EPD Templating
EPD Templates define the verified modeling envelope for a given tool within a project. They validate performance at defined boundary conditions and confirm that the tool enforces approved limits without exceeding the verified scope.
Smart EPD provides the framework and logic for defining, enforcing, and versioning template constraints. The specific setpoints, ranges, and rules within a template are determined by tool providers, manufacturers, consultants, and verifiers during the verification process. Smart EPD facilitates; it does not prescribe.
How templates relate to specifications
A template is not authored directly. It is the resolved output of the specification inheritance chain. Each level can define or override constraints, and the template is what results when the full chain is evaluated:
The specification chain defines the rules. The verifier reviews, adjusts, and approves the constraints. The result is a locked, versioned EPD Template that governs every EPD created within that project.
Within an EPD Template, each field has a defined state:
- Locked fields -Prefilled values that cannot be changed (e.g., declared unit, methodology references)
- Unlocked fields -Editable by the project team, but subject to validation constraints
- Constrained fields -Unlocked, but bounded by range constraints
Templates and Reference EPDs
A template defines what is allowed — locked values and input bounds — but it is not itself a declaration, and it cannot be verified in the abstract: there is nothing concrete to check until real values exist. Supplying those values is the job of a Reference EPD — the fully populated instance that fills in every field and anchors a product family.
The template and its Reference EPD are therefore verified together, in a single pass. Approving the Reference EPD simultaneously confirms the template's locked fields, its input bounds, and that a complete, conformant EPD comes out the other side. Once approved, the template governs every variant derived from that reference.
In short: the template is the rulebook and the form; the Reference EPD is that form filled with representative numbers to prove the rulebook works; variant EPDs are validated against the same template constraints when they are generated.
Template characteristics
Each EPD Template represents a bounded modeling framework. The dimensions along which a template defines its scope are flexible and project-specific, but common examples include:
Manufacturing plants or plant archetypes
The template may scope which manufacturing facilities are covered. A product manufactured at Plant A under different energy mixes than Plant B may require separate templates or plant-level parameterization.
Manufacturing process archetypes
The template may define which production processes are in scope, such as extrusion, injection molding, or assembly. Different processes may produce different environmental profiles even for the same product family.
Material families
The template may bound which material families are permitted, such as PE, PVC, or PP for a plastics manufacturer. Materials outside the defined families fall outside the verified scope.
Parameter ranges (input constraints)
The most nuanced dimension. Parameter ranges define the allowable variability for inputs that drive the LCA model: dimensions, material composition, energy use, waste ratios. These ensure variant EPDs stay within the verified modeling envelope.
The dimensions listed above are illustrative examples. EPD Templating is flexible: a template can define scope along any dimension relevant to the product category and verification context. The platform does not prescribe a fixed set of bounding dimensions.
Input range constraints
Smart EPD supports four constraint types for validating input values: range, statistical, formula, and allowed_values. These are not mutually exclusive: a single template can combine several to create a comprehensive validation framework. The verifier determines which types to apply and what specific values to set.
Any constraint can also be conditional via the applies_if_json field — the constraint is only evaluated when the specified condition is true.
Range constraints
Numeric min/max bounds on an input value with configurable inclusivity (inclusive, exclusive, left-open, right-open). Catches data entry errors and enforces verified scope boundaries. Most templates will have range constraints on the majority of their inputs.
Example: Total product mass must be between 0.5 and 25 kg. PE content must fall within 0.30–0.78 kg PE / kg product.
Range constraints handle absolute bounds, ratio-based bounds, and any simple min/max pattern. The parameters_json keys are min, max (either may be omitted for an open-ended bound), and bounds_mode — one of inclusive (default), exclusive, left_open, or right_open.
{ "min": 0.5, "max": 25, "bounds_mode": "inclusive" }
Statistical constraints
Distribution-based bounds using z-score checks. Values outside a defined statistical threshold from the population mean are flagged as outliers. Supports both normal and lognormal distributions.
Example: GWP value more than 2 standard deviations from the category mean is flagged.
The parameters_json keys are distribution ("normal", the default, or "lognormal") and threshold — the maximum allowed z-score (default 3.0). A normal distribution takes mean and std_dev; a lognormal distribution takes geometric_mean (> 0) and geometric_std_dev (> 1) instead.
{ "distribution": "normal", "mean": 50, "std_dev": 10, "threshold": 2 }
Most valuable for mature product categories with many existing declarations.
Formula constraints
Constraints defined by an arbitrary mathematical expression evaluated against EPD data. Where range constraints handle simple bounds, formula constraints handle relationships across multiple fields — non-linear interactions, cross-field comparisons, and domain-specific expressions.
The expression computes a number; the separate operator and bound parameters then compare that number against a threshold. The parameters_json keys are:
| Key | Required | Meaning |
|---|---|---|
expression | yes | Math expression evaluated against the input data. Must produce a number. |
operator | no (default lte) | One of eq, lt, lte, gt, gte, between. An unrecognized operator falls back to lte. |
threshold | with the single-sided operators (default 0) | The bound the computed value is compared against. |
min, max | with between | Inclusive bounds: the constraint passes when min <= computed <= max. Either may be omitted for an open end. |
{ "expression": "gwp_a1_a3 / total_mass", "operator": "lte", "threshold": 12.5 }
{ "expression": "pe_content + pp_content", "operator": "between", "min": 0.30, "max": 0.78 }
Cross-field comparisons are written by moving everything to one side of the expression and comparing against 0:
{ "expression": "gwp_a1_a3 - base_gwp * (1 + 0.15 * recycled_fraction)", "operator": "lte", "threshold": 0 }
- The bound key is
threshold, notvalue. Unknown keys inparameters_jsonare silently ignored andthresholddefaults to0— so{"operator": "lte", "value": 1000}actually enforces<= 0. - Do not write the comparison inside the expression (
"a + b <= 1000"withoperator: "eq"). The engine evaluates the comparison to a boolean, coerces it to1.0/0.0, and compares that against the threshold — inverting your intent. Rearrange onto one side instead, as above. eqcompares floats with a small tolerance (1e-9), not exact equality.
Expression grammar
Expressions are evaluated with a sandboxed engine (simpleeval) — no builtins, imports, or attribute access.
- Variables: every numeric input field is available by its
field_id(numeric strings are coerced). Non-numeric fields are excluded — referencing one makes the constraint not evaluable rather than failing it. The special name_valueholds the current value of the field the constraint is attached to. External inputs are referenced with theirext__prefix (declared per template — see Reading the template). - Identifiers: a field can only be referenced if its ID is a valid identifier — letters, digits, and underscores, not starting with a digit (
gwp_totalworks;gwp-totalcannot be referenced). - Functions:
abs,min, andmax—min/maxaccept any number of arguments (min(a, b, c)). Nothing else works: noround, nopow(use**), and nosum— the sandbox rejects the list syntaxsumneeds, so usea + b + cinstead. - Arithmetic:
+,-,*,/,//,%,**(exponent magnitude is capped for safety), unary+/-, and parentheses. - Result: must be a number. Comparison and conditional syntax technically parse, but their boolean results coerce to
1.0/0.0— keep comparisons in theoperatorfield.
A division by zero or a reference to an absent field marks the constraint not evaluable (still reported as a violation); an unparseable expression marks it misconfigured. Neither silently passes.
The term "parametric" is avoided here intentionally. In the EPD and LCA context it carries a specific meaning related to parameterized and variant EPDs. Formula constraint is used to prevent ambiguity in documentation and audit trails.
Allowed-values constraints
Checks a text (or multiselect) value against a fixed list of permitted strings — built for fields like LCI background datasets where the PCR publishes the exact list manufacturers may choose from.
The parameters_json keys are values (required, the list of allowed strings) and case_sensitive (optional, default false — comparison trims whitespace and ignores case). List inputs pass only when every item is allowed. Blank input passes: this constraint only polices provided values; required-ness is enforced separately by the field's is_required.
{ "values": ["Ecoinvent 3.9", "Ecoinvent 3.10", "GaBi 2023"], "case_sensitive": false }
Constraint model structure
Every constraint in the system follows the same data model:
| Field | Type | Description |
|---|---|---|
constraint_type | string | One of: range, statistical, formula, allowed_values |
parameters_json | object | Constraint-specific parameters — see each type above for its exact keys (min/max/bounds_mode for range; distribution/threshold/mean/std_dev for statistical; expression/operator/threshold or min/max for formula; values/case_sensitive for allowed_values). Unknown keys are silently ignored. |
severity | string | error (blocks variant approve/auto-approve, and a reference EPD's submission when the template enforces its constraints unless the author acknowledges it), warning (surfaced to verifier), or info (informational). Default error. |
applies_if_json | object or null | Optional conditional logic — when present, the constraint is only evaluated if the condition is true |
reference_field_id | string | Template-level constraints only: the anchor field — see below |
label | string | Template-level constraints only: display name shown in the editor and review surfaces |
The anchor field (reference_field_id)
Spec-level constraints are defined on an input field, so they anchor to it implicitly. Template-level constraints (entries of template_constraints) declare their anchor explicitly via reference_field_id. The anchor determines three things:
- Attribution — violations report the anchor as their
field_id, including for formula constraints that reference many fields in the expression. - The required value — when the constraint applies, the anchor field must have a value; a missing (or, for the numeric types, blank) anchor value is itself reported as a violation before the constraint logic runs. An unmet
applies_if_jsongate suppresses this too — a gated-off constraint reports nothing. (allowed_valuesis the exception for blanks: a provided but blank anchor passes, per its blank semantics above.) _value— in formula expressions,_valueis the anchor field's (numeric) value.
reference_field_id is requiredSaving a template-level constraint with an empty reference_field_id is rejected with a 400 validation_error. (Constraints stored before this validation existed are silently skipped during evaluation — they still appear in verifier checklists but never produce violations — so if you have a legacy unanchored constraint, re-save it with a real field ID.)
Conditional application
Any constraint type can be made conditional via applies_if_json. This handles product configurations where certain validation rules are relevant only for specific variants:
Example: If the product contains recycled content, the recycled content fraction range constraint only applies.
Shape
applies_if_json is a JSON object using a small grammar. A leaf condition names an input field, an operator, and an expected value:
{
"field": "recycled_content",
"operator": "gt",
"value": 0
}
Supported operators:
| Operator | Meaning | Notes |
|---|---|---|
eq (default) | Equal | String comparison |
neq | Not equal | String comparison |
in | Value is in a list | value must be an array |
gt, gte, lt, lte | Numeric comparisons | Both sides coerced to float |
present | Field has a non-null value | No value needed |
absent | Field is missing or null | No value needed |
Leaf conditions can be combined with all (logical AND) or any (logical OR), and the combinators nest recursively:
{
"all": [
{ "field": "category", "operator": "eq", "value": "concrete" },
{ "field": "strength_mpa", "operator": "gte", "value": 20 },
{
"any": [
{ "field": "admixture", "operator": "present" },
{ "field": "recycled_content", "operator": "gt", "value": 0 }
]
}
]
}
Semantics
nullor an empty object → the constraint always applies.- Field references resolve against the same input data that drives constraint evaluation, so
applies_if_jsoncan gate a constraint on the value of any sibling field in the template. - A numeric comparison (
gt/gte/lt/lte) whose sides can't coerce tofloatfalls through to "apply the constraint" — those operators fail open. - The other operators evaluate against the missing value when the referenced field is absent:
eq,in, andpresentthen read as unmet (the constraint is skipped), whileneqandabsentread as met. Gate on fields you know will be present, or combine with apresentcondition. - Send only the documented shape. Combinator values (
all/any) must be arrays of condition objects — malformed shapes are outside the contract and can fail the validation call rather than being ignored.
applies_if_json in the systemConstraint-level applies_if_json is distinct from the requirement-level applies_if_json used for document search, and from show_if_json used for form-field visibility. Each has its own evaluator and its own shape — don't copy snippets between them.
Constraint type summary
| Constraint Type | Complexity | When to Use |
|---|---|---|
| Range | Low | First layer: catches data entry errors, enforces ratio bounds |
| Statistical | Medium | Outlier detection against population data (z-score) |
| Formula | Medium–High | Multi-variable or non-linear relationships not expressible as a simple range |
| Allowed values | Low | Text fields limited to a published list (e.g. permitted LCI datasets) |
Sensitivity analysis
Before defining input range constraints, it is strongly recommended that the tool provider and verifier collaborate on a sensitivity analysis to identify which inputs have the greatest impact on EPD results.
A common approach is to identify inputs responsible for the top 80% of GWP variance. High-impact inputs should receive the tightest constraints, while lower-impact inputs can receive wider bounds.
This is a recommended best practice, not enforced by the platform. Sensitivity-driven constraint design leads to templates that are rigorous where it matters and flexible where it doesn't.
Out-of-bounds handling
When a manufacturer attempts to generate a variant EPD with input values that fall outside the template's verified bounds, the platform enforces a structured response:
When an out-of-bounds input is detected:
- All violations are identified at once (not just the first one found)
- Each violation shows which input is out of range, what the valid range is, and by how much
- The "Generate EPD" action is disabled until all inputs are within bounds
- A "Request Template Update" action is available to initiate a template revision
Related pages
- Terminology -Constraint types reference and template versioning overview
- Specifications & Validation -How the specification inheritance chain works