Skip to content

Commit

Permalink
Merge pull request #65 from tarao1006/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
tarao1006 authored Jul 18, 2021
2 parents 71acac7 + b001dc2 commit 68301b4
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 99 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.0
0.5.0
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'tarao1006'

# The full version, including alpha/beta/rc tags
release = '0.4.0'
release = '0.5.0'


# -- General configuration ---------------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ Quick Start
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from pyheatintegration import PinchAnalyzer, Stream
from pyheatintegration import PinchAnalyzer, Stream, extract_x, y_range
# 熱交換を行う流体を準備
streams = [
Stream(40.0, 90.0, 150.0),
Stream(80.0, 110.0, 180.0),
Stream(125.0, 80.0, 180.0),
Stream(100.0, 60.0, 160.0)
Stream(40.0, 90.0, 150.0),
Stream(80.0, 110.0, 180.0),
Stream(125.0, 80.0, 180.0),
Stream(100.0, 60.0, 160.0)
]
# 最小接近温度差を指定し、作成した流体のリストとともにPinchAnalyzerのインスタンスを得る。
Expand Down
6 changes: 5 additions & 1 deletion src/pyheatintegration/errors.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
class InvalidStreamError(Exception):
class PyHeatIntegrationError(Exception):
"""ヒートインテグレーションに由来するエラー"""


class InvalidStreamError(PyHeatIntegrationError):
"""流体に関するエラー"""
84 changes: 60 additions & 24 deletions src/pyheatintegration/pinch_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import math
from copy import deepcopy

from .errors import PyHeatIntegrationError
from .grand_composite_curve import GrandCompositeCurve
from .heat_exchanger import HeatExchanger
from .heat_range import HeatRange, get_merged_heat_ranges
from .line import Line
from .plot_segment import PlotSegment, get_plot_segments
from .stream import Stream, is_valid_streams
from .stream import Stream
from .tq_diagram import TQDiagram, get_possible_minimum_temp_diff_range


Expand Down Expand Up @@ -40,7 +41,7 @@ class PinchAnalyzer:
Args:
streams_ (list[Stream]): 流体のリスト。
minimum_approach_temp_diff (float): 最小接近温度差 [℃]。
ignore_maximum (bool): 最小接近温度差の最大値のチェックを無視するかどうか
force_validation (bool): 与熱流体と受熱流体の最高温度と最低温度の関係の検証を強制するか
Attributes:
gcc (GrandCompositeCurve): グランドコンポジットカーブ。
Expand All @@ -61,39 +62,24 @@ def __init__(
self,
streams_: list[Stream],
minimum_approach_temp_diff: float,
force_validation: bool = False
force_validation: bool = True
):
streams = deepcopy(streams_)

id_set: set[str] = set()
for stream in streams:
if stream.id_ in id_set:
raise ValueError(
'流体のidは一意である必要があります。'
f'重複しているid: {stream.id_}'
)
id_set.add(stream.id_)

if not is_valid_streams(streams):
raise ValueError('与熱流体および受熱流体は少なくとも1つは指定する必要があります。')

# 外部流体が存在する場合は検証を行う。
ignore_validation = True
for stream in streams:
if stream.is_external():
ignore_validation = False
break
ignore_validation = not any(stream.is_external() for stream in streams)

if force_validation:
ignore_validation = False

if message := PinchAnalyzer.validate_streams(streams, ignore_validation):
raise PyHeatIntegrationError(message)

self.minimum_approach_temp_diff_range = get_possible_minimum_temp_diff_range(
streams,
ignore_validation
streams, ignore_validation
)

if minimum_approach_temp_diff not in self.minimum_approach_temp_diff_range:
raise ValueError(
raise PyHeatIntegrationError(
"最小接近温度差が不正です。"
f"指定最小接近温度差 [℃]: {minimum_approach_temp_diff}, "
f"設定可能最小接近温度差 [℃]: {self.minimum_approach_temp_diff_range.start:.3f}"
Expand Down Expand Up @@ -143,6 +129,56 @@ def __init__(
HeatExchanger(heat_range, hot_plot_segment, cold_plot_segment)
)

@staticmethod
def validate_streams(
streams: list[Stream],
ignore_validation: bool = False
) -> str:
# idの重複を検証。必ず検証する必要あり。
id_set: set[str] = set()
for stream in streams:
if stream.id_ in id_set:
return (
'流体のidは一意である必要があります。'
f'重複しているid: {stream.id_}'
)
id_set.add(stream.id_)

# 与熱流体と受熱流体が必ず一つ以上設定されていることを検証。必ず検証する必要がり。
hot_streams = [stream for stream in streams if stream.is_hot()]
cold_streams = [stream for stream in streams if stream.is_cold()]

if not hot_streams or not cold_streams:
return '与熱流体および受熱流体は少なくとも1つは指定する必要があります。'

hot_maximum_temp = max(stream.input_temperature() for stream in hot_streams)
hot_minimum_temp = min(stream.output_temperature() for stream in hot_streams)
cold_maximum_temp = max(stream.output_temperature() for stream in cold_streams)
cold_minimum_temp = min(stream.input_temperature() for stream in cold_streams)

# 与熱流体の最高温度 ≤ 受熱流体の最低温度は解析不可能。必ず検証する必要あり。
if hot_maximum_temp <= cold_minimum_temp:
return '与熱流体の最高温度が受熱流体の最低温度を下回っています。'

# 与熱流体の最低温度と受熱流体の最低温度、与熱流体の最高温度と受熱流体の最高温度の関係
# を検証。必ず検証する必要なし。
if not ignore_validation:
if hot_minimum_temp - cold_minimum_temp < 0:
return (
'与熱流体の最低温度が受熱流体の最低温度を下回っています。'
f'与熱流体最低温度: {hot_minimum_temp:.3f} ℃ '
f'受熱流体最低温度: {cold_minimum_temp:.3f} ℃'
)

if hot_maximum_temp - cold_maximum_temp < 0:
return (
'与熱流体の最高温度が受熱流体の最高温度を下回っています。'
f'与熱流体最高温度: {hot_maximum_temp:.3f} ℃ '
f'受熱流体最高温度: {cold_maximum_temp:.3f} ℃'
)

return ''

def create_grand_composite_curve(self) -> tuple[list[float], list[float]]:
"""グランドコンポジットカーブを描くために必要な熱量と温度を返します。
"""
Expand Down
32 changes: 0 additions & 32 deletions src/pyheatintegration/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,38 +333,6 @@ def update_heat(self, heat_flow: float) -> None:
self.heat_flow = heat_flow


def is_valid_streams(streams: list[Stream]) -> bool:
"""与熱流体および受熱流体がそれぞれ1つ以上含まれるかどうかを返します。
Args:
streams (list[Stream]): 流体のリスト。
Returns:
bool: 流体のリストが不正かどうか。
Example:
>>> is_valid_streams([
Stream(40, 90, 150),
Stream(80, 110, 180),
Stream(125, 80, 160),
Stream(100, 60, 160)
])
True
>>> is_valid_streams([
Stream(40, 90, 150),
Stream(80, 110, 180)
])
False
"""

hot_streams = [stream for stream in streams if stream.is_hot()]
cold_streams = [stream for stream in streams if stream.is_cold()]

if not hot_streams or not cold_streams:
return False
return True


def get_temperature_range_streams(
streams: list[Stream]
) -> tuple[
Expand Down
14 changes: 0 additions & 14 deletions src/pyheatintegration/tq_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,20 +387,6 @@ def get_possible_minimum_temp_diff_range(
if ignore_validation:
maximum_minimum_approch_temp_diff = hot_maximum_temp - cold_minimum_temp
else:
if hot_minimum_temp - cold_minimum_temp < 0:
raise ValueError(
'与熱流体の最低温度が受熱流体の最低温度を下回っています。'
f'与熱流体最低温度: {hot_minimum_temp:.3f} ℃ '
f'受熱流体最低温度: {cold_minimum_temp:.3f} ℃'
)

if hot_maximum_temp - cold_maximum_temp < 0:
raise ValueError(
'与熱流体の最高温度が受熱流体の最高温度を下回っています。'
f'与熱流体最高温度: {hot_maximum_temp:.3f} ℃ '
f'受熱流体最高温度: {cold_maximum_temp:.3f} ℃'
)

maximum_minimum_approch_temp_diff = min(
hot_maximum_temp - cold_maximum_temp,
hot_minimum_temp - cold_minimum_temp
Expand Down
63 changes: 60 additions & 3 deletions tests/pinch_analyzer_test.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,70 @@
import unittest

from src.pyheatintegration.pinch_analyzer import PinchAnalyzer
from src.pyheatintegration.stream import Stream


class TestPinchAnalyzer(unittest.TestCase):

def test(self):
with self.assertRaises(ValueError):
PinchAnalyzer([], 10.0)
def test_duplication_id(self):
self.assertEqual(
'流体のidは一意である必要があります。重複しているid: a',
PinchAnalyzer.validate_streams([
Stream(10, 20, 100, id_='a'),
Stream(15, 25, 100, id_='a'),
])
)

def test_empty_hot_streams(self):
self.assertEqual(
'与熱流体および受熱流体は少なくとも1つは指定する必要があります。',
PinchAnalyzer.validate_streams([Stream(10, 20, 100)])
)

def test_empty_cold_streams(self):
self.assertEqual(
'与熱流体および受熱流体は少なくとも1つは指定する必要があります。',
PinchAnalyzer.validate_streams([Stream(20, 10, 100)])
)

def test_hot_maximum_temp_below_cold_minimum_temp(self):
self.assertEqual(
'与熱流体の最高温度が受熱流体の最低温度を下回っています。',
PinchAnalyzer.validate_streams([
Stream(40, 50, 100),
Stream(30, 10, 100)
])
)

def test_hot_minimum_temp_below_cold_minimum_temp(self):
self.assertEqual(
('与熱流体の最低温度が受熱流体の最低温度を下回っています。'
'与熱流体最低温度: 35.000 ℃ '
'受熱流体最低温度: 40.000 ℃'),
PinchAnalyzer.validate_streams([
Stream(40, 50, 100),
Stream(50, 35, 100)
])
)

def test_hot_maximum_temp_below_cold_maximum_temp(self):
self.assertEqual(
('与熱流体の最高温度が受熱流体の最高温度を下回っています。'
'与熱流体最高温度: 45.000 ℃ '
'受熱流体最高温度: 50.000 ℃'),
PinchAnalyzer.validate_streams([
Stream(40, 50, 100),
Stream(45, 40, 100)
])
)

def test_no_error(self):
self.assertEqual(
'',
PinchAnalyzer.validate_streams(
[Stream(10, 20, 100), Stream(25, 15, 100)]
)
)


if __name__ == '__main__':
Expand Down
19 changes: 1 addition & 18 deletions tests/stream_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from src.pyheatintegration.enums import StreamType
from src.pyheatintegration.errors import InvalidStreamError
from src.pyheatintegration.stream import (Stream,
get_temperature_range_streams,
is_valid_streams)
get_temperature_range_streams)
from src.pyheatintegration.temperature_range import TemperatureRange


Expand Down Expand Up @@ -69,21 +68,5 @@ def test_get_temperature_range_streams(self):
self.assertEqual(result_stream.type_, expected_stream.type_)


class TestIsValidStreams(unittest.TestCase):

def test_is_valid_streams(self):
self.assertTrue(is_valid_streams([
Stream(40, 90, 150),
Stream(80, 110, 180),
Stream(125, 80, 160),
Stream(100, 60, 160)
]))

self.assertFalse(is_valid_streams([
Stream(40, 90, 150),
Stream(80, 110, 180)
]))


if __name__ == '__main__':
unittest.main()

0 comments on commit 68301b4

Please sign in to comment.