Code Monkey home page Code Monkey logo

engineeringunits's People

Contributors

atefebahrani avatar hediasjarno avatar ikijano avatar jccockre avatar jcockrelanalog avatar madsfoged avatar madskirkfoged avatar mschmitz2 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  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

engineeringunits's Issues

Specific Heat

Hi,

I didn't find the specific heat Cp [J.kg.K], did i miss something ? If no, is there a way to extend or derive this new unit as power/(surface*temperature) ?

Best regards.

Optimize for performance

I would like to start a discussion about how we can optimize for better performance.
Feel free to chip in!

  1. Analyse where the CPU is spending much of the time?
    --> Can we write better code that fixes the heavy CPU using functions?

  2. Could we implement new strategies?
    --> fx. If two units are Set as SI we could ship Unitchecks and other parts of the system

Thread safety and Possible race conditions

Hello, thanks for providing such a wonderful piece of software.

Ive been writing code based on your modules and for some reason when i use xunit, there happens to be unrepeatable wrong unit exceptions which come. It means like once every twenty or thirty test runs, a wrong unit exception comes up.

When i rerun the tests, it disaappears and the tests pass.

It's about 200+ tests, and i strongly suspect it's because xunit does things in parallel, and also because some classes use static operators eg, addition and subtraction.

I strongly suspect a race condition as several classes or test cases are accessing static operators at the same time. And therefore it's not really thread safe.

Were there any of such cases before?

And what are some possible fixes we can work towards?

Edit: I'll also be checking in case my code (or what i'm building off - spicesharp) isn't thread safe. But tbh i have never used static methods in my code and all the exceptions and errors i've gotten were from the EngineeringUnits package tho.

Add support for NaN

Currently, constructing with a double.NaN value (e.g. Pressure.FromSI(double.NaN)) results in a value of Infinity.

Looking through the source code I found that this is done in BaseUnit:

if (IsValueOverDecimalMax(value))
{
    Inf = true;
    return;
}

where

private static bool IsValueOverDecimalMax(double value)
{
    return double.IsInfinity(value) || value > 7.9228162514264338E+28 || value < -7.9228162514264338E+28 || double.IsNaN(value);
}

I imagine this was implemented due to internally saving the value as decimal, where decimal does not support NaN.

But from what I can see it could be possible to handle this in exactly the same way as infinity, but side by side. I.e.

if (double.IsNaN(value))
{
    IsNaN = true;
    return;
}
if (double.IsInfinity(value) || value > 7.9228162514264338E+28 || value < -7.9228162514264338E+28)
{
    Inf = true;
    return;
}

and then also checking for IsNaN when getting a value as double, everywhere you currently already check for Inf.
Hopefully this would not have too much of an impact on performance.

Ideally, to avoid having to use XXX.FromSI(double.NaN), there could also be a static property .NaN similar to .Zero.

The application where this would be of help to me is for plotting, where NaN is an empty point and values of Infinity causes issues and results in exceptions. I can of course get around it in my application by transforming every Infinity value back to NaN before passing to the plotting tool, so this is not super crucial, but might be a nice addition if easy.

Add ideal gas constant

Might be useful to some.

E.g. defined as

    /// <summary>
    /// Ideal gas constant
    /// </summary>
    public static BaseUnit IdealGasConstant
    {
        get
        {
            UnitSystem unit = MolarEntropyUnit.JoulePerMoleKelvin.Unit;
            return new BaseUnit(8.31446261815324, unit);
        }
    }

Add unary minus operator?

Currently

var p = Pressure.FromBar(10);
// var minusP = -p; // Does not compile
var minusP = p * -1; // Workaround

I noticed you are using this workaround yourself, e.g. in Extensions.Abs, so I'm pretty sure I didn't miss something this time.

Would probably need to define something along the lines of (following your way of defining the regular operator-)

public class UnknownUnit
{
    // ...

    public static UnknownUnit operator -(UnknownUnit right)
    {
        return -right?.BaseUnit;
    }
}

and

public class BaseUnit
{
    // ...

    public static UnknownUnit operator -(BaseUnit right)
    {
        if ((object)right == null)
        {
            return null;
        }

        try
        {
            if (right.Unit.IsSIUnit())
            {
                return new UnknownUnit(-right.NEWValue, right.Unit);
            }

            return new UnknownUnit(-right.ConvertValueInto(right), right.Unit);
        }
        catch (OverflowException)
        {
            return new UnknownUnit(double.PositiveInfinity, -right.Unit);
        }
    }
}

What do you think?

Help convertint class from UnitsNet

I have a vector and matrix class that I had written for UnitsNet that I am trying to convert over to EngineeringUnits and having some trouble.

I have included the whole class, but there are two basic areas I am having trouble understanding how to implement. Since this is a generic class, I am having trouble initializing the values of the array to a value. I am also having trouble assigning the values of the EngineeringUnits overloaded operators back to . I get

Cannot implicitly convert type 'EngineeringUnits.UnknownUnit' to 'T'. An explicit conversion exists (are you missing a cast?)

using System;
using System.Globalization;
using UnitsNet;

namespace Tidal.Support.DataStructures
{
    public class UVector<T> : ICloneable where T : IQuantity, new()
    {
        private T[] _vector;

        public UVector(int nDim)
        {
            GetVectorSize = nDim;
            _vector = new T[nDim];
            for (var i = 0; i < nDim; i++) _vector[i] = Zero();
        }

        public UVector(T[] vector)
        {
            GetVectorSize = vector.Length;
            _vector = vector;
        }

        public T this[int i]
        {
            get
            {
                if (i < 0 || i > GetVectorSize) throw new ArgumentException("Requested vector index is out of range.");

                return _vector[i];
            }
            set => _vector[i] = value;
        }

        public int GetVectorSize { get; }

        object ICloneable.Clone()
        {
            return Clone();
        }

        private T Zero()
        {
            QuantityValue v = 0.0;
            var value = Quantity.FromQuantityInfo(_vector[0].QuantityInfo, v);
            return (T) value;
        }

        public UVector<T> Clone()
        {
            var v = new UVector<T>(_vector);
            v._vector = (T[]) _vector.Clone();
            return v;
        }

        public UVector<T> SwapVectorEntries(int m, int n)
        {
            var temp = _vector[m];
            _vector[m] = _vector[n];
            _vector[n] = temp;
            return new UVector<T>(_vector);
        }

        public override string ToString()
        {
            var str = "(";
            for (var i = 0; i < GetVectorSize - 1; i++) str += _vector[i].ToString(CultureInfo.InvariantCulture) + ", ";

            str += _vector[GetVectorSize - 1].ToString(CultureInfo.InvariantCulture) + ")";
            return str;
        }

        public override bool Equals(object obj)
        {
            return obj is UVector<T> && Equals((UVector<T>) obj);
        }

        public bool Equals(UVector<T> v)
        {
            return _vector == v._vector;
        }

        public override int GetHashCode()
        {
            return _vector.GetHashCode();
        }

        public static bool operator ==(UVector<T> v1, UVector<T> v2)
        {
            return v1.Equals(v2);
        }

        public static bool operator !=(UVector<T> v1, UVector<T> v2)
        {
            return !v1.Equals(v2);
        }

        public static UVector<T> operator +(UVector<T> v)
        {
            return v;
        }

        public static UVector<T> operator +(UVector<T> v1, UVector<T> v2)
        {
            var result = new UVector<T>(v1.GetVectorSize);

            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++)
            {
                val = v1[i].As(a) + v2[i].As(a);
                result[i] = (T) Quantity.From(val, a);
            }

            return result;
        }

        public static UVector<T> operator -(UVector<T> v1)
        {
            var result = new UVector<T>(v1.GetVectorSize);

            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++)
            {
                val = -v1[i].As(a);
                result[i] = (T) Quantity.From(val, a);
            }

            return result;
        }

        public static UVector<T> operator -(UVector<T> v1, UVector<T> v2)
        {
            var result = new UVector<T>(v1.GetVectorSize);

            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++)
            {
                val = v1[i].As(a) - v2[i].As(a);
                result[i] = (T) Quantity.From(val, a);
            }

            return result;
        }

        public static UVector<T> operator *(UVector<T> v1, double d)
        {
            var result = new UVector<T>(v1.GetVectorSize);

            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++)
            {
                val = v1[i].As(a) * d;
                result[i] = (T) Quantity.From(val, a);
            }

            return result;
        }

        public static UVector<T> operator *(double d, UVector<T> v)
        {
            return v * d;
        }

        public static UVector<T> operator /(UVector<T> v1, double d)
        {
            var result = new UVector<T>(v1.GetVectorSize);

            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++)
            {
                val = v1[i].As(a) / d;
                result[i] = (T) Quantity.From(val, a);
            }

            return result;
        }

        public static UVector<T> operator /(double d, UVector<T> v)
        {
            throw new Exception("Matrix dimensions must agree.");
        }

        public static T DotProduct(UVector<T> v1, UVector<T> v2)
        {
            var dim = v1[0].Dimensions;
            QuantityValue val = 0.0;
            var temp = 0.0;
            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;
            for (var i = 0; i < v1.GetVectorSize; i++) temp += v1[i].As(a) * v2[i].As(a);

            val = temp;
            return (T) Quantity.From(val, a);
        }


        /// <summary>
        ///     ///////////////////////
        /// </summary>
        /// <returns></returns>
        public T GetNorm()
        {
            var unit = GetNormSquare();
            var dim = _vector[0].Dimensions;
            var a = _vector[0].QuantityInfo.BaseUnitInfo.Value;

            return (T) Quantity.From(Math.Sqrt(unit.As(a)), a);
        }

        /// <summary>
        ///     need GetNormSquare to be private as units are not consistent.
        /// </summary>
        /// <returns></returns>
        private T GetNormSquare()
        {
            var dim = _vector[0].Dimensions;
            QuantityValue result = 0.0;
            var temp = 0.0;
            var a = _vector[0].QuantityInfo.BaseUnitInfo.Value;
            //  double result = 0.0;
            for (var i = 0; i < GetVectorSize; i++) temp += _vector[i].As(a) * _vector[i].As(a);

            result = temp;
            return (T) Quantity.From(result, a);
            ;
        }

        public void Normalize()
        {
            var norm = GetNorm();
            var a = _vector[0].QuantityInfo.BaseUnitInfo.Value;
            if (norm.As(a) == 0) throw new Exception("Tried to normalize a vector with a norm of zero.");

            for (var i = 0; i < GetVectorSize; i++) _vector[i] = (T) Quantity.From(_vector[i].As(a) / norm.As(a), a);
        }

        public UVector<T> GetUnitVector()
        {
            var result = new UVector<T>(_vector);
            result.Normalize();
            return result;
        }

        public RVector ToUnit(Enum unit)
        {
            var result = new RVector(GetVectorSize);
            for (var i = 0; i < GetVectorSize; i++) result[i] = _vector[i].As(unit);

            return result;
        }

        public static UVector<T> CrossProduct(UVector<T> v1, UVector<T> v2)
        {
            if (v1.GetVectorSize != 3) throw new Exception("Vector v1 must be 3 dimensional.");

            if (v2.GetVectorSize != 3) throw new Exception("Vector v2 must be 3 dimensional.");

            var a = v1[0].QuantityInfo.BaseUnitInfo.Value;

            var result = new UVector<T>(3);
            result[0] = (T) Quantity.From(v1[1].As(a) * v2[2].As(a) - v1[2].As(a) * v2[1].As(a), a);
            result[1] = (T) Quantity.From(v1[2].As(a) * v2[0].As(a) - v1[0].As(a) * v2[2].As(a), a);
            result[2] = (T) Quantity.From(v1[0].As(a) * v2[1].As(a) - v1[1].As(a) * v2[0].As(a), a);
            return result;
        }
    }
}

New Unit JERK

Need to add JERK, it is the same as Acceleration but time is cubed. This is used in motion profiling and anywhere there is a linear change in Acceleration and Parabolic change in Velocity.

New unit: Molar flow

Molar flow (i.e. AmountOfSubstance/ Duration e.g. mol/s) is a unit that would be of interest for me.

Specifically, I ran into needing to compute MassFlow / MolarMass, which I then noticed I could not cast to anything appropriate.

Thanks for the amazing work!

Ratios of units should return as a double

An engineering example of this is the ratio of specific heats (Gamma). This should be a double. UnitsNet results in the correct output of the following

            var Cp = SpecificEntropy.FromKilojoulesPerKilogramKelvin(1.00);
            var CV = SpecificEntropy.FromKilojoulesPerKilogramKelvin(0.718);
            double gamma = Cp / Cv;

In EngineeringUnits this results in an implicit conversion error.

.Minimum and .Maximum are mixed up

Test code:

var p1 = Pressure.FromBar(10);
var p2 = Pressure.FromBar(20);

Console.WriteLine($"{nameof(p1)}: {p1}"); // prints "p1: 10 bar"
Console.WriteLine($"{nameof(p2)}: {p2}"); // prints "p2: 20 bar"
Console.WriteLine($"p1.Minimum(p2): {p1.Minimum(p2)}"); // prints "p1.Minimum(p2): 20 bar"
Console.WriteLine($"p2.Minimum(p1): {p2.Minimum(p1)}"); // prints "p2.Minimum(p1): 20 bar"
Console.WriteLine($"p1.Maximum(p2): {p1.Maximum(p2)}"); // prints "p1.Maximum(p2): 10 bar"
Console.WriteLine($"p2.Maximum(p1): {p2.Maximum(p1)}"); // prints "p2.Maximum(p1): 10 bar"

Temperature conversion doesn't work

I am using sharpfluids when i noticed this bug
This is piece of code that i can isolate

static void Main(string[] args)
{
	var temperature = Temperature.FromDegreesCelsius(20);
	var pressure = Pressure.FromKilopascals(100);
	Console.WriteLine("Pressure: {0} \nTemperature: {1}", pressure.ToUnit(EngineeringUnits.Units.PressureUnit.Kilopascal), temperature.ToUnit(EngineeringUnits.Units.TemperatureUnit.DegreeCelsius));
}

Output :
Pressure: 100 kPa
Temperature: 293,2 K

Expected behavior:
Temperature in Celsius

Observed behavior:
Temperature in Kelvin

Application

For some unknown reason my IT department has not granted me permission to publish my package to Nuget at this time. (I didn't even know I needed permission until a few days ago!)

Nonetheless the source code for my application is visible: https://github.com/jccockre/constraints

It seems to automatically show up in EngineeringUnits under "Used by," which is cool.

Power Zero gives incorrect result

Currently the BaseUnit will return itself when asked to raise itself to the zeroth power.

Real numbers raised to the zeroth power are defined to be one, and dimensional quantities to the zeroth power are dimensionless.

I have a fix including unit tests but getting 403 error when trying to submit a pull request; maybe I need you to grant me permissions or maybe I don't know how to use Git.

Null comparison with operator throws NullReferenceException

Temperature? temperature = GetTemperatureFromMethodThatCouldReturnNull();

if(temperature == null)
{
   ...
}

The null comparison in the if statement throws because it uses the overloaded == operator for BaseUnit vs BaseUnit which calls:

if (left.Unit != right.Unit)

When right is null such as the comparison above this throws a NullReferenceException.

Molar Specific Heat Units Don't Match

Hello, thanks for making this great package,

I'm trying to make custom units for heat capacity using BaseUnit Class

I find that when i do:

Console.WriteLine(EnergyUnit.Kilojoule/AmountOfSubstanceUnit.Kilomole)
Console.WriteLine(EnergyUnit.SI/AmountOfSubstanceUNit.SI)

They are not equivalent.

I'm making a video guide on how to use your unit package, but the bug is at
https://youtu.be/R_2DvEKXpac
12:00 approx onwards.

I will stick to SI for now, but could you check this out? Thank you so much!

bug: Race Condition when accessing the UnitSystem.CacheMultiply and UnitSystem.CacheDivide dictionaries

The access to the caches of the multiplied and divided units is not protected from concurrent access.
I had sporadic errors when running unit tests involving unit calculations/unit initializations. I reduced the problem to the followinf unit test which fails with a high probability:

        [Fact]// (Skip = "race condition demonstration")]
        [Exploratory]
        public void ParallelAccess2()
        {
            var unit1 = LengthUnit.Meter;
            var unit2 = DurationUnit.Millisecond;

            Func<UnitSystem> dividedUnit = () =>
            {
                var unit3 = unit1.Unit / unit2.Unit;
                return unit3;
            };

            List<Task> tasks = new List<Task>();
            for (int i = 1; i < 400; ++i)
            {
                tasks.Add(Task.Run(dividedUnit));
            }

            Task.WaitAll(tasks.ToArray());
        }

Observed behavior

System.ArgumentException
An item with the same key has already been added. Key: 1100879750
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at EngineeringUnits.UnitSystem.Divide(UnitSystem a, UnitSystem b)
   at EngineeringUnits.UnitSystem.op_Division(UnitSystem left, UnitSystem right)
   at EngineeringUnitsTests.<>c__DisplayClass6_0.<ParallelAccess2>b__0() 
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)

I had another test with random units which showed some different errors, e.g.

System.InvalidOperationException
Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
   at System.Collections.Generic.Dictionary`2.FindValue(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at EngineeringUnits.UnitSystem.Divide(UnitSystem a, UnitSystem b)
   at EngineeringUnits.UnitSystem.op_Division(UnitSystem left, UnitSystem right)
   at EngineeringUnitsTests.<>c__DisplayClass5_0.<ParallelAccess>b__5() 
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)

Expected behavior:
No exception when doing unit calculations in different threads.

Unexpected / inconsistent behavior of BaseUnit.GetValueAs() for mass units (potential bug)

I'm working on a project that is using EngineeringUnits for internal representation of physical units. So far it has worked as expected, but I've come across an issue where the method

BaseUnit.GetValueAs(UnitSystem to)

returns an incorrect value when the base unit is using the same UnitSystem as the method argument. I managed to pin it down to some particular sequence of instructions - here is a minimal XUnit test to reporduce the issue:

[Fact]
public void Test()
{
    var unit = EngineeringUnits.Units.MassUnit.Gram.Unit;
    var valueWithUnit = new BaseUnit(1, unit);

    //var before = valueWithUnit.GetValueAs(unit);

    for (int i = 0; i < 2; i++)
    {
        var siUnit = unit.GetSIUnitsystem();
        var convertedValue = valueWithUnit.GetValueAs(siUnit);
        var value = new UnknownUnit(convertedValue, unit);
    }

    var after = valueWithUnit.GetValueAs(unit);
    Assert.Equal(1, after);
}

Expectated behavior: The result of GetValueAs has to be 1, because converting 1 gram to grams should yield 1 gram.

Actual behavior: The result of GetValueAs is 0.001

This test seems to work for any dimension other than mass, but I haven't verified it for every single one. Interestingly, if you execute the commented line before the for-loop, it works as expected again...

My suspicion is that it has to do with the fact that for mass units, the SI base unit is kilogram - that might explain why the returned value is off by a factor of 1000 for mass units, but not for other units. But the details are beyond my understanding, like for example I don't understand why this issue only materializes when the loop is executed more than once...

Some help on this issue and a bugfix (if this is indeed unexpected behavior) would be much appreciated.

Adding Cost/Currency/Price

Creating a Issue based in @mschmitz2's excellent suggestion in #19 to add cost.
It has been on my list for some time now so lets give it a go!

I have added following (still subject to change..)

Cost f1 = new Cost(10, CostUnit.USDollar); //10 $
Energy e1 = new Energy(10, EnergyUnit.KilowattHour); //10 kWh

EnergyCost test = f1 / e1; //1 $/kWh

Any other name we should consider for the unit representing $£€?
Price Cost Currency

Name when combining it with other units:
EnergyCost CostOfEnergy EnergySpecificCost EnergyPrice

What do you guys think?

Also, we need to figure out how to convert between the different Currencies.

  • Have a hardcoded conversion that will (automatic) be updated with each build.
  • Have a way you can override the hardcoded conversions with your own
  • an option to Connect to a public Currency exchange API that will give you the current currencies

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.