Working with Formulae¶
This guide demonstrates how to use the dcclib.extraction.formulae
module to extract and evaluate formulas from Digital Calibration Certificates (DCCs).
A DCC can embed measurement models as MathML. dcclib parses
these into SymPy expressions, resolves their variables to the
referenced quantity values, and evaluates them — optionally with unit-aware arithmetic and
uncertainty propagation via dccQuantities.
Note
Evaluation automatically selects unit-aware arithmetic (via dccQuantities) when it
is installed, including uncertainty propagation. Without it, plain numeric evaluation
via mpmath is used. See the installation notes for
the optional quantities extra.
1. Extracting formulas from a DCC¶
The FormulaExtractor reads all MathML formulas from a DCC and resolves
their variables to the quantity values referenced via xref. Each formula is returned as a
DCCFunction.
from dcclib.extraction.formulae import FormulaExtractor, format_quantity
extractor = FormulaExtractor.from_file(formula_xml_path)
functions = extractor.extract()
print(f"Extracted {len(functions)} formula(s).")
for i, func in enumerate(functions):
print(f"\nFormula {i}: {func.name}")
print(f" Expression: {func.expression}")
print(f" Variables: {', '.join(func.variables) or '(resolved via dccQuantities)'}")
for key, val in func.variables.items():
val_str, unit_str = format_quantity(val)
print(f" {key} = {val_str} {unit_str}")
Note
Variables extracted from a DCC are resolved to dccQuantities objects. When the optional
quantities extra is not installed, func.variables is empty because the referenced
quantities cannot be materialised — extraction of the expression itself still works.
2. Evaluating an extracted formula¶
A DCCFunction is evaluated with its
evaluate() method. When the variables were resolved during extraction, it can be called
without arguments to use those embedded values. evaluate() returns one result per
evaluation step (variables holding lists are zipped, not combined cartesian-style).
from dcclib.extraction.formulae import FormulaExtractor, format_quantity
extractor = FormulaExtractor.from_file(formula_xml_path)
functions = extractor.extract()
for func in functions:
if not func.variables:
# Variables were not resolved (dccQuantities not installed); skip evaluation.
continue
print(f"Results for {func.name}:")
for res in func.evaluate():
inputs_str = ", ".join(f"{k}={format_quantity(v)[0]}" for k, v in res.inputs.items())
val_str, unit_str = format_quantity(res.output)
print(f" f({inputs_str}) = {val_str} {unit_str}")
3. Creating a DCCFunction manually¶
You can also build a DCCFunction yourself from a
name, a SymPy expression (or string), and a dictionary of variable values. Plain numeric
values work without any optional dependencies:
from sympy import symbols
from dcclib.extraction.formulae import format_quantity, wrap_variable_value
from dcclib.extraction.formulae.formula import DCCFunction
x, y = symbols("x y")
expr = x * y + 1
func = DCCFunction(name="MyFunc", expression=expr, variables={"x": 2.0, "y": 3.0}, bound_variables={"x", "y"})
for res in func.evaluate():
val_str, unit_str = format_quantity(res.output)
print(f"Result: {val_str} {unit_str}")
# Override a variable, keeping the original variable's unit metadata (as the CLI does):
overrides = {"y": 10.0}
eval_vars = func.variables | {k: wrap_variable_value(v, func.variables.get(k)) for k, v in overrides.items()}
for res in func.evaluate(variables=eval_vars):
val_str, unit_str = format_quantity(res.output)
print(f"Overridden result: {val_str} {unit_str}")
Evaluating over a list of inputs¶
A variable holding a list is evaluated element-wise, producing one result per entry:
from sympy import symbols
from dcclib.extraction.formulae import format_quantity
from dcclib.extraction.formulae.formula import DCCFunction
t = symbols("t")
func = DCCFunction(name="Ramp", expression=2 * t + 1, variables={"t": [0.0, 1.0, 2.0]}, bound_variables={"t"})
for res in func.evaluate():
val_str, _ = format_quantity(res.output)
print(f"t={res.inputs['t']} -> {val_str}")
4. Unit-aware quantities¶
When the optional quantities extra is installed, variables can be DccQuantityType
objects carrying a value, an uncertainty and a unit. Evaluation then propagates units and uncertainties
automatically. The following snippet only runs the unit-aware path when dccQuantities
is available, so it is safe to execute in any environment:
from sympy import symbols
from dcclib.extraction.formulae import format_quantity
from dcclib.extraction.formulae.formula import DCCFunction
from dcclib.utils.quantities_check import HAS_DCC_QUANTITIES
if HAS_DCC_QUANTITIES:
from dcc_quantities.dcc_quantity_type import DccQuantityType
x, y = symbols("x y")
qx = DccQuantityType.from_single_quantity_value(value=2.0, uncertainty=0.1, name="x", unit="\\metre")
qy = DccQuantityType.from_single_quantity_value(value=3.0, uncertainty=0.2, name="y", unit="\\metre")
func = DCCFunction(name="Area", expression=x * y, variables={"x": qx, "y": qy}, bound_variables={"x", "y"})
for res in func.evaluate():
val_str, unit_str = format_quantity(res.output)
# e.g. "6.0 ± 0.36 \metre\tothe{2}"
print(f"Result: {val_str} {unit_str}")
else:
print("dccQuantities is not installed; skipping unit-aware evaluation.")
See also¶
- The
dcclib extract formulaecommand for extracting and evaluating formulas from the command line. - The API reference for the full
dcclib.extraction.formulaeinterface.