SyntaxHighlighter

4 March 2013

Formulating problems (3)

Object-oriented programming and systematic errors

In the previous post (March 3, 2013), I showed how to use GTC when both random and systematic errors contribute to the overall measurement uncertainty.

The key is that an estimate of each measurement error in the model must be associated with one, and only one, uncertain number.

In the last post, we saw that the idea of matching one uncertain number to one error estimate offers a straightforward way of using GTC. However, it becomes hard to keep track of all the different uncertain numbers as problems become more complicated.

In this post, I will show how to define a class of objects to represent the voltmeter used earlier. This class of objects will encapsulate the data and data processing needed by the voltmeter error model, making data processing much more straightforward.

A simple voltmeter

An equation for the meter reading \(x_\mathrm{dis} \) in terms of the applied voltage \( V \) and instrument errors is
\[
x_\mathrm{dis}  = (1 + e_\mathrm{gain} + e_\mathrm{ran}) V + e_\mathrm{zero}
\]
where, \( e_\mathrm{gain} \) is the residual error in the gain setting, \( e_\mathrm{zero} \) is the residual error in the zero offset and \( e_\mathrm{ran}\) is an error due to system noise.

To carry out data processing, we invert this equation and replace the errors by their estimates (indicated by the hat symbol). In this way we can estimate the applied voltage given a reading
\[
\widehat{V} = \frac{x_\mathrm{dis} - \widehat{e}_\mathrm{zero}}{1 + \widehat{e}_\mathrm{gain} + \widehat{e}_\mathrm{ran}}
\]
Remember, \( e_\mathrm{gain} \) and \( e_\mathrm{zero} \) are systematic errors: they endure from one measurement to the next. Whereas \( e_\mathrm{ran} \) changes at every meter reading. So we usually estimate \( e_\mathrm{gain} \) and \( e_\mathrm{zero} \) just once, but need a fresh estimate of \( e_\mathrm{ran} \) for every reading.

I'll now define a Python class to implement the required uncertain-number data processing.

A voltmeter class

In object-oriented languages like Python, the structure and behaviour of new types of objects can be defined by a 'class'. An object of a given class can have data (that other objects of the same class do not share) and functions that implement certain behaviour (shared among all objects of the same class).

Here is a class definition for the voltmeter

class Meter(object):
    def __init__(self,u_e_gain,u_e_zero,u_e_ran=0,tag=None): 
        """Initialise a new voltmeter object"""

        self.u_e_ran = u_e_ran
        
        tag = "_%s" % tag if tag is not None else ""
        label = 'e_gain%s' % tag
        self.e_gain = ureal(0,u_e_gain,label=label)
        
        label = 'e_zero%s' % tag
        self.e_zero = ureal(0,u_e_zero,label=label)

        self.u_e_ran = u_e_ran

    def voltage(self,x,tag=None):
        """Return an uncertain number for the voltage"""

        tag = "_%s" % tag if tag is not None else ""
        if self.u_e_ran != 0:
            label = 'e_ran%s' % tag
            e_ran = ureal(0,self.u_e_ran,label=label)
        else:
            e_ran = 0

        return (x - self.e_zero)/(1 + self.e_gain + e_ran)
When objects of the Meter class are created, their data is initialised by the __init__ function. We see here that uncertain numbers for the residual gain and zero setting errors are created during initialisation. The standard uncertainty associated with random noise is also saved.

The voltage function uses the object-data to return an uncertain number for an applied voltage estimate. When voltage is called, an uncertain number associated with the random error is created first. This is combined with the uncertain numbers already defined to estimate the applied voltage.

Resistance measurement with one meter

As in the previous post, I will calculate an estimate of the unknown resistance \( R_\mathrm{x} \) from measurements of the voltage ratio \( V_\mathrm{x} / V_\mathrm{s} \) and an estimate of \( R_\mathrm{s} \)



To do this, I create an object of the Meter class by supplying numbers for the standard uncertainty parameters: u_e_gain, u_e_zero and u_e_ran:
m = Meter(u_e_gain=3E-6,u_e_zero=1E-6,u_e_ran=1E-7)
Then use the meter object to obtain a pair of uncertain numbers for the voltage readings
Vs = m.voltage(5.0100,tag='Vs')
Vx = m.voltage(4.9885,tag='Vx')

The code to calculate the unknown resistance and display the results (apart from the Meter class definition) now looks like this
m = Meter(u_e_gain=3E-6,u_e_zero=1E-6,u_e_ran=1E-7)

Rs = 1000 + ureal(0,1E-3,label='Rs')

Vs = m.voltage(5.0100,tag='Vs')
Vx = m.voltage(4.9885,tag='Vx')

Rx = Rs * Vx/Vs

print "Rx = %G, u(Rx) = %G" % (value(Rx), uncertainty(Rx))
for cpt in reporting.budget(Rx,trim=0):
    print "%s: %G" % cpt
we obtain (as before)
Rx = 995.709, u(Rx) = 0.00100562
Rs: 0.000995709
e_ran_Vs: 9.95709E-05
e_ran_Vx: 9.95709E-05
e_zero: 8.5657E-07
e_gain: 0
This is easier to read, write and understand than the code I posted earlier.

Resistance measurement with two meters

As might be expected, if two physically different meters are used to measure the different voltages, then two different Meter objects should be created to do the data processing.

So with just a few simple changes to the code we have
m1 = Meter(u_e_gain=3E-6,u_e_zero=1E-6,u_e_ran=1E-7,tag='m1')
m2 = Meter(u_e_gain=3E-6,u_e_zero=1E-6,u_e_ran=1E-7,tag='m2')

Vs = m1.voltage(5.0100,tag='Vs')
Vx = m2.voltage(4.9885,tag='Vx')

Rs = 1000 + ureal(0,1E-3,label='Rs')

Rx = Rs * Vx/Vs
print "Rx = %G, u(Rx) = %G" % (value(Rx), uncertainty(Rx))
for cpt in reporting.budget(Rx,trim=0):
    print "%s: %G" % cpt
which gives the same results as we obtained last time
Rx = 995.709, u(Rx) = 0.0043516
e_gain_m1: 0.00298713
e_gain_m2: 0.00298713
Rs: 0.000995709
e_zero_m2: 0.000199601
e_zero_m1: 0.000198744
e_ran_Vs: 9.95709E-05
e_ran_Vx: 9.95709E-05

This simple and intuitive behaviour is due to the way that uncertain numbers are created during object initialisation. When one Meter object is initialsed, a pair of uncertain numbers are created; but when two objects are initialsed, each has its own pair of uncertain numbers representing independent estimates of the different gain and zero setting errors in each meter.

Hence the definition of a Meter class is able to capture the creation of uncertain numbers we need in a natural and intuitive way that matches the model of the measurement system.

No comments:

Post a Comment