Giter VIP home page Giter VIP logo

nrc-cnrc / metrolopy Goto Github PK

View Code? Open in Web Editor NEW
33.0 8.0 6.0 10.03 MB

Tools for uncertainty propagation and measurement unit conversion — Outils pour la propagation des incertitudes et la conversion d'unités de mesure

Home Page: https://nrc-cnrc.github.io/MetroloPy

License: GNU General Public License v3.0

Python 100.00%
uncertainty-propagation uncertainty-analysis uncertainty-visualisation uncertainties unit-conversion unit-converter monte-carlo-simulation error-propagation curve-fitting uncertainty

metrolopy's Introduction

MetroloPy

tools for dealing with physical quantities: uncertainty propagation and unit conversion


MetroloPy is a pure python package and requires Python 3.5 or later and the SciPy stack (NumPy, SciPy and Pandas). It looks best in a Jupyter Notebook.

Install MetroloPy with pip install metrolopy or conda install -c conda-forge metrolopy.

Physical quantities can then be represented in Python as gummy objects with an uncertainty and (or) a unit:

>>> import metrolopy as uc
>>> a = uc.gummy(1.2345,u=0.0234,unit='cm')
>>> a
1.234(23) cm

>>> b = uc.gummy(3.034,u=0.174,unit='mm')
>>> f = uc.gummy(uc.UniformDist(center=0.9345,half_width=0.096),unit='N')
>>> p = f/(a*b)
>>> p
2.50(21) N/cm2

>>> p.unit = 'kPa'
>>> p.uunit = '%'
>>> p
25.0 kPa ± 8.5%

MetroloPy can do much more including Monte-Carlo uncertainty propagation, generating uncertainty budget tables, and curve fitting. It can also handle expanded uncertainties, degrees of freedom, correlated quantities, and complex valued quantities. See:

new in version 0.6.0

  • A constant library has been added with physical constants that can be accessed by name or alias with the constant function. The search_constants function with no argument gives a listing of all built-in constants. Each constant definition includes any correlations with other constants.

  • The Quantity class has been added to represent a general numerical value multiplied by a unit and the unit function has been added to retrieve Unit instances from the unit library by name or alias. Unit instances can now be multiplied and divided by other Unit instances to produce composite units, can be multiplied and divided by numbers to produce Quantity instances or multiply or divide Quantity instances. The gummy class is now a subclass of Quantity with a nummy value rather than a subclass of nummy. A QuantityArray class has been introduced to represent an array of values all with the same unit. Multiplying a Unit instance by a list, tuple, or numpy array produces a QuantityArray instance.

  • The immy class has been introduced as an ummy valued counterpart of the jummy class for representing complex values with uncertainties. immy and jummy values can now be displayed in a polar representation in addition to a cartesian representation. immy and jummy .r and .phi properties have been added to access the magnitude and argument of the values as a complement to the .real and .imag properties.

metrolopy's People

Contributors

ftessier avatar hvparks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metrolopy's Issues

budget() raises BudgetWarning for an uncertainty of 0

Hi,
While trying to validate metrolopy (v0.6.1) with example S9 from EA-4/02 M: 2013, I noticed that an BudgetWarning is raised for uncertainties with a value 0, when trying to create a budget. I don't want to post the whole code so here is a minimal example showcasing this:

import metrolopy as uc

x_1 = uc.gummy(42, 0)
x_2 = uc.gummy(42, 1)
y = x_1 - x_2
print(y.budget([x_1, x_2]))

the traceback is:

gummy.py:2070: BudgetWarning: the x variables are over determined; they cannot all be taken as independant variables
  return Budget(self,xlist,**kwds)
budget.py:443: RuntimeWarning: invalid value encountered in double_scalars
  d = [p*yu/q.u for p,q in zip(s,x)]
Component Value    u |dy/dx|     s
     x[2]  42.0  1.0     1.0  1.00
     x[1]    42    0     nan   nan
        y   0.0  1.0

This is by itself not a problem, since the calculation still seems to be correct and the warnings are easily captured. I just wonder if this is an intended behavior?

Bugs in unitparser.py

There are at least two bugs in unitparser.py:

  • Giving a unit as e.g. "V**(0.5)" will attempt to typecast "(0.5)" as float, when it should be typecasting "0.5" (line 212, 201, etc.). It seems to currently impossible to set fractional exponents.
  • isspace() is misspelled on several occasions (as ispace()).

Budget Values with Names

In the budget values are displayed with names if uunit of output variable is set to % or ppm.

image

Adding show_name = False to budget.py solved this issue for me:
image

Result:
image

thousand_spaces

As far as I know standard conform spacing is:
4.899 7 kΩ ± 1.1 Ω
4.899 68 kΩ ± 0.1 Ω
4.899 681 kΩ ± 0.01 Ω

Currently it is in the first case like this:
4.8997 kΩ ± 1.1 Ω

so the first should be: "nr < 4 and nl < 4: " and "if nr >= 4: " and " if nl >= 4:".

Modul is ummy.py

elif self.thousand_spaces:

if nr < 4 and nl < 4:
    return s + ellipses

s = list(s)
if nr >= 4:
    for i in range(int(nr/3)):
        s.insert(d + i + (i+1)*3 + 1, sp)
if nl >= 4:
    for i in range(int(nl/3)):
        s.insert(d - (i+1)*3, sp)

I love your great work with MetrolPy!
I'm sorry, for my bad contribution quality - I'm a rooky in this issue. But I'll soon learn how to to proper issues, push, pulls and what ever exist to contribute good way.

Uniform Distribution Uncertainty Error

The standard uncertainty of the uniform distribution is not correct if it is calculated by upper and lower limit. I think you missed a factor 2 in the denominator.

image

multiplying a gummy with a complex number should create a jummy

Any operation (e.g. multiplication) of a gummy with a complex number raises an exception. It should rather create a jummy.

This probably require only some additional conditional checks in the ummy code to work and would lead to a more expectable behaviour.

budget() omits names of uncertainties

Hey,

since 0.6.2 the names of uncertainties are omitted by the budget method.

Here's a minimal example:

import metrolopy as uc
x_1 = uc.gummy(42, 0, name='A')
x_2 = uc.gummy(42, 1, name='B')
y = x_1 - x_2
print(y.budget([x_1, x_2]))

output for 0.6.1

Component Value    u |dy/dx|     s
        B  42.0  1.0     1.0  1.00
        A    42    0     nan   nan
        y   0.0  1.0

output for 0.6.2

Component Value    u |dy/dx|     s
     x[2]  42.0  1.0     1.0  1.00
     x[1]    42    0     0.0     0
        y   0.0  1.0

Please notice the 'Component' column

UniformDist Error

Although the following expressions should be equivalent results differ. Only the latter is correct.

tmp = uc.gummy(uc.UniformDist(lower_limit=6,upper_limit=9),unit='pA')
uc.gummy.simulate([tmp],n=1e7)
tmp.hist(p=0.99)

image
image

tmp = uc.gummy(uc.UniformDist(center=7.5,half_width=1.5),unit='pA') 
uc.gummy.simulate([tmp],n=1e7)
tmp.hist(p=0.99)

image
image

documentation: definition of significance seems to be wrong

The definition of "significance" seems to be wrong. According to the docs:

“s” or “significance”: the sensitivity coefficient (below) multiplied by the standard uncertainty, displayed by default

The significance is in fact calculated as x.u / result.u (or v[0]/yu in budget.py)

If I am not mistaken, the correct text should therefore be:

“s” or “significance”: the sensitivity coefficient (below) multiplied by the standard uncertainty divided by the combined standard uncertainty, displayed by default

0°C - Bug

If the value 0°C is entered it is automatically converted to - 273.15(15) °C. A Value slightly above ist giving the correct result.
Enclosed my code:

import metrolopy as uc

UUT = uc.gummy(0, 0.15, uunit="°C", unit="°C")
Out[4]: -273.15(15) °C

UUT = uc.gummy(0, 0.15, uunit="degC", unit="degC")
Out[8]: -273.15(15) °C

UUT = uc.gummy(0.1, 0.15, uunit="degC", unit="degC")
Out[10]: 0.10(15) °C

Lack of derivative causes angle() to fail on jummy

Hello,

I'm new to MetroloPy, trying to handle complex numbers with uncertainty.
I've noticed that the uc.angle() function fails on jummy objects with

~\Miniconda3\envs\default\lib\site-packages\metrolopy\dfunc.py in tofloat(self)
    193         # this should return a copy of self with the x and u properties
    194         # converted to float values
--> 195         raise NotImplementedError()
    196
    197     @classmethod

NotImplementedError:

As far as I can tell this is from MetroloPy/dfunc.py

try_fconvert = True
def _call(f,*x):
    if try_fconvert:
        try:
            return f(*x)
        except:
--->        x = [a.tofloat() if isinstance(a,Dfunc) else float(a) for a in x]
            return f(*x)

    return f(*x)

since the initial return f(*x) throws a KeyError stemming from MetroloPy/dfunc.py:

    def __array_ufunc__(self,ufunc,method,*args,**kwds):
        if method != '__call__':
            return None
        
        try:
--->        return _call(lambda *x: self._apply(ufunc,ddict[ufunc],*x), *args)
        except KeyError:
            try:
                return _call(fdict[ufunc],*args)
            except KeyError:
                return None

since ddict[np.angle] is not defined.

gummy.create() with covariance matrix mutates input matrix

Hi,

I noticed that gummy.create() mutates its input covariance matrix.
This leads to unexpected behavior:

from metrolopy import gummy
import numpy as np

val = [1., 2.]
cov = np.array([[0.3, 0.1], [0.1, 0.2]])

print("cov before gummy creation:\n", cov)
print()

a, b = gummy.create(val, covariance_matrix=cov)

print("cov after gummy creation:\n", cov)
print()
print("a + b after first gummy creation ", a + b)

a, b = gummy.create(val, covariance_matrix=cov)

print("a + b after second gummy creation ", a + b)
cov before gummy creation:
 [[0.3 0.1]
 [0.1 0.2]]

cov after gummy creation:
 [[1.         0.40824829]
 [0.40824829 1.        ]]

a + b after first gummy creation  3.00(84)
a + b after second gummy creation  3.0(17)

I think this line is the problem:

matrix[i][j] /= gummys[i]._u*gummys[j]._u

Is the mutation necessary?
A workaround is to pass a deepcopy of the covariance matrix.

Missing Feature: Uncertainty by Observations

I am missing a feature for automatic calculation of uncertainty by a given array of observations similliar to GUM Type A Bayes. Please find an example for student distribution assumptions below.

from scipy.stats import t
from scipy import special
import numpy as np

def u(x):
    confidence = special.erf(1/np.sqrt(2)) # single standard deviation 68 percent
    return t.ppf(1-(1-confidence)/2, len(x)-1)*x.std(ddof=1)/np.sqrt(len(x))

def mean(x):
    return np.mean(x)

Metrolopy2word

Here is a suggestion for a Microsoft Word export of budget and frequency distribution. Might be interesting for people who don't know Latex.

Result
image

SourceCode

from docx import Document
import matplotlib
from docx.enum.section import WD_ORIENT, WD_SECTION
from docx.shared import Mm
def budget2word(budget,file,output=None):    
    df = budget.df_str
    doc = Document()
    section = doc.sections[-1]
    section.page_height = Mm(297)
    section.page_width = Mm(210)
    section.left_margin = Mm(25.4)
    section.right_margin = Mm(25.4)
    section.top_margin = Mm(25.4)
    section.bottom_margin = Mm(25.4)
    section.header_distance = Mm(12.7)
    section.footer_distance = Mm(12.7)
    t = doc.add_table(df.shape[0]+1, df.shape[1])
    t.style = doc.styles['Colorful List Accent 2']
    for j in range(df.shape[-1]):
        t.cell(0,j).text = df.columns[j]
    for i in range(df.shape[0]):
        for j in range(df.shape[-1]):
            t.cell(i+1,j).text = df.values[i,j] 
    if output is not None:
        matplotlib.pyplot.clf()
        uc.gummy.simulate([output],n=5e5)
        output.hist(p=0.95, density=False)
        matplotlib.pyplot.gca().set_ylabel("frequency distribution")
        output.k = 1
        matplotlib.pyplot.savefig(file.split(".")[0]+".png")
        doc.add_picture(file.split(".")[0]+".png")
    doc.save(file)

Execution

fname = 'budgets/budget.docx'
budget2word(budget,fname,output=outputVariable)

Uncertainty not calculated correctly when working with integers as base in exponential functions

First of all, thank you for uploading this package, I was look for something like this for a while!

When testing the package (v0.5.4) on a convoluted model function which had a 10**(...) term I found that the uncertainty does not seem to be calculated correctly (double checking with a calculation by hand and the uncertainties package yielded significantly different results). By accident, I changed the integer 10 to a float 10.0**(...) which solved the problem!

Here is a minimal example:

>>> a = uc.gummy(1, u=0.1)
>>> 10**a

10.0 +/- 2.0

vs.

>>> a = uc.gummy(1, u=0.1)
>>> 10.0**a

10.0 +/- 2.3

Calculation of combined uncertainties

Hi,

the docs don't explicitly mention this, but from looking into the source code I take it that nonlinear dependencies are implemented by linear approximation, is that correct?

Missing Feature

A function normalizing the gummy to the corresponding SI unit would be nice, i.e., gummy.x in SI and gummy.U in SI

matplot lib normed depreciation warning

Newer versions of matplotlib raise a depreciation warning for the hist normed keyword argument ("use the density keyword argument instead") when gummy.hist() is executed.

Function _unit.mulr() does not exist

Hello,
I think I have found a typo...

un = self._unit.mulr(one)[0]

I have tested the two examples given and I get AttributeError '_CompositeUnit' object has no attribute 'mulr'
This could have been caught with a unit tests ;)
Loving this module, keep up the great work!

gummy.ufrom() only works for single characters

According to the docs the utype property of a gummy should accept an arbitrary string, but this only works for single characters, e.G. A/B (which are obvious choices for a utype).
I tried grouping uncertainties according to the source (DUT, REF, ...) and noticed that the gummy.ufrom() method returned a value of 0. Looking at the code, it seems that only single characters are supported. Here is a minimal example:

import metrolopy as uc

x_1 = uc.gummy(10.0, 1, utype='A')
x_2 = uc.gummy(20.0, 2, utype='DUT')
y = x_1 - x_2
print(y.ufrom('A'))
print(y.ufrom('DUT'))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.