From 0409cb5b3844f6c5946272228822c8e27caf1518 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Tue, 9 May 2023 23:27:51 +0300 Subject: [PATCH 1/9] Refactoring --- pyfluids/enums/fluids_list.py | 4 ++-- pyfluids/enums/mix.py | 4 ++-- pyfluids/enums/phases.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyfluids/enums/fluids_list.py b/pyfluids/enums/fluids_list.py index 7e4689e..a5f30f5 100644 --- a/pyfluids/enums/fluids_list.py +++ b/pyfluids/enums/fluids_list.py @@ -409,10 +409,10 @@ def __init__( fraction_max, ) - def __repr__(self): + def __repr__(self) -> str: return self.name - def __str__(self): + def __str__(self) -> str: return self.name @property diff --git a/pyfluids/enums/mix.py b/pyfluids/enums/mix.py index 25ee3c1..cb964df 100644 --- a/pyfluids/enums/mix.py +++ b/pyfluids/enums/mix.py @@ -9,8 +9,8 @@ class Mix(Enum): Mass = "Mass-based mixture" Volume = "Volume-based mixture" - def __repr__(self): + def __repr__(self) -> str: return self.name - def __str__(self): + def __str__(self) -> str: return self.name diff --git a/pyfluids/enums/phases.py b/pyfluids/enums/phases.py index 5d516bc..fd0ef69 100644 --- a/pyfluids/enums/phases.py +++ b/pyfluids/enums/phases.py @@ -18,8 +18,8 @@ class Phases(Enum): Unknown = CoolProp.iphase_unknown NotImposed = CoolProp.iphase_not_imposed - def __repr__(self): + def __repr__(self) -> str: return self.name - def __str__(self): + def __str__(self) -> str: return self.name From 631ccc7aa2f12d3fdca0e08514afa100178c2294 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Tue, 9 May 2023 23:32:47 +0300 Subject: [PATCH 2/9] Added UnitsSystem enum --- pyfluids/enums/__init__.py | 3 ++- pyfluids/enums/units_system.py | 17 +++++++++++++++++ tests/enums/test_units_system.py | 13 +++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 pyfluids/enums/units_system.py create mode 100644 tests/enums/test_units_system.py diff --git a/pyfluids/enums/__init__.py b/pyfluids/enums/__init__.py index d56a598..48d969a 100644 --- a/pyfluids/enums/__init__.py +++ b/pyfluids/enums/__init__.py @@ -1,5 +1,6 @@ from .fluids_list import * from .mix import * from .phases import * +from .units_system import * -__all__ = fluids_list.__all__ + mix.__all__ + phases.__all__ +__all__ = fluids_list.__all__ + mix.__all__ + phases.__all__ + units_system.__all__ diff --git a/pyfluids/enums/units_system.py b/pyfluids/enums/units_system.py new file mode 100644 index 0000000..e8c74b1 --- /dev/null +++ b/pyfluids/enums/units_system.py @@ -0,0 +1,17 @@ +from enum import Enum + +__all__ = ["UnitsSystem"] + + +class UnitsSystem(Enum): + """List of available units systems.""" + + SI = "SI" + SIWithCelsius = "SIWithCelsius" + SIWithCelsiusAndPercents = "SIWithCelsiusAndPercents" + + def __repr__(self) -> str: + return self.name + + def __str__(self) -> str: + return self.name diff --git a/tests/enums/test_units_system.py b/tests/enums/test_units_system.py new file mode 100644 index 0000000..7d8121c --- /dev/null +++ b/tests/enums/test_units_system.py @@ -0,0 +1,13 @@ +import pytest + +from pyfluids import UnitsSystem + + +class TestUnitsSystem: + @pytest.mark.parametrize("units_system", list(UnitsSystem)) + def test_repr_always_returns_name(self, units_system: UnitsSystem): + assert repr(units_system) == units_system.name + + @pytest.mark.parametrize("units_system", list(UnitsSystem)) + def test_str_always_returns_name(self, units_system: UnitsSystem): + assert str(units_system) == units_system.name From f7c727013f8a48e029730a455b87e896a1baf194 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Wed, 10 May 2023 00:00:04 +0300 Subject: [PATCH 3/9] Added PyFluidsConfig class --- pyfluids/__init__.py | 5 ++++- pyfluids/config/__init__.py | 3 +++ pyfluids/config/pyfluids_config.py | 12 ++++++++++++ tests/config/__init__.py | 0 tests/config/test_pyfluids_config.py | 6 ++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 pyfluids/config/__init__.py create mode 100644 pyfluids/config/pyfluids_config.py create mode 100644 tests/config/__init__.py create mode 100644 tests/config/test_pyfluids_config.py diff --git a/pyfluids/__init__.py b/pyfluids/__init__.py index 674b4cc..02831f5 100644 --- a/pyfluids/__init__.py +++ b/pyfluids/__init__.py @@ -1,6 +1,9 @@ +from .config import * from .enums import * from .fluids import * from .humid_air import * from .io import * -__all__ = enums.__all__ + fluids.__all__ + humid_air.__all__ + io.__all__ +__all__ = ( + config.__all__ + enums.__all__ + fluids.__all__ + humid_air.__all__ + io.__all__ +) diff --git a/pyfluids/config/__init__.py b/pyfluids/config/__init__.py new file mode 100644 index 0000000..d8a5f9d --- /dev/null +++ b/pyfluids/config/__init__.py @@ -0,0 +1,3 @@ +from .pyfluids_config import * + +__all__ = pyfluids_config.__all__ diff --git a/pyfluids/config/pyfluids_config.py b/pyfluids/config/pyfluids_config.py new file mode 100644 index 0000000..eae7eec --- /dev/null +++ b/pyfluids/config/pyfluids_config.py @@ -0,0 +1,12 @@ +from dataclasses import dataclass + +from ..enums import UnitsSystem + +__all__ = ["PyFluidsConfig"] + + +@dataclass +class PyFluidsConfig: + """PyFluids configuration.""" + + units_system: UnitsSystem = UnitsSystem.SIWithCelsiusAndPercents diff --git a/tests/config/__init__.py b/tests/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/config/test_pyfluids_config.py b/tests/config/test_pyfluids_config.py new file mode 100644 index 0000000..c4a5274 --- /dev/null +++ b/tests/config/test_pyfluids_config.py @@ -0,0 +1,6 @@ +from pyfluids import PyFluidsConfig, UnitsSystem + + +class TestPyFluidsConfig: + def test_pyfluids_config_default_unit_system_is_si_with_celsius_and_percents(self): + assert PyFluidsConfig().units_system == UnitsSystem.SIWithCelsiusAndPercents From 2d3e04e9f930b6da07f7b71ea59405843ceb51f9 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Sun, 14 May 2023 21:39:03 +0300 Subject: [PATCH 4/9] Added tomli to the dependencies --- poetry.lock | 154 ++++++++++++++++++++++++------------------------- pyproject.toml | 1 + 2 files changed, 78 insertions(+), 77 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2420f4e..f50db7e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. [[package]] name = "colorama" @@ -70,63 +70,63 @@ files = [ [[package]] name = "coverage" -version = "7.0.0" +version = "7.2.5" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-7.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2569682d6ea9628da8d6ba38579a48b1e53081226ec7a6c82b5024b3ce5009f"}, - {file = "coverage-7.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ec256a592b497f26054195f7d7148892aca8c4cdcc064a7cc66ef7a0455b811"}, - {file = "coverage-7.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5885a4ceb6dde34271bb0adafa4a248a7f589c89821e9da3110c39f92f41e21b"}, - {file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d43d406a4d73aa7f855fa44fa77ff47e739b565b2af3844600cdc016d01e46b9"}, - {file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18df11efa615b79b9ecc13035a712957ff6283f7b244e57684e1c092869f541"}, - {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f6a4bf5bdee93f6817797beba7086292c2ebde6df0d5822e0c33f8b05415c339"}, - {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33efe89cd0efef016db19d8d05aa46631f76793de90a61b6717acb202b36fe60"}, - {file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96b5b1f1079e48f56bfccf103bcf44d48b9eb5163f1ea523fad580f15d3fe5e0"}, - {file = "coverage-7.0.0-cp310-cp310-win32.whl", hash = "sha256:fb85b7a7a4b204bd59d6d0b0c8d87d9ffa820da225e691dfaffc3137dc05b5f6"}, - {file = "coverage-7.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:793dcd9d42035746fc7637df4336f7581df19d33c5c5253cf988c99d8e93a8ba"}, - {file = "coverage-7.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d564142a03d3bc8913499a458e931b52ddfe952f69b6cd4b24d810fd2959044a"}, - {file = "coverage-7.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0a8b0e86bede874bf5da566b02194fbb12dd14ce3585cabd58452007f272ba81"}, - {file = "coverage-7.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e645c73cbfc4577d93747d3f793115acf6f907a7eb9208fa807fdcf2da1964a4"}, - {file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de06e7585abe88c6d38c1b73ce4c3cb4c1a79fbb0da0d0f8e8689ef5729ec60d"}, - {file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a30b646fbdd5bc52f506e149fa4fbdef82432baf6b81774e61ec4e3b43b9cbde"}, - {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:db8141856dc9be0917413df7200f53accf1d84c8b156868e6af058a1ea8e903a"}, - {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:59e71912c7fc78d08a567ee65656123878f49ca1b5672e660ea70bf8dfbebf8f"}, - {file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b8f7cd942dda3795fc9eadf303cc53a422ac057e3b70c2ad6d4276ec6a83a541"}, - {file = "coverage-7.0.0-cp311-cp311-win32.whl", hash = "sha256:bf437a04b9790d3c9cd5b48e9ce9aa84229040e3ae7d6c670a55118906113c5a"}, - {file = "coverage-7.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7e1bb36b4e57a2d304322021b35d4e4a25fa0d501ba56e8e51efaebf4480556"}, - {file = "coverage-7.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:215f40ef86f1958a1151fa7fad2b4f2f99534c4e10a34a1e065eba3f19ef8868"}, - {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae088eb1cbdad8206931b1bf3f11dee644e038a9300be84d3e705e29356e5b1d"}, - {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9071e197faa24837b967bc9aa0b9ef961f805a75f1ee3ea1f3367f55cd46c3c"}, - {file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f1e6d9c70d45a960d3f3d781ea62b167fdf2e0e1f6bb282b96feea653adb923"}, - {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fadd15f9fcfd7b16d9cccce9f5e6ec6f9b8df860633ad9aa62c2b14c259560f"}, - {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:10b6246cae61896ab4c7568e498e492cbb73a2dfa4c3af79141c43cf806f929a"}, - {file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a8785791c2120af114ea7a06137f7778632e568a5aa2bbfc3b46c573b702af74"}, - {file = "coverage-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:30220518dd89c4878908d73f5f3d1269f86e9e045354436534587a18c7b9da85"}, - {file = "coverage-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bc904aa96105d73357de03de76336b1e3db28e2b12067d36625fd9646ab043fd"}, - {file = "coverage-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2331b7bd84a1be79bd17ca8e103ce38db8cbf7cb354dc56e651ba489cf849212"}, - {file = "coverage-7.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e907db8bdd0ad1253a33c20fdc5f0f6209d271114a9c6f1fcdf96617343f7ca0"}, - {file = "coverage-7.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0deee68e0dae1d6e3fe6943c76d7e66fbeb6519bd08e4e5366bcc28a8a9aca"}, - {file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fff0f08bc5ffd0d78db821971472b4adc2ee876b86f743e46d634fb8e3c22f"}, - {file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a290b7921c1c05787b953e5854d394e887df40696f21381cc33c4e2179bf50ac"}, - {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:100546219af59d2ad82d4575de03a303eb27b75ea36ffbd1677371924d50bcbc"}, - {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c1ba6e63b831112b9484ff5905370d89e43d4316bac76d403031f60d61597466"}, - {file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c685fc17d6f4f1a3833e9dac27d0b931f7ccb52be6c30d269374203c7d0204a2"}, - {file = "coverage-7.0.0-cp38-cp38-win32.whl", hash = "sha256:8938f3a10f45019b502020ba9567b97b6ecc8c76b664b421705c5406d4f92fe8"}, - {file = "coverage-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:c4b63888bef2928d0eca12cbce0760cfb696acb4fe226eb55178b6a2a039328a"}, - {file = "coverage-7.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cda63459eb20652b22e038729a8f5063862c189a3963cb042a764b753172f75e"}, - {file = "coverage-7.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e06abac1a4aec1ff989131e43ca917fc7bd296f34bf0cfe86cbf74343b21566d"}, - {file = "coverage-7.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b94ad926e933976627f040f96dd1d9b0ac91f8d27e868c30a28253b9b6ac2d"}, - {file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6b4af31fb49a2ae8de1cd505fa66c403bfcc5066e845ac19d8904dcfc9d40da"}, - {file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b62f0220459e528ad5806cc7dede71aa716e067d2cb10cb4a09686b8791fba"}, - {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:43ec1935c6d6caab4f3bc126d20bd709c0002a175d62208ebe745be37a826a41"}, - {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8593c9baf1f0f273afa22f5b45508b76adc7b8e94e17e7d98fbe1e3cd5812af2"}, - {file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fee283cd36c3f14422d9c1b51da24ddbb5e1eed89ad2480f6a9f115df38b5df8"}, - {file = "coverage-7.0.0-cp39-cp39-win32.whl", hash = "sha256:97c0b001ff15b8e8882995fc07ac0a08c8baf8b13c1145f3f12e0587bbb0e335"}, - {file = "coverage-7.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8dbf83a4611c591b5de65069b6fd4dd3889200ed270cd2f7f5ac765d3842889f"}, - {file = "coverage-7.0.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:bcaf18e46668057051a312c714a4548b81f7e8fb3454116ad97be7562d2a99e4"}, - {file = "coverage-7.0.0.tar.gz", hash = "sha256:9a175da2a7320e18fc3ee1d147639a2b3a8f037e508c96aa2da160294eb50e17"}, + {file = "coverage-7.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c"}, + {file = "coverage-7.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c"}, + {file = "coverage-7.2.5-cp310-cp310-win32.whl", hash = "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5"}, + {file = "coverage-7.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6"}, + {file = "coverage-7.2.5-cp311-cp311-win32.whl", hash = "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b"}, + {file = "coverage-7.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068"}, + {file = "coverage-7.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969"}, + {file = "coverage-7.2.5-cp37-cp37m-win32.whl", hash = "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718"}, + {file = "coverage-7.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1"}, + {file = "coverage-7.2.5-cp38-cp38-win32.whl", hash = "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813"}, + {file = "coverage-7.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1"}, + {file = "coverage-7.2.5-cp39-cp39-win32.whl", hash = "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31"}, + {file = "coverage-7.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252"}, + {file = "coverage-7.2.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3"}, + {file = "coverage-7.2.5.tar.gz", hash = "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47"}, ] [package.dependencies] @@ -137,14 +137,14 @@ toml = ["tomli"] [[package]] name = "exceptiongroup" -version = "1.0.4" +version = "1.1.1" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, - {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, ] [package.extras] @@ -152,14 +152,14 @@ test = ["pytest (>=6)"] [[package]] name = "importlib-metadata" -version = "5.2.0" +version = "6.6.0" description = "Read metadata from Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_metadata-5.2.0-py3-none-any.whl", hash = "sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f"}, - {file = "importlib_metadata-5.2.0.tar.gz", hash = "sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd"}, + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, ] [package.dependencies] @@ -173,26 +173,26 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "packaging" -version = "22.0" +version = "23.1" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"}, - {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"}, + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] @@ -281,7 +281,7 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -291,33 +291,33 @@ files = [ [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] [[package]] name = "zipp" -version = "3.11.0" +version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, - {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.7, <3.12" -content-hash = "04802b71888716bae2594c631182ed0d548c3e58d5e8f444f89cb272d71df5a8" +content-hash = "22b3bccd9cfcdfd7a78f56618969df7da4c649be84d8c9be6c35049b2a2976e3" diff --git a/pyproject.toml b/pyproject.toml index 6246491..6c126ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ packages = [ [tool.poetry.dependencies] python = ">=3.7, <3.12" CoolProp = "6.4.3.post1" +tomli = "2.0.1" [tool.poetry.group.test.dependencies] pytest = "7.3.1" From dc66f1decf1bdfcdf4ad52d6f768555b9a1f8c88 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Sun, 14 May 2023 21:39:22 +0300 Subject: [PATCH 5/9] Added PyFluidsConfigBuilder class --- pyfluids/config/__init__.py | 3 +- pyfluids/config/pyfluids_config_builder.py | 110 ++++++++++++++++ pyfluids/config/singleton.py | 7 + tests/config/test_pyfluids_config_builder.py | 132 +++++++++++++++++++ 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 pyfluids/config/pyfluids_config_builder.py create mode 100644 pyfluids/config/singleton.py create mode 100644 tests/config/test_pyfluids_config_builder.py diff --git a/pyfluids/config/__init__.py b/pyfluids/config/__init__.py index d8a5f9d..8d044fd 100644 --- a/pyfluids/config/__init__.py +++ b/pyfluids/config/__init__.py @@ -1,3 +1,4 @@ from .pyfluids_config import * +from .pyfluids_config_builder import * -__all__ = pyfluids_config.__all__ +__all__ = pyfluids_config.__all__ + pyfluids_config_builder.__all__ diff --git a/pyfluids/config/pyfluids_config_builder.py b/pyfluids/config/pyfluids_config_builder.py new file mode 100644 index 0000000..3e7bbd3 --- /dev/null +++ b/pyfluids/config/pyfluids_config_builder.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +import json +from configparser import ConfigParser +from os.path import abspath +from pathlib import Path + +import tomli + +from .pyfluids_config import PyFluidsConfig +from .singleton import Singleton +from ..enums import UnitsSystem + +__all__ = ["PyFluidsConfigBuilder"] + + +# noinspection PyBroadException +class PyFluidsConfigBuilder(metaclass=Singleton): + """PyFluids configuration builder.""" + + def __init__(self): + """PyFluids configuration builder.""" + self.__config: PyFluidsConfig | None = None + self.__config_names: list[str] = [ + "pyfluids.ini", + "pyfluids.json", + "pyproject.toml", + "tox.ini", + ] + + @property + def __current_path(self) -> Path: + return Path(abspath(Path.cwd())) + + @property + def __config_file(self) -> Path | None: + for root in (self.__current_path, *self.__current_path.parents): + for config_name in self.__config_names: + path = root / config_name + if path.is_file(): + return path + return None + + @property + def __config_data(self) -> str: + return self.__config_file.read_text(encoding="utf-8") + + def build(self) -> PyFluidsConfig: + """ + Build PyFluids configuration. + + If the configuration file is not found or an incorrect + configuration is found in the files "pyproject.toml" or "tox.ini", + returns the default configuration. + + :raises ValueError: If configuration file is invalid. + """ + if self.__config is not None: + return self.__config + if self.__config_file is None: + return self.__create_default_config() + if self.__config_file.suffix == ".ini": + return self.__load_config_from_ini_file() + if self.__config_file.suffix == ".json": + return self.__load_config_from_json_file() + return self.__load_config_from_toml_file() + + def _reset(self): + self.__config = None + + def __create_default_config(self) -> PyFluidsConfig: + self.__config = PyFluidsConfig() + return self.__config + + def __create_config_from_dict(self, config_dict: dict) -> PyFluidsConfig: + config_dict["units_system"] = UnitsSystem[config_dict["units_system"]] + self.__config = PyFluidsConfig(**config_dict) + return self.__config + + def __load_config_from_ini_file(self) -> PyFluidsConfig: + try: + config_parser = ConfigParser() + config_parser.read(self.__config_file) + return self.__create_config_from_dict(dict(config_parser.items("pyfluids"))) + except Exception: + if self.__config_file.name.startswith("pyfluids"): + self.__raise_invalid_config_exception() + return self.__create_default_config() + + def __load_config_from_json_file(self) -> PyFluidsConfig: + try: + return self.__create_config_from_dict( + json.loads(self.__config_data)["pyfluids"] + ) + except Exception: + self.__raise_invalid_config_exception() + + def __load_config_from_toml_file(self) -> PyFluidsConfig: + try: + return self.__create_config_from_dict( + tomli.loads(self.__config_data)["tool"]["pyfluids"] + ) + except Exception: + return self.__create_default_config() + + def __raise_invalid_config_exception(self): + raise ValueError( + "Invalid PyFluids configuration! " + f"Check your configuration file: {self.__config_file}" + ) diff --git a/pyfluids/config/singleton.py b/pyfluids/config/singleton.py new file mode 100644 index 0000000..3776cb9 --- /dev/null +++ b/pyfluids/config/singleton.py @@ -0,0 +1,7 @@ +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] diff --git a/tests/config/test_pyfluids_config_builder.py b/tests/config/test_pyfluids_config_builder.py new file mode 100644 index 0000000..52bd556 --- /dev/null +++ b/tests/config/test_pyfluids_config_builder.py @@ -0,0 +1,132 @@ +import os +from pathlib import Path + +import pytest + +from pyfluids import PyFluidsConfigBuilder, PyFluidsConfig, UnitsSystem + +INVALID_CONTENT = "Hello, World!" + +PYFLUIDS_INI_CONTENT = """ +[pyfluids] +units_system = SI +""" + +PYFLUIDS_JSON_CONTENT = """ +{ + "pyfluids": { + "units_system": "SIWithCelsius" + } +} +""" + +PYPROJECT_TOML_CONTENT = """ +[tool.pyfluids] +units_system = SIWithCelsiusAndPercents +""" + +TOX_INI_CONTENT = PYFLUIDS_INI_CONTENT + + +class TestPyFluidsConfigBuilder: + config_builder: PyFluidsConfigBuilder = PyFluidsConfigBuilder() + + def test_config_builder_is_singleton(self): + first_config_builder = PyFluidsConfigBuilder() + second_config_builder = PyFluidsConfigBuilder() + assert hash(first_config_builder) == hash(second_config_builder) + + def test_build_invokes_once_then_returns_config_from_cache(self, tmp_path: Path): + os.chdir(tmp_path) + self.config_builder._reset() + first_config = self.config_builder.build() + config_file = tmp_path / "pyfluids.ini" + config_file.write_text(PYFLUIDS_INI_CONTENT) + second_config = self.config_builder.build() + assert first_config == second_config + + def test_build_when_config_file_is_not_found_returns_default_config( + self, tmp_path: Path + ): + os.chdir(tmp_path) + self.config_builder._reset() + config = self.config_builder.build() + assert config == PyFluidsConfig() + + def test_build_from_pyfluids_ini_when_content_is_invalid_raises_value_error( + self, tmp_path: Path + ): + os.chdir(tmp_path) + config_file = tmp_path / "pyfluids.ini" + config_file.write_text(INVALID_CONTENT) + self.config_builder._reset() + with pytest.raises(ValueError) as e: + self.config_builder.build() + assert ( + "Invalid PyFluids configuration! " + f"Check your configuration file: {config_file}" in str(e.value) + ) + + def test_build_from_pyfluids_ini_returns_specified_config(self, tmp_path: Path): + os.chdir(tmp_path) + config_file = tmp_path / "pyfluids.ini" + config_file.write_text(PYFLUIDS_INI_CONTENT) + self.config_builder._reset() + config = self.config_builder.build() + assert config.units_system == UnitsSystem.SI + + def test_build_from_pyfluids_json_when_content_is_invalid_raises_value_error( + self, tmp_path: Path + ): + os.chdir(tmp_path) + config_file = tmp_path / "pyfluids.json" + config_file.write_text(INVALID_CONTENT) + self.config_builder._reset() + with pytest.raises(ValueError) as e: + self.config_builder.build() + assert ( + "Invalid PyFluids configuration! " + f"Check your configuration file: {config_file}" in str(e.value) + ) + + def test_build_from_pyfluids_json_returns_specified_config(self, tmp_path: Path): + os.chdir(tmp_path) + config_file = tmp_path / "pyfluids.json" + config_file.write_text(PYFLUIDS_JSON_CONTENT) + self.config_builder._reset() + config = self.config_builder.build() + assert config.units_system == UnitsSystem.SIWithCelsius + + def test_build_from_pyproject_toml_when_content_is_invalid_returns_default_config( + self, tmp_path: Path + ): + os.chdir(tmp_path) + config_file = tmp_path / "pyproject.toml" + config_file.write_text(INVALID_CONTENT) + self.config_builder._reset() + assert self.config_builder.build() == PyFluidsConfig() + + def test_build_from_pyproject_toml_returns_specified_config(self, tmp_path: Path): + os.chdir(tmp_path) + config_file = tmp_path / "pyproject.toml" + config_file.write_text(PYPROJECT_TOML_CONTENT) + self.config_builder._reset() + config = self.config_builder.build() + assert config.units_system == UnitsSystem.SIWithCelsiusAndPercents + + def test_build_from_tox_ini_when_content_is_invalid_returns_default_config( + self, tmp_path: Path + ): + os.chdir(tmp_path) + config_file = tmp_path / "tox.ini" + config_file.write_text(INVALID_CONTENT) + self.config_builder._reset() + assert self.config_builder.build() == PyFluidsConfig() + + def test_build_from_tox_ini_returns_specified_config(self, tmp_path: Path): + os.chdir(tmp_path) + config_file = tmp_path / "tox.ini" + config_file.write_text(TOX_INI_CONTENT) + self.config_builder._reset() + config = self.config_builder.build() + assert config.units_system == UnitsSystem.SI From 30cfbe853c74ef303383fc0667496b6abab6be44 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Sun, 14 May 2023 22:40:08 +0300 Subject: [PATCH 6/9] Added UnitConverter class --- pyfluids/config/__init__.py | 5 +- pyfluids/config/unit_converter.py | 62 +++++++++++++++++ tests/config/test_unit_converter.py | 102 ++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 pyfluids/config/unit_converter.py create mode 100644 tests/config/test_unit_converter.py diff --git a/pyfluids/config/__init__.py b/pyfluids/config/__init__.py index 8d044fd..4f9ba64 100644 --- a/pyfluids/config/__init__.py +++ b/pyfluids/config/__init__.py @@ -1,4 +1,7 @@ from .pyfluids_config import * from .pyfluids_config_builder import * +from .unit_converter import * -__all__ = pyfluids_config.__all__ + pyfluids_config_builder.__all__ +__all__ = ( + pyfluids_config.__all__ + pyfluids_config_builder.__all__ + unit_converter.__all__ +) diff --git a/pyfluids/config/unit_converter.py b/pyfluids/config/unit_converter.py new file mode 100644 index 0000000..48ac5ed --- /dev/null +++ b/pyfluids/config/unit_converter.py @@ -0,0 +1,62 @@ +from .pyfluids_config import PyFluidsConfig +from .pyfluids_config_builder import PyFluidsConfigBuilder +from ..enums import UnitsSystem + +__all__ = ["UnitConverter"] + + +class UnitConverter: + """Unit converter.""" + + def __init__(self): + """Unit converter.""" + self.__config: PyFluidsConfig = PyFluidsConfigBuilder().build() + + @property + def units_system(self) -> UnitsSystem: + """Configured units system.""" + return self.__config.units_system + + def convert_temperature_from_si(self, value: float) -> float: + """ + Convert temperature from SI to configured units system. + + :param value: Temperature value. + :return: Converted value. + """ + if self.units_system == UnitsSystem.SI: + return value + return value - 273.15 + + def convert_temperature_to_si(self, value: float) -> float: + """ + Convert temperature from configured units system to SI. + + :param value: Temperature value. + :return: Converted value. + """ + if self.units_system == UnitsSystem.SI: + return value + return value + 273.15 + + def convert_decimal_fraction_from_si(self, value: float) -> float: + """ + Convert decimal fraction from SI to configured units system. + + :param value: Decimal fraction value. + :return: Converted value. + """ + if self.units_system == UnitsSystem.SIWithCelsiusAndPercents: + return value * 1e2 + return value + + def convert_decimal_fraction_to_si(self, value: float) -> float: + """ + Convert decimal fraction from configured units system to SI. + + :param value: Decimal fraction value. + :return: Converted value. + """ + if self.units_system == UnitsSystem.SIWithCelsiusAndPercents: + return value * 1e-2 + return value diff --git a/tests/config/test_unit_converter.py b/tests/config/test_unit_converter.py new file mode 100644 index 0000000..5753bc7 --- /dev/null +++ b/tests/config/test_unit_converter.py @@ -0,0 +1,102 @@ +import os +from pathlib import Path + +import pytest + +from pyfluids import PyFluidsConfigBuilder, UnitConverter, UnitsSystem + + +class TestUnitConverter: + config_builder: PyFluidsConfigBuilder = PyFluidsConfigBuilder() + + @pytest.mark.parametrize("units_system", list(UnitsSystem)) + def test_units_system_returns_configured_value( + self, units_system: UnitsSystem, tmp_path: Path + ): + self.__configure(units_system, tmp_path) + assert UnitConverter().units_system == units_system + + @pytest.mark.parametrize( + "units_system, value, expected_result", + [ + (UnitsSystem.SI, 293.15, 293.15), + (UnitsSystem.SIWithCelsius, 293.15, 20), + (UnitsSystem.SIWithCelsiusAndPercents, 293.15, 20), + ], + ) + def test_convert_temperature_from_si( + self, + units_system: UnitsSystem, + value: float, + expected_result: float, + tmp_path: Path, + ): + self.__configure(units_system, tmp_path) + assert UnitConverter().convert_temperature_from_si(value) == expected_result + + @pytest.mark.parametrize( + "units_system, value, expected_result", + [ + (UnitsSystem.SI, 293.15, 293.15), + (UnitsSystem.SIWithCelsius, 20, 293.15), + (UnitsSystem.SIWithCelsiusAndPercents, 20, 293.15), + ], + ) + def test_convert_temperature_to_si( + self, + units_system: UnitsSystem, + value: float, + expected_result: float, + tmp_path: Path, + ): + self.__configure(units_system, tmp_path) + assert UnitConverter().convert_temperature_to_si(value) == expected_result + + @pytest.mark.parametrize( + "units_system, value, expected_result", + [ + (UnitsSystem.SI, 0.5, 0.5), + (UnitsSystem.SIWithCelsius, 0.5, 0.5), + (UnitsSystem.SIWithCelsiusAndPercents, 0.5, 50), + ], + ) + def test_convert_decimal_fraction_from_si( + self, + units_system: UnitsSystem, + value: float, + expected_result: float, + tmp_path: Path, + ): + self.__configure(units_system, tmp_path) + assert ( + UnitConverter().convert_decimal_fraction_from_si(value) == expected_result + ) + + @pytest.mark.parametrize( + "units_system, value, expected_result", + [ + (UnitsSystem.SI, 0.5, 0.5), + (UnitsSystem.SIWithCelsius, 0.5, 0.5), + (UnitsSystem.SIWithCelsiusAndPercents, 50, 0.5), + ], + ) + def test_convert_decimal_fraction_to_si( + self, + units_system: UnitsSystem, + value: float, + expected_result: float, + tmp_path: Path, + ): + self.__configure(units_system, tmp_path) + assert UnitConverter().convert_decimal_fraction_to_si(value) == expected_result + + def __configure(self, units_system: UnitsSystem, tmp_path: Path): + os.chdir(tmp_path) + config_file = tmp_path / "pyfluids.ini" + config_file.write_text( + f""" + [pyfluids] + units_system = {units_system} + """ + ) + self.config_builder._reset() From f466c9188efee9a253f8f2926451a42d28d6bc90 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Sun, 14 May 2023 23:39:02 +0300 Subject: [PATCH 7/9] Refactoring --- pyfluids/config/__init__.py | 6 +++++- pyfluids/config/pyfluids_config.py | 2 +- pyfluids/config/pyfluids_config_builder.py | 2 +- pyfluids/config/unit_converter.py | 2 +- pyfluids/{enums => config}/units_system.py | 0 pyfluids/enums/__init__.py | 3 +-- tests/{enums => config}/test_units_system.py | 0 7 files changed, 9 insertions(+), 6 deletions(-) rename pyfluids/{enums => config}/units_system.py (100%) rename tests/{enums => config}/test_units_system.py (100%) diff --git a/pyfluids/config/__init__.py b/pyfluids/config/__init__.py index 4f9ba64..8a78f5a 100644 --- a/pyfluids/config/__init__.py +++ b/pyfluids/config/__init__.py @@ -1,7 +1,11 @@ from .pyfluids_config import * from .pyfluids_config_builder import * from .unit_converter import * +from .units_system import * __all__ = ( - pyfluids_config.__all__ + pyfluids_config_builder.__all__ + unit_converter.__all__ + pyfluids_config.__all__ + + pyfluids_config_builder.__all__ + + unit_converter.__all__ + + units_system.__all__ ) diff --git a/pyfluids/config/pyfluids_config.py b/pyfluids/config/pyfluids_config.py index eae7eec..b030f08 100644 --- a/pyfluids/config/pyfluids_config.py +++ b/pyfluids/config/pyfluids_config.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from ..enums import UnitsSystem +from .units_system import UnitsSystem __all__ = ["PyFluidsConfig"] diff --git a/pyfluids/config/pyfluids_config_builder.py b/pyfluids/config/pyfluids_config_builder.py index 3e7bbd3..dccec0a 100644 --- a/pyfluids/config/pyfluids_config_builder.py +++ b/pyfluids/config/pyfluids_config_builder.py @@ -9,7 +9,7 @@ from .pyfluids_config import PyFluidsConfig from .singleton import Singleton -from ..enums import UnitsSystem +from .units_system import UnitsSystem __all__ = ["PyFluidsConfigBuilder"] diff --git a/pyfluids/config/unit_converter.py b/pyfluids/config/unit_converter.py index 48ac5ed..52e15fd 100644 --- a/pyfluids/config/unit_converter.py +++ b/pyfluids/config/unit_converter.py @@ -1,6 +1,6 @@ from .pyfluids_config import PyFluidsConfig from .pyfluids_config_builder import PyFluidsConfigBuilder -from ..enums import UnitsSystem +from .units_system import UnitsSystem __all__ = ["UnitConverter"] diff --git a/pyfluids/enums/units_system.py b/pyfluids/config/units_system.py similarity index 100% rename from pyfluids/enums/units_system.py rename to pyfluids/config/units_system.py diff --git a/pyfluids/enums/__init__.py b/pyfluids/enums/__init__.py index 48d969a..d56a598 100644 --- a/pyfluids/enums/__init__.py +++ b/pyfluids/enums/__init__.py @@ -1,6 +1,5 @@ from .fluids_list import * from .mix import * from .phases import * -from .units_system import * -__all__ = fluids_list.__all__ + mix.__all__ + phases.__all__ + units_system.__all__ +__all__ = fluids_list.__all__ + mix.__all__ + phases.__all__ diff --git a/tests/enums/test_units_system.py b/tests/config/test_units_system.py similarity index 100% rename from tests/enums/test_units_system.py rename to tests/config/test_units_system.py From 7150530606f02efa982ff355b099022c2cc6d889 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Thu, 25 May 2023 22:09:33 +0300 Subject: [PATCH 8/9] Added unit converting --- pyfluids/enums/fluids_list.py | 16 +++- pyfluids/fluids/abstract_fluid.py | 138 ++++++++++++++++++++++++------ pyfluids/fluids/fluid.py | 29 +++++-- pyfluids/fluids/mixture.py | 39 +++++++-- pyfluids/humid_air/humid_air.py | 82 ++++++++++++++---- pyfluids/io/input.py | 11 ++- pyfluids/io/input_humid_air.py | 21 +++-- tests/fluids/test_mixture.py | 11 ++- 8 files changed, 265 insertions(+), 82 deletions(-) diff --git a/pyfluids/enums/fluids_list.py b/pyfluids/enums/fluids_list.py index a5f30f5..5504a8e 100644 --- a/pyfluids/enums/fluids_list.py +++ b/pyfluids/enums/fluids_list.py @@ -1,6 +1,8 @@ from enum import Enum from .mix import Mix +from ..config import UnitConverter + __all__ = ["FluidsList"] @@ -437,10 +439,16 @@ def mix_type(self) -> Mix: @property def fraction_min(self) -> float: - """Minimum possible fraction [%].""" - return self.__fraction_min * 1e2 + """ + Minimum possible fraction + [by default, %; you can change this using the configuration file]. + """ + return UnitConverter().convert_decimal_fraction_from_si(self.__fraction_min) @property def fraction_max(self) -> float: - """Maximum possible fraction [%].""" - return self.__fraction_max * 1e2 + """ + Maximum possible fraction + [by default, %; you can change this using the configuration file]. + """ + return UnitConverter().convert_decimal_fraction_from_si(self.__fraction_max) diff --git a/pyfluids/fluids/abstract_fluid.py b/pyfluids/fluids/abstract_fluid.py index e23019e..8a8edd8 100644 --- a/pyfluids/fluids/abstract_fluid.py +++ b/pyfluids/fluids/abstract_fluid.py @@ -7,6 +7,7 @@ from CoolProp import AbstractState from CoolProp.CoolProp import generate_update_pair +from ..config import UnitConverter, UnitsSystem from ..enums import Phases from ..io import Input, OutputsValidator @@ -44,6 +45,15 @@ def __init__(self): self.__temperature: float | None = None self.__triple_pressure: float | None = None self.__triple_temperature: float | None = None + self._unit_converter: UnitConverter = UnitConverter() + self._fraction_unit: str = ( + " %" if self.units_system == UnitsSystem.SIWithCelsiusAndPercents else "" + ) + + @property + def units_system(self) -> UnitsSystem: + """Configured units system.""" + return self._unit_converter.units_system @property def compressibility(self) -> float | None: @@ -68,10 +78,17 @@ def critical_pressure(self) -> float | None: @property def critical_temperature(self) -> float | None: - """Temperature at the critical point [°C].""" + """ + Temperature at the critical point + [by default, °C; you can change this using the configuration file]. + """ if self.__critical_temperature is None: value = self._nullable_keyed_output(CoolProp.iT_critical) - self.__critical_temperature = value - 273.15 if value is not None else None + self.__critical_temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__critical_temperature @property @@ -104,10 +121,17 @@ def entropy(self) -> float: @property def freezing_temperature(self) -> float | None: - """Temperature at the freezing point (for incompressible fluids) [°C].""" + """ + Temperature at the freezing point (for incompressible fluids) + [by default, °C; you can change this using the configuration file]. + """ if self.__freezing_temperature is None: value = self._nullable_keyed_output(CoolProp.iT_freeze) - self.__freezing_temperature = value - 273.15 if value is not None else None + self.__freezing_temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__freezing_temperature @property @@ -135,10 +159,17 @@ def max_pressure(self) -> float | None: @property def max_temperature(self) -> float: - """Maximum temperature limit [°C].""" + """ + Maximum temperature limit + [by default, °C; you can change this using the configuration file]. + """ if self.__max_temperature is None: value = self._keyed_output(CoolProp.iT_max) - self.__max_temperature = value - 273.15 if value is not None else None + self.__max_temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__max_temperature @property @@ -150,10 +181,17 @@ def min_pressure(self) -> float | None: @property def min_temperature(self) -> float: - """Minimum temperature limit [°C].""" + """ + Minimum temperature limit + [by default, °C; you can change this using the configuration file]. + """ if self.__min_temperature is None: value = self._keyed_output(CoolProp.iT_min) - self.__min_temperature = value - 273.15 if value is not None else None + self.__min_temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__min_temperature @property @@ -186,10 +224,17 @@ def pressure(self) -> float: @property def quality(self) -> float | None: - """Mass vapor quality [%].""" + """ + Mass vapor quality + [by default, %; you can change this using the configuration file]. + """ if self.__quality is None: value = self._nullable_keyed_output(CoolProp.iQ) - self.__quality = value * 1e2 if value is not None else None + self.__quality = ( + self._unit_converter.convert_decimal_fraction_from_si(value) + if value is not None + else None + ) return self.__quality @property @@ -217,10 +262,17 @@ def surface_tension(self) -> float | None: @property def temperature(self) -> float: - """Temperature [°C].""" + """ + Temperature + [by default, °C; you can change this using the configuration file]. + """ if self.__temperature is None: value = self._keyed_output(CoolProp.iT) - self.__temperature = value - 273.15 if value is not None else None + self.__temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__temperature @property @@ -232,10 +284,17 @@ def triple_pressure(self) -> float | None: @property def triple_temperature(self) -> float | None: - """Temperature at the triple point [°C].""" + """ + Temperature at the triple point + [by default, °C; you can change this using the configuration file]. + """ if self.__triple_temperature is None: value = self._nullable_keyed_output(CoolProp.iT_triple) - self.__triple_temperature = value - 273.15 if value is not None else None + self.__triple_temperature = ( + self._unit_converter.convert_temperature_from_si(value) + if value is not None + else None + ) return self.__triple_temperature @abstractmethod @@ -322,11 +381,15 @@ def compression_to_pressure( The process of compression to a given pressure. :param pressure: Absolute pressure [Pa]. - :param isentropic_efficiency: Compressor isentropic efficiency [%]. + :param isentropic_efficiency: Compressor isentropic efficiency + [by default, %; you can change this using the configuration file]. :return: The state of the fluid at the end of the process. :raises ValueError: If pressure or isentropic efficiency is invalid. """ - if not 0 < isentropic_efficiency < 100: + isentropic_efficiency = self._unit_converter.convert_decimal_fraction_to_si( + isentropic_efficiency + ) + if not 0 < isentropic_efficiency < 1: raise ValueError("Invalid compressor isentropic efficiency!") return self.with_state( Input.pressure(pressure), @@ -336,7 +399,7 @@ def compression_to_pressure( self.isentropic_compression_to_pressure(pressure).enthalpy - self.enthalpy ) - / (isentropic_efficiency * 1e-2) + / isentropic_efficiency ), ) @@ -375,11 +438,15 @@ def expansion_to_pressure( The process of expansion to a given pressure. :param pressure: Absolute pressure [Pa]. - :param isentropic_efficiency: Expander isentropic efficiency [%]. + :param isentropic_efficiency: Expander isentropic efficiency + [by default, %; you can change this using the configuration file]. :return: The state of the fluid at the end of the process. :raises ValueError: If pressure or isentropic efficiency is invalid. """ - if not 0 < isentropic_efficiency < 100: + isentropic_efficiency = self._unit_converter.convert_decimal_fraction_to_si( + isentropic_efficiency + ) + if not 0 < isentropic_efficiency < 1: raise ValueError("Invalid expander isentropic efficiency!") return self.with_state( Input.pressure(pressure), @@ -389,7 +456,7 @@ def expansion_to_pressure( self.enthalpy - self.isentropic_expansion_to_pressure(pressure).enthalpy ) - * (isentropic_efficiency * 1e-2) + * isentropic_efficiency ), ) @@ -399,7 +466,8 @@ def cooling_to_temperature( """ The process of cooling to a given temperature. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the fluid at the end of the process. :raises ValueError: If temperature or pressure drop is invalid. @@ -433,7 +501,8 @@ def heating_to_temperature( """ The process of heating to a given temperature. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the fluid at the end of the process. :raises ValueError: If temperature or pressure drop is invalid. @@ -474,8 +543,9 @@ def bubble_point_at_temperature(self, temperature: float) -> AbstractFluid: """ Bubble point at a given temperature. - :param temperature: Temperature [°C]. - :return: TBubble point at a given temperature. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. + :return: Bubble point at a given temperature. """ return self.with_state(Input.temperature(temperature), Input.quality(0)) @@ -486,16 +556,27 @@ def dew_point_at_pressure(self, pressure: float) -> AbstractFluid: :param pressure: Absolute pressure [Pa]. :return: Dew point at a given pressure. """ - return self.with_state(Input.pressure(pressure), Input.quality(100)) + return self.with_state( + Input.pressure(pressure), + Input.quality( + 100 if self.units_system == UnitsSystem.SIWithCelsiusAndPercents else 1 + ), + ) def dew_point_at_temperature(self, temperature: float) -> AbstractFluid: """ Dew point at a given temperature. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :return: Dew point at a given temperature. """ - return self.with_state(Input.temperature(temperature), Input.quality(100)) + return self.with_state( + Input.temperature(temperature), + Input.quality( + 100 if self.units_system == UnitsSystem.SIWithCelsiusAndPercents else 1 + ), + ) def two_phase_point_at_pressure( self, pressure: float, quality: float @@ -504,7 +585,8 @@ def two_phase_point_at_pressure( Two phase point at a given pressure. :param pressure: Absolute pressure [Pa]. - :param quality: Vapor quality [%]. + :param quality: Vapor quality + [by default, %; you can change this using the configuration file]. :return: Two phase point at a given pressure. """ return self.with_state(Input.pressure(pressure), Input.quality(quality)) diff --git a/pyfluids/fluids/fluid.py b/pyfluids/fluids/fluid.py index 5f84d8b..319ac86 100644 --- a/pyfluids/fluids/fluid.py +++ b/pyfluids/fluids/fluid.py @@ -3,6 +3,7 @@ from CoolProp import AbstractState from .abstract_fluid import AbstractFluid +from ..config import UnitsSystem from ..enums import FluidsList, Mix __all__ = ["Fluid"] @@ -16,23 +17,28 @@ def __init__(self, name: FluidsList, fraction: float | None = None): Pure/pseudo-pure fluid or binary mixture. :param name: Selected fluid name. - :param fraction: Mass-based or volume-based fraction for binary mixtures [%]. + :param fraction: Mass-based or volume-based fraction for binary mixtures + [by default, %; you can change this using the configuration file]. :raises ValueError: If fraction is invalid. """ + super().__init__() if fraction is not None and ( not name.fraction_min <= fraction <= name.fraction_max ): raise ValueError( f"Invalid fraction value! It should be in " f"[{'{0:g}'.format(name.fraction_min)};" - f"{'{0:g}'.format(name.fraction_max)}] %. " - f"Entered value = {'{0:g}'.format(fraction)} %." + f"{'{0:g}'.format(name.fraction_max)}]{self._fraction_unit}. " + f"Entered value = {'{0:g}'.format(fraction)}{self._fraction_unit}." ) if fraction is None and not name.pure: raise ValueError("Need to define fraction!") - super().__init__() self.__name = name - self.__fraction = 100 if self.__name.pure else fraction + self.__fraction = ( + (100 if self.units_system == UnitsSystem.SIWithCelsiusAndPercents else 1) + if self.__name.pure + else fraction + ) self._backend = AbstractState( self.__name.coolprop_backend, self.__name.coolprop_name ) @@ -49,7 +55,10 @@ def name(self) -> FluidsList: @property def fraction(self) -> float: - """Mass-based or volume-based fraction for binary mixtures [%].""" + """ + Mass-based or volume-based fraction for binary mixtures + [by default, %; you can change this using the configuration file]. + """ return self.__fraction def mixing( @@ -67,9 +76,13 @@ def mixing( def __set_fraction(self): if self.__name.mix_type == Mix.Mass: - self._backend.set_mass_fractions([self.__fraction * 1e-2]) + self._backend.set_mass_fractions( + [self._unit_converter.convert_decimal_fraction_to_si(self.__fraction)] + ) else: - self._backend.set_volu_fractions([self.__fraction * 1e-2]) + self._backend.set_volu_fractions( + [self._unit_converter.convert_decimal_fraction_to_si(self.__fraction)] + ) def __is_valid_fluids_for_mixing( self, first: AbstractFluid, second: AbstractFluid diff --git a/pyfluids/fluids/mixture.py b/pyfluids/fluids/mixture.py index 2dae823..93e65f4 100644 --- a/pyfluids/fluids/mixture.py +++ b/pyfluids/fluids/mixture.py @@ -3,6 +3,7 @@ from CoolProp import AbstractState from .abstract_fluid import AbstractFluid +from ..config import UnitsSystem from ..enums import FluidsList __all__ = ["Mixture"] @@ -18,9 +19,11 @@ def __init__(self, fluids: list[FluidsList], fractions: list[float]): Mass-based mixture of pure fluids. :param fluids: List of selected names of pure fluids. - :param fractions: List of mass-based fractions [%]. + :param fractions: List of mass-based fractions + [by default, %; you can change this using the configuration file]. :raises ValueError: If fluids or fractions are invalid. """ + super().__init__() if len(fluids) != len(fractions): raise ValueError( "Invalid input! Fluids and fractions should be of the same length." @@ -33,21 +36,38 @@ def __init__(self, fluids: list[FluidsList], fractions: list[float]): "Invalid components! All of them should be " f"a pure fluid with {self.__AVAILABLE_BACKEND} backend." ) - if not all(0 < fraction < 100 for fraction in fractions): + if not all( + 0 < self._unit_converter.convert_decimal_fraction_to_si(fraction) < 1 + for fraction in fractions + ): + fraction_range = ( + "(0;100) %" + if self.units_system == UnitsSystem.SIWithCelsiusAndPercents + else "(0;1)" + ) raise ValueError( - "Invalid component mass fractions! All of them should be in (0;100) %." + "Invalid components mass fractions! " + f"All of them should be in {fraction_range}." ) - if abs(sum(fractions) - 100) > 1e-6: + fractions_sum = ( + 100 if self.units_system == UnitsSystem.SIWithCelsiusAndPercents else 1 + ) + if abs(sum(fractions) - fractions_sum) > 1e-6: raise ValueError( - "Invalid component mass fractions! Their sum should be equal to 100 %." + "Invalid components mass fractions! " + f"Their sum should be equal to {fractions_sum}{self._fraction_unit}." ) - super().__init__() self.__fluids, self.__fractions = fluids, fractions self._backend = AbstractState( self.__AVAILABLE_BACKEND, "&".join(fluid.coolprop_name for fluid in self.__fluids), ) - self._backend.set_mass_fractions(self.__fractions) + self._backend.set_mass_fractions( + [ + self._unit_converter.convert_decimal_fraction_to_si(fraction) + for fraction in self.__fractions + ] + ) def factory(self) -> Mixture: return Mixture(self.__fluids, self.__fractions) @@ -59,7 +79,10 @@ def fluids(self) -> list[FluidsList]: @property def fractions(self) -> list[float]: - """List of mass-based fractions [%].""" + """ + List of mass-based fractions + [by default, %; you can change this using the configuration file]. + """ return self.__fractions def __eq__(self, other: Mixture) -> bool: diff --git a/pyfluids/humid_air/humid_air.py b/pyfluids/humid_air/humid_air.py index 524f5ef..a408737 100644 --- a/pyfluids/humid_air/humid_air.py +++ b/pyfluids/humid_air/humid_air.py @@ -4,6 +4,7 @@ from CoolProp.HumidAirProp import HAPropsSI +from ..config import UnitConverter, UnitsSystem from ..io import InputHumidAir, OutputsValidator __all__ = ["HumidAir"] @@ -29,6 +30,12 @@ def __init__(self): self.__specific_heat: float | None = None self.__temperature: float | None = None self.__wet_bulb_temperature: float | None = None + self._unit_converter: UnitConverter = UnitConverter() + + @property + def units_system(self) -> UnitsSystem: + """Configured units system.""" + return self._unit_converter.units_system @property def compressibility(self) -> float: @@ -53,9 +60,14 @@ def density(self) -> float: @property def dew_temperature(self) -> float: - """Dew-point temperature [°C].""" + """ + Dew-point temperature + [by default, °C; you can change this using the configuration file]. + """ if self.__dew_temperature is None: - self.__dew_temperature = self._keyed_output("D") - 273.15 + self.__dew_temperature = self._unit_converter.convert_temperature_from_si( + self._keyed_output("D") + ) return self.__dew_temperature @property @@ -112,9 +124,16 @@ def pressure(self) -> float: @property def relative_humidity(self) -> float: - """Relative humidity ratio [%].""" + """ + Relative humidity ratio + [by default, %; you can change this using the configuration file]. + """ if self.__relative_humidity is None: - self.__relative_humidity = self._keyed_output("R") * 1e2 + self.__relative_humidity = ( + self._unit_converter.convert_decimal_fraction_from_si( + self._keyed_output("R") + ) + ) return self.__relative_humidity @property @@ -126,16 +145,28 @@ def specific_heat(self) -> float: @property def temperature(self) -> float: - """Dry-bulb temperature [°C].""" + """ + Dry-bulb temperature + [by default, °C; you can change this using the configuration file]. + """ if self.__temperature is None: - self.__temperature = self._keyed_output("T") - 273.15 + self.__temperature = self._unit_converter.convert_temperature_from_si( + self._keyed_output("T") + ) return self.__temperature @property def wet_bulb_temperature(self) -> float: - """Wet-bulb temperature [°C].""" + """ + Wet-bulb temperature + [by default, °C; you can change this using the configuration file]. + """ if self.__wet_bulb_temperature is None: - self.__wet_bulb_temperature = self._keyed_output("B") - 273.15 + self.__wet_bulb_temperature = ( + self._unit_converter.convert_temperature_from_si( + self._keyed_output("B") + ) + ) return self.__wet_bulb_temperature def factory(self) -> HumidAir: @@ -208,7 +239,8 @@ def dry_cooling_to_temperature( """ The process of cooling without dehumidification to a given temperature. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the humid air at the end of the process. :raises ValueError: If temperature or pressure drop is invalid. @@ -235,8 +267,10 @@ def wet_cooling_to_temperature_and_relative_humidity( The process of cooling with dehumidification to a given temperature and relative humidity ratio. - :param temperature: Temperature [°C]. - :param relative_humidity: Relative humidity ratio [%]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. + :param relative_humidity: Relative humidity ratio + [by default, %; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the humid air at the end of the process. :raises ValueError: If temperature, relative humidity ratio or @@ -255,7 +289,8 @@ def wet_cooling_to_temperature_and_absolute_humidity( The process of cooling with dehumidification to a given temperature and absolute humidity ratio. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :param humidity: Absolute humidity ratio [kg/kg d.a.]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the humid air at the end of the process. @@ -276,7 +311,8 @@ def wet_cooling_to_enthalpy_and_relative_humidity( to a given enthalpy and relative humidity ratio. :param enthalpy: Enthalpy [J/kg]. - :param relative_humidity: Relative humidity ratio [%]. + :param relative_humidity: Relative humidity ratio + [by default, %; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the humid air at the end of the process. :raises ValueError: If enthalpy, relative humidity ratio or @@ -314,7 +350,8 @@ def heating_to_temperature( """ The process of heating to a given temperature. - :param temperature: Temperature [°C]. + :param temperature: Temperature + [by default, °C; you can change this using the configuration file]. :param pressure_drop: Pressure drop in the heat exchanger (optional) [Pa]. :return: The state of the humid air at the end of the process. :raises ValueError: If temperature or pressure drop is invalid. @@ -343,7 +380,8 @@ def humidification_by_water_to_relative_humidity( The process of humidification by water (isenthalpic) to a given relative humidity ratio. - :param relative_humidity: Relative humidity ratio [%]. + :param relative_humidity: Relative humidity ratio + [by default, %; you can change this using the configuration file]. :return: The state of the humid air at the end of the process. :raises ValueError: If relative humidity ratio is invalid. """ @@ -373,7 +411,8 @@ def humidification_by_steam_to_relative_humidity( The process of humidification by steam (isothermal) to a given relative humidity ratio. - :param relative_humidity: Relative humidity ratio [%]. + :param relative_humidity: Relative humidity ratio + [by default, %; you can change this using the configuration file]. :return: The state of the humid air at the end of the process. :raises ValueError: If relative humidity ratio is invalid. """ @@ -527,7 +566,10 @@ def __wet_cooling_to( pressure_drop: float = 0, ): if first_input.coolprop_key == "T": - self.__check_temperature(first_input.value - 273.15, True) + self.__check_temperature( + self._unit_converter.convert_temperature_from_si(first_input.value), + True, + ) if first_input.coolprop_key == "Hha": self.__check_enthalpy(first_input.value, True) self.__check_pressure_drop(pressure_drop) @@ -589,7 +631,11 @@ def __check_dew_enthalpy(self, enthalpy: float): < self.with_state( InputHumidAir.pressure(self.pressure), InputHumidAir.temperature(self.dew_temperature), - InputHumidAir.relative_humidity(100), + InputHumidAir.relative_humidity( + 100 + if self.units_system == UnitsSystem.SIWithCelsiusAndPercents + else 1 + ), ).enthalpy ): raise ValueError( diff --git a/pyfluids/io/input.py b/pyfluids/io/input.py index a56a4a5..920fc1a 100644 --- a/pyfluids/io/input.py +++ b/pyfluids/io/input.py @@ -3,6 +3,7 @@ import CoolProp from .abstract_input import AbstractInput +from ..config import UnitConverter __all__ = ["Input"] @@ -74,17 +75,19 @@ def quality(cls, value: float) -> Input: """ Mass vapor quality. - :param value: The value of the input [%]. + :param value: The value of the input + [by default, %; you can change this using the configuration file]. :return: Mass vapor quality for the input. """ - return cls(CoolProp.iQ, value * 1e-2) + return cls(CoolProp.iQ, UnitConverter().convert_decimal_fraction_to_si(value)) @classmethod def temperature(cls, value: float) -> Input: """ Temperature. - :param value: The value of the input [°C]. + :param value: The value of the input + [by default, °C; you can change this using the configuration file]. :return: Temperature for the input. """ - return cls(CoolProp.iT, value + 273.15) + return cls(CoolProp.iT, UnitConverter().convert_temperature_to_si(value)) diff --git a/pyfluids/io/input_humid_air.py b/pyfluids/io/input_humid_air.py index adb2a14..ac4297d 100644 --- a/pyfluids/io/input_humid_air.py +++ b/pyfluids/io/input_humid_air.py @@ -1,6 +1,7 @@ from __future__ import annotations from .abstract_input import AbstractInput +from ..config import UnitConverter __all__ = ["InputHumidAir"] @@ -51,10 +52,11 @@ def dew_temperature(cls, value: float) -> InputHumidAir: """ Dew-point temperature. - :param value: The value of the input [°C]. + :param value: The value of the input + [by default, °C; you can change this using the configuration file]. :return: Dew-point temperature for the input. """ - return cls("D", value + 273.15) + return cls("D", UnitConverter().convert_temperature_to_si(value)) @classmethod def enthalpy(cls, value: float) -> InputHumidAir: @@ -111,27 +113,30 @@ def relative_humidity(cls, value: float) -> InputHumidAir: """ Relative humidity. - :param value: The value of the input [%]. + :param value: The value of the input + [by default, %; you can change this using the configuration file]. :return: Relative humidity for the input. """ - return cls("R", value * 1e-2) + return cls("R", UnitConverter().convert_decimal_fraction_to_si(value)) @classmethod def temperature(cls, value: float) -> InputHumidAir: """ Temperature. - :param value: The value of the input [°C]. + :param value: The value of the input + [by default, °C; you can change this using the configuration file]. :return: Temperature for the input. """ - return cls("T", value + 273.15) + return cls("T", UnitConverter().convert_temperature_to_si(value)) @classmethod def wet_bulb_temperature(cls, value: float) -> InputHumidAir: """ Wet-bulb temperature. - :param value: The value of the input [°C]. + :param value: The value of the input + [by default, °C; you can change this using the configuration file]. :return: Wet-bulb temperature for the input. """ - return cls("B", value + 273.15) + return cls("B", UnitConverter().convert_temperature_to_si(value)) diff --git a/tests/fluids/test_mixture.py b/tests/fluids/test_mixture.py index 953cff1..87685bd 100644 --- a/tests/fluids/test_mixture.py +++ b/tests/fluids/test_mixture.py @@ -16,7 +16,7 @@ class TestMixture: ( [FluidsList.Water], mixture.fractions, - "Invalid input! Fluids and fractions should be of the same length.", + "Invalid input! Fluids and fractions " "should be of the same length.", ), ( [FluidsList.MPG, FluidsList.MEG], @@ -27,17 +27,20 @@ class TestMixture: ( mixture.fluids, [-200, 50], - "Invalid component mass fractions! All of them should be in (0;100) %.", + "Invalid components mass fractions! " + "All of them should be in (0;100) %.", ), ( mixture.fluids, [50, 200], - "Invalid component mass fractions! All of them should be in (0;100) %.", + "Invalid components mass fractions! " + "All of them should be in (0;100) %.", ), ( mixture.fluids, [80, 80], - "Invalid component mass fractions! Their sum should be equal to 100 %.", + "Invalid components mass fractions! " + "Their sum should be equal to 100 %.", ), ], ) From f7f7c72d47e45be31d7266c3138c0e52b9614dd8 Mon Sep 17 00:00:00 2001 From: Vladimir Portyanikhin <86243191+portyanikhin@users.noreply.github.com> Date: Thu, 25 May 2023 22:35:48 +0300 Subject: [PATCH 9/9] Fix typo --- tests/config/test_pyfluids_config_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config/test_pyfluids_config_builder.py b/tests/config/test_pyfluids_config_builder.py index 52bd556..1dc43bc 100644 --- a/tests/config/test_pyfluids_config_builder.py +++ b/tests/config/test_pyfluids_config_builder.py @@ -22,7 +22,7 @@ PYPROJECT_TOML_CONTENT = """ [tool.pyfluids] -units_system = SIWithCelsiusAndPercents +units_system = "SIWithCelsiusAndPercents" """ TOX_INI_CONTENT = PYFLUIDS_INI_CONTENT