Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Units_Engine/Convert/FromUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Units;

namespace BH.Engine.Units
{
public static partial class Convert
{
[Description("Converts a value from the given unit symbol to SI, e.g. 12 \"mm\" → 0.012 m.")]
[Input("value", "The value in the specified unit.")]
[Input("unitSymbol", "Unit symbol (e.g. \"mm\", \"kN\", \"MPa\"). Case-sensitive.")]
[Output("siValue", "The value in SI units, or NaN if the unit is unrecognised.")]
public static double FromUnit(this double value, string unitSymbol)
{
UnitSpec spec = Query.ResolveUnit(unitSymbol);
if (spec == null)
return double.NaN;

return FromUnit(value, spec);
}

private static double FromUnit(double value, UnitSpec spec)
{
switch (spec.Family)
{
case QuantityFamily.Length: return value.FromLength(spec.Unit);
case QuantityFamily.Area: return value.FromArea(spec.Unit);
case QuantityFamily.Volume: return value.FromVolume(spec.Unit);
case QuantityFamily.AreaMomentOfInertia: return value.FromAreaMomentOfInertia(spec.Unit);
case QuantityFamily.Pressure: return value.FromPressure(spec.Unit);
case QuantityFamily.Force: return value.FromForce(spec.Unit);
case QuantityFamily.Torque: return value.FromTorque(spec.Unit);
case QuantityFamily.ForcePerLength: return value.FromForcePerLength(spec.Unit);
case QuantityFamily.Angle: return value.FromAngle(spec.Unit);
case QuantityFamily.Mass: return value.FromMass(spec.Unit);
case QuantityFamily.Acceleration: return value.FromAcceleration(spec.Unit);
case QuantityFamily.Density: return value.FromDensity(spec.Unit);
case QuantityFamily.Energy: return value.FromEnergy(spec.Unit);
case QuantityFamily.Speed: return value.FromSpeed(spec.Unit);
case QuantityFamily.Temperature: return value.FromTemperature(spec.Unit);
case QuantityFamily.Time: return value.FromDuration(spec.Unit);
case QuantityFamily.None: return value;
default: return double.NaN;
}
}
}
}
69 changes: 69 additions & 0 deletions Units_Engine/Convert/ToUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Units;

namespace BH.Engine.Units
{
public static partial class Convert
{
[Description("Converts an SI value to the given unit symbol, e.g. 0.012 m → 12 \"mm\".")]
[Input("siValue", "The value in SI units.")]
[Input("unitSymbol", "Unit symbol to convert to (e.g. \"mm\", \"kN\", \"MPa\"). Case-sensitive.")]
[Output("value", "The value in the specified unit, or NaN if the unit is unrecognised.")]
public static double ToUnit(this double siValue, string unitSymbol)
{
UnitSpec spec = Query.ResolveUnit(unitSymbol);
if (spec == null)
return double.NaN;

return ToUnit(siValue, spec);
}

private static double ToUnit(double siValue, UnitSpec spec)
{
switch (spec.Family)
{
case QuantityFamily.Length: return siValue.ToLength(spec.Unit);
case QuantityFamily.Area: return siValue.ToArea(spec.Unit);
case QuantityFamily.Volume: return siValue.ToVolume(spec.Unit);
case QuantityFamily.AreaMomentOfInertia: return siValue.ToAreaMomentOfInertia(spec.Unit);
case QuantityFamily.Pressure: return siValue.ToPressure(spec.Unit);
case QuantityFamily.Force: return siValue.ToForce(spec.Unit);
case QuantityFamily.Torque: return siValue.ToTorque(spec.Unit);
case QuantityFamily.ForcePerLength: return siValue.ToForcePerLength(spec.Unit);
case QuantityFamily.Angle: return siValue.ToAngle(spec.Unit);
case QuantityFamily.Mass: return siValue.ToMass(spec.Unit);
case QuantityFamily.Acceleration: return siValue.ToAcceleration(spec.Unit);
case QuantityFamily.Density: return siValue.ToDensity(spec.Unit);
case QuantityFamily.Energy: return siValue.ToEnergy(spec.Unit);
case QuantityFamily.Speed: return siValue.ToSpeed(spec.Unit);
case QuantityFamily.Temperature: return siValue.ToTemperature(spec.Unit);
case QuantityFamily.Time: return siValue.ToDuration(spec.Unit);
case QuantityFamily.None: return siValue;
default: return double.NaN;
}
}
}
}
145 changes: 145 additions & 0 deletions Units_Engine/Query/ResolveUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System;
using System.Collections.Generic;
using System.Globalization;

using UN = UnitsNet; //This is to avoid clashes between UnitsNet quantity attributes and BHoM quantity attributes
using UNU = UnitsNet.Units;

using System.ComponentModel;
using BH.oM.Base.Attributes;
using BH.oM.Units;
using BH.Engine.Base;

namespace BH.Engine.Units
{
public static partial class Query
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Resolves a unit symbol string to its corresponding UnitSpec, identifying the quantity family and unit.")]
[Input("unitSymbol", "Unit symbol to resolve (e.g. \"mm\", \"kN\", \"MPa\"). Case-sensitive.")]
[Output("unitSpec", "The resolved UnitSpec, or null if the unit is unrecognised.")]
public static UnitSpec ResolveUnit(string unitSymbol)
{
string key = unitSymbol?.Trim() ?? "";
if (key == "" || key == "-")
return new UnitSpec { Family = QuantityFamily.None, Unit = null };

if (m_UnitTable.TryGetValue(key, out UnitSpec spec))
return spec;

Compute.RecordError($"Unit '{unitSymbol}' is not recognised. Unit symbols are case-sensitive.");
return null;
}

/***************************************************/
/**** Private Methods ****/
/***************************************************/

private static Dictionary<string, UnitSpec> BuildUnitTable()
{
var table = new Dictionary<string, UnitSpec>(StringComparer.Ordinal);

foreach (var family in m_Families)
{
foreach (object unit in Enum.GetValues(family.Key))
{
foreach (string symbol in GetAbbreviations(family.Key, (int)(object)unit))
{
TryAdd(table, symbol, family.Value, unit);
TryAdd(table, Ascii(symbol), family.Value, unit);
}
}
}

// Torque aliases UnitsNet doesn't publish
TryAdd(table, "Nm", QuantityFamily.Torque, UNU.TorqueUnit.NewtonMeter);
TryAdd(table, "N.m", QuantityFamily.Torque, UNU.TorqueUnit.NewtonMeter);
TryAdd(table, "kNm", QuantityFamily.Torque, UNU.TorqueUnit.KilonewtonMeter);
TryAdd(table, "kN.m", QuantityFamily.Torque, UNU.TorqueUnit.KilonewtonMeter);

return table;
}

/***************************************************/

private static IEnumerable<string> GetAbbreviations(Type unitType, int unitValue)
{
try
{
return UN.UnitsNetSetup.Default.UnitAbbreviations
.GetUnitAbbreviations(unitType, unitValue, CultureInfo.InvariantCulture);
}
catch
{
return Array.Empty<string>();
}
}

/***************************************************/

private static void TryAdd(Dictionary<string, UnitSpec> table, string symbol,
QuantityFamily family, object unit)
{
if (!string.IsNullOrWhiteSpace(symbol) && !table.ContainsKey(symbol))
table[symbol] = new UnitSpec { Family = family, Unit = unit };
}

/***************************************************/

private static string Ascii(string symbol)
{
return symbol?
.Replace("²", "2")
.Replace("³", "3")
.Replace("⁴", "4")
.Replace("⁶", "6");
}

/***************************************************/
/**** Private Fields ****/
/***************************************************/

private static readonly Dictionary<Type, QuantityFamily> m_Families = new Dictionary<Type, QuantityFamily>
{
{ typeof(UNU.LengthUnit), QuantityFamily.Length },
{ typeof(UNU.AreaUnit), QuantityFamily.Area },
{ typeof(UNU.VolumeUnit), QuantityFamily.Volume },
{ typeof(UNU.AreaMomentOfInertiaUnit), QuantityFamily.AreaMomentOfInertia },
{ typeof(UNU.PressureUnit), QuantityFamily.Pressure },
{ typeof(UNU.ForceUnit), QuantityFamily.Force },
{ typeof(UNU.TorqueUnit), QuantityFamily.Torque },
{ typeof(UNU.ForcePerLengthUnit), QuantityFamily.ForcePerLength },
{ typeof(UNU.AngleUnit), QuantityFamily.Angle },
{ typeof(UNU.MassUnit), QuantityFamily.Mass },
};

private static readonly Dictionary<string, UnitSpec> m_UnitTable = BuildUnitTable();

/***************************************************/
}
}
49 changes: 49 additions & 0 deletions Units_oM/Enums/QuantityFamily.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System.ComponentModel;

namespace BH.oM.Units
{
[Description("Categorises physical quantities for dispatch to the correct conversion method.")]
public enum QuantityFamily
{
None = 0,
Length = 1,
Area = 2,
Volume = 3,
AreaMomentOfInertia = 4,
Pressure = 5,
Force = 6,
Torque = 7,
ForcePerLength = 8,
Angle = 9,
Mass = 10,
Acceleration = 11,
Density = 12,
Energy = 13,
Power = 14,
Speed = 15,
Temperature = 16,
Time = 17,
}
}
33 changes: 33 additions & 0 deletions Units_oM/UnitSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using System.ComponentModel;

namespace BH.oM.Units
{
[Description("Specification linking a unit symbol to its quantity family and unit enumeration value.")]
public class UnitSpec
{
public virtual QuantityFamily Family { get; set; } = QuantityFamily.None;
public virtual object Unit { get; set; } = null;
}
}