Skip to content

Check uncercalculation for uniform/rect uncer serDeser behaves strange

Input

<?xml version='1.0' encoding='utf-8'?>
<dcc:quantity xmlns:dcc="https://ptb.de/dcc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:si="https://ptb.de/si" refType="vibration_sensorTemperature">
  <dcc:name>
    <dcc:content lang="en">Sensor temperature</dcc:content>
    <dcc:content lang="de">Temperatur des Aufnehmers</dcc:content>
  </dcc:name>
  <si:hybrid>
    <si:real>
      <si:value>296.15</si:value>
      <si:unit>\kelvin</si:unit>
      <si:measurementUncertaintyUnivariate>
        <si:expandedMU>
          <si:valueExpandedMU>1</si:valueExpandedMU>
          <si:coverageFactor>1.73</si:coverageFactor>
          <si:coverageProbability>0.95</si:coverageProbability>
          <si:distribution>rectangular</si:distribution>
        </si:expandedMU>
      </si:measurementUncertaintyUnivariate>
    </si:real>
    <si:real>
      <si:value>23.0</si:value>
      <si:unit>\degreecelsius</si:unit>
      <si:measurementUncertaintyUnivariate>
        <si:expandedMU>
          <si:valueExpandedMU>1</si:valueExpandedMU>
          <si:coverageFactor>1.73</si:coverageFactor>
          <si:coverageProbability>0.95</si:coverageProbability>
          <si:distribution>rectangular</si:distribution>
        </si:expandedMU>
      </si:measurementUncertaintyUnivariate>
    </si:real>
  </si:hybrid>
</dcc:quantity>

Output

<?xml version='1.0' encoding='utf-8'?>
<dcc:quantity xmlns:dcc="https://ptb.de/dcc" xmlns:si="https://ptb.de/si" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="U6a8247a1-9808-4157-8613-2275e382eef0" refType="vibration_sensorTemperature">
  <dcc:name>
    <dcc:content lang="en">Sensor temperature</dcc:content>
    <dcc:content lang="de">Temperatur des Aufnehmers</dcc:content>
  </dcc:name>
  <si:hybrid>
    <si:real>
      <si:value>296.15</si:value>
      <si:unit>\kelvin</si:unit>
      <si:measurementUncertaintyUnivariate>
        <si:expandedMU>
          <si:valueExpandedMU>1.0</si:valueExpandedMU>
          <si:coverageFactor>1.73</si:coverageFactor>
          <si:coverageProbability>0.9994079828490263</si:coverageProbability>
          <si:distribution>uniform</si:distribution>
        </si:expandedMU>
      </si:measurementUncertaintyUnivariate>
    </si:real>
    <si:real>
      <si:value>23.0</si:value>
      <si:unit>\degreecelsius</si:unit>
      <si:measurementUncertaintyUnivariate>
        <si:expandedMU>
          <si:valueExpandedMU>1.0</si:valueExpandedMU>
          <si:coverageFactor>1.73</si:coverageFactor>
          <si:coverageProbability>0.9994079828490263</si:coverageProbability>
          <si:distribution>uniform</si:distribution>
        </si:expandedMU>
      </si:measurementUncertaintyUnivariate>
    </si:real>
  </si:hybrid>
</dcc:quantity>

check https://gitlab1.ptb.de/digitaldynamicmeasurement/dccQuantities/-/blame/devel-serializer/src/parseUncertainties.py?ref_type=heads#L262

def handle_coverage_params(coverageProbability, k_factor, distribution):
    """
    Calculate and check coverageProbability and k_factor for normal and uniform distributions.

    For normal distributions:
      - If k_factor is provided, compute coverageProbability = erf(k_factor/√2).
      - If only coverageProbability is provided, compute k_factor = √2 * erfinv(coverageProbability).
      - If both are provided, recalc coverageProbability from k_factor (priority to k_factor)
        and ensure the mismatch is within dccConfiguration.allowedCoveragePropabilityMissmatch.
      - If a missmatch occures either an warning is generated and given k is used (DccConfiguration.CoveragePropabilityMissmatchBehavior.WARNING_TAKE_K_VALUE)
        or and Exception is raised DccConfiguration.CoveragePropabilityMissmatchBehavior.EXCEPTION

    For uniform distributions:
      - If k_factor is provided:
            computed_cov = (k_factor/√3 + 1)/2 if k_factor <= √3, else computed_cov = 1.0.
      - If only coverageProbability is provided, compute k_factor = √3*(2*coverageProbability - 1).
      - If both are provided, compute computed_cov and check against the provided coverageProbability.

    Parameters:
        coverageProbability (float or None): The provided coverage probability.
        k_factor (float or None): The provided coverage factor.
        distribution (str): "normal" or "uniform".

    Returns:
        tuple: (coverageProbability, k_factor) after calculation/validation.

    Raises:
        ValueError: If neither parameter is provided or if the provided values are inconsistent.
    """
    try:
        allowed = dccConfiguration.allowedCoveragePropabilityMissmatch[distribution]
    except KeyError:
        allowed = dccConfiguration.allowedCoveragePropabilityMissmatch['default']
    if distribution == "normal":
        if k_factor is not None:
            computed_cov = math.erf(k_factor / math.sqrt(2))
            if coverageProbability is None:
                coverageProbability = computed_cov
            else:
                if abs(coverageProbability - computed_cov) > allowed:
                    if dccConfiguration.coveragePropabilityMissmatchBehaivior == DccConfiguration.CoveragePropabilityMissmatchBehavior.EXCEPTION:
                        raise ValueError(
                            f"Normal distribution mismatch: provided coverageProbability {coverageProbability} vs computed {computed_cov}"
                        )
                    elif dccConfiguration.coveragePropabilityMissmatchBehaivior == DccConfiguration.CoveragePropabilityMissmatchBehavior.WARNING_TAKE_K_VALUE:
                        warnings.warn( f"Normal distribution mismatch: provided coverageProbability {coverageProbability} vs computed {computed_cov} using k value for further calculations. Since dccConfiguration.CoveragePropabilityMissmatchBehavior.WARNING_TAKE_K_VALUE is set" )
                        return computed_cov, k_factor
        elif coverageProbability is not None:
            # Requires Python 3.8+ for math.erfinv
            k_factor = math.sqrt(2) * math.erfinv(coverageProbability)
        else:
            raise ValueError(
                "For normal distribution, at least one of coverageProbability or k_factor must be provided.")
        return coverageProbability, k_factor

    elif distribution == "uniform":

#        For a rectangular (i.e. uniform) distribution, one commonly used result is that if a random variable is uniformly distributed between –a and a, then its standard uncertainty is given by
#        \[
#        u = \frac{a}{\sqrt{3}},
#        \]
#        because the variance of a uniform distribution on [–a, a] is \( \sigma^2 = a^2/3 \). Consequently, if you define an "expanded uncertainty" U (which might be provided by the user) and wish to express it in terms of standard uncertainty via a coverage factor \(k\), then
#        \[
#        u = \frac{U}{k}.
#       \]
#        Combining these gives a way to relate U, \(k\), and the half-width \(a\) of the distribution:
#        \[
#        a = \frac{U \sqrt{3}}{k}.
#        \]
#        Furthermore, the probability that a value falls within the interval \([-k\cdot u, k\cdot u]\) is obtained by integrating the uniform density. For \([-c, c]\) (with \(c \le a\)), the cumulative probability is
#        \[
#       P = \frac{2c}{2a} = \frac{c}{a}.
#        \]
#        Substituting \(c = k\cdot u\) and \(u = a/\sqrt{3}\) yields
#        \[
#        P = \frac{k\,(a/\sqrt{3})}{a} = \frac{k}{\sqrt{3}} \quad \text{(for } k \le \sqrt{3}\text{)},
#        \]
#        with \(P = 1\) if \(k > \sqrt{3}\).
#        These relationships are widely cited in metrology and uncertainty analysis. For example:
#        - The *Guide to the Expression of Uncertainty in Measurement* (GUM, JCGM 100:2008) explains that for a uniform (rectangular) distribution the standard uncertainty is \(u = a/\sqrt{3}\).
#        - NIST’s *Technical Note 1297* provides guidelines for evaluating uncertainties where similar derivations for the uniform distribution are given.

        if k_factor is not None:
            # Calculate computed coverageProbability from k_factor.
            if k_factor <= math.sqrt(3):
                computed_cov = (k_factor / math.sqrt(3) + 1) / 2
            else:
                computed_cov = 1.0  # Entire range covered.
            if coverageProbability is None:
                coverageProbability = computed_cov
            else:
                if abs(coverageProbability - computed_cov) > allowed:
                    if dccConfiguration.coveragePropabilityMissmatchBehaivior == DccConfiguration.CoveragePropabilityMissmatchBehavior.EXCEPTION:
                        raise ValueError(
                            f"Normal distribution mismatch: provided coverageProbability {coverageProbability} vs computed {computed_cov}"
                        )
                    elif dccConfiguration.coveragePropabilityMissmatchBehaivior == DccConfiguration.CoveragePropabilityMissmatchBehavior.WARNING_TAKE_K_VALUE:
                        warnings.warn( f"Normal distribution mismatch: provided coverageProbability {coverageProbability} vs computed {computed_cov} using k value for further calculations. Since dccConfiguration.CoveragePropabilityMissmatchBehavior.WARNING_TAKE_K_VALUE is set" )
                        return computed_cov, k_factor
        elif coverageProbability is not None:
            # Invert the relationship for uniform distributions.
            k_factor = math.sqrt(3) * (2 * coverageProbability - 1)
        else:
            raise ValueError(
                "For uniform distribution, at least one of coverageProbability or k_factor must be provided.")
        return coverageProbability, k_factor

    else:
        raise ValueError("Unsupported distribution type. >>" +str(distribution)+"<<")