Skip to content

Commit ac8f619

Browse files
Merge pull request #328 from MassimoCimmino/maintenance/issue327_version-2-3-1
Maintenance/issue327 version 2 3 1
2 parents 035d0f7 + ed50f0a commit ac8f619

File tree

21 files changed

+4163
-3082
lines changed

21 files changed

+4163
-3082
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ on:
55
branches:
66
- master
77
- maintenance/2.2.x
8+
- maintenance/2.3.x
89
pull_request:
910
branches:
1011
- master
1112
- maintenance/2.2.x
13+
- maintenance/2.3.x
1214
types: [opened, synchronize, reopened]
1315

1416
jobs:

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# History of changes
22

3+
## Version 2.3.1 (2025-08-04)
4+
5+
### New features
6+
7+
* [Pull Request 325](https://github.com/MassimoCimmino/pygfunction/pull/325) - Borefields and boreholes can now be concatenated using the `+` operator, e.g. using `new_field = field_1 + field_2`.
8+
* [Pull Request 326](https://github.com/MassimoCimmino/pygfunction/pull/326) - Introduced `gFunction.from_static_params` and `Network.from_static_params` methods. These methods facilitate the creation of `Network` objects and the evaluation of g-functions by automatically evaluating the required thermal resistances for the creation of `Pipe` objects.
9+
10+
### Other changes
11+
12+
* [Issue 319](https://github.com/MassimoCimmino/pygfunction/issues/319) - Created `solvers` module. `Solver` classes are moved out of the `gfunction` module and into the new module.
13+
314
## Version 2.3 (2025-04-29)
415

516
### New features

doc/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
# The short X.Y version.
7777
version = u'2.3'
7878
# The full version, including alpha/beta/rc tags.
79-
release = u'2.3.1.dev0'
79+
release = u'2.3.1'
8080

8181
# The language for content autogenerated by Sphinx. Refer to documentation
8282
# for a list of supported languages.

doc/source/modules.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ Modules
1414
modules/media
1515
modules/networks
1616
modules/pipes
17+
modules/solvers
1718
modules/utilities

doc/source/modules/solvers.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.. solvers:
2+
3+
**************
4+
Solvers Module
5+
**************
6+
7+
.. automodule:: pygfunction.solvers
8+
:members:
9+
:show-inheritance:

pygfunction/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
media,
88
networks,
99
pipes,
10+
solvers,
1011
utilities,
1112
)

pygfunction/borefield.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,42 @@ def __ne__(
107107
check = not self == other_field
108108
return check
109109

110+
def __add__(self,
111+
other_field: Union[Borehole, List[Borehole], Self]) -> Self:
112+
"""Add two borefields together"""
113+
if not isinstance(other_field, (Borehole, list, self.__class__)):
114+
raise TypeError(
115+
f'Expected Borefield, list or Borehole input;'
116+
f' got {other_field}'
117+
)
118+
# List of boreholes
119+
field = self.to_boreholes()
120+
# Convert other_field to a list if it is a Borehole
121+
if isinstance(other_field, Borehole):
122+
other_field = [other_field]
123+
# Convert borefield to a list if it is a Borefield
124+
if isinstance(other_field, self.__class__):
125+
other_field = other_field.to_boreholes()
126+
return Borefield.from_boreholes(field + other_field)
127+
128+
def __radd__(self,
129+
other_field: Union[Borehole, List[Borehole], Self]) -> Self:
130+
"""Add two borefields together"""
131+
if not isinstance(other_field, (Borehole, list, self.__class__)):
132+
raise TypeError(
133+
f'Expected Borefield, list or Borehole input;'
134+
f' got {other_field}'
135+
)
136+
# List of boreholes
137+
field = self.to_boreholes()
138+
# Convert other_field to a list if it is a Borehole
139+
if isinstance(other_field, Borehole):
140+
other_field = [other_field]
141+
# Convert borefield to a list if it is a Borefield
142+
if isinstance(other_field, self.__class__):
143+
other_field = other_field.to_boreholes()
144+
return Borefield.from_boreholes(other_field + field)
145+
110146
def evaluate_g_function(
111147
self,
112148
alpha: float,

pygfunction/boreholes.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*- coding: utf-8 -*-
2+
from typing import Union
23
import warnings
34

45
import numpy as np
56
from scipy.spatial.distance import pdist
7+
from typing_extensions import Self # for compatibility with Python <= 3.10
68

79
from .utilities import _initialize_figure, _format_axes, _format_axes_3d
810

@@ -54,6 +56,56 @@ def __repr__(self):
5456
f' orientation={self.orientation})')
5557
return s
5658

59+
def __add__(self, other: Union[Self, list]):
60+
"""
61+
Adds two boreholes together to form a borefield
62+
"""
63+
if not isinstance(other, (self.__class__, list)):
64+
# Check if other is a borefield and try the operation using
65+
# other.__radd__
66+
try:
67+
field = other.__radd__(self)
68+
except:
69+
# Invalid input
70+
raise TypeError(
71+
f'Expected Borefield, list or Borehole input;'
72+
f' got {other}'
73+
)
74+
elif isinstance(other, list):
75+
# Create a borefield from the borehole and a list
76+
from .borefield import Borefield
77+
field = Borefield.from_boreholes([self] + other)
78+
else:
79+
# Create a borefield from the two boreholes
80+
from .borefield import Borefield
81+
field = Borefield.from_boreholes([self, other])
82+
return field
83+
84+
def __radd__(self, other: Union[Self, list]):
85+
"""
86+
Adds two boreholes together to form a borefield
87+
"""
88+
if not isinstance(other, (self.__class__, list)):
89+
# Check if other is a borefield and try the operation using
90+
# other.__radd__
91+
try:
92+
field = other.__add__(self)
93+
except:
94+
# Invalid input
95+
raise TypeError(
96+
f'Expected Borefield, list or Borehole input;'
97+
f' got {other}'
98+
)
99+
elif isinstance(other, list):
100+
# Create a borefield from the borehole and a list
101+
from .borefield import Borefield
102+
field = Borefield.from_boreholes(other + [self])
103+
else:
104+
# Create a borefield from the two boreholes
105+
from .borefield import Borefield
106+
field = Borefield.from_boreholes([other, self])
107+
return field
108+
57109
def distance(self, target):
58110
"""
59111
Evaluate the distance between the current borehole and a target

pygfunction/enums.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from enum import Enum, auto
2+
from typing import Annotated
3+
4+
5+
class PipeType(Enum):
6+
"""Enumerator for pipe configuration type."""
7+
COAXIAL_ANNULAR_IN: Annotated[
8+
int, "Coaxial pipe (annular inlet)"
9+
] = auto()
10+
COAXIAL_ANNULAR_OUT: Annotated[
11+
int, "Coaxial pipe (annular outlet)"
12+
] = auto()
13+
DOUBLE_UTUBE_PARALLEL: Annotated[
14+
int, "Double U-tube (parallel)"
15+
] = auto()
16+
DOUBLE_UTUBE_SERIES: Annotated[
17+
int, "Double U-tube (series)"
18+
] = auto()
19+
SINGLE_UTUBE: Annotated[
20+
int, "Single U-tube"
21+
] = auto()

0 commit comments

Comments
 (0)