geoloop 0.0.1__py3-none-any.whl → 1.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. geoloop/axisym/AxisymetricEL.py +751 -0
  2. geoloop/axisym/__init__.py +3 -0
  3. geoloop/bin/Flowdatamain.py +89 -0
  4. geoloop/bin/Lithologymain.py +84 -0
  5. geoloop/bin/Loadprofilemain.py +100 -0
  6. geoloop/bin/Plotmain.py +250 -0
  7. geoloop/bin/Runbatch.py +81 -0
  8. geoloop/bin/Runmain.py +86 -0
  9. geoloop/bin/SingleRunSim.py +928 -0
  10. geoloop/bin/__init__.py +3 -0
  11. geoloop/cli/__init__.py +0 -0
  12. geoloop/cli/batch.py +106 -0
  13. geoloop/cli/main.py +105 -0
  14. geoloop/configuration.py +946 -0
  15. geoloop/constants.py +112 -0
  16. geoloop/geoloopcore/CoaxialPipe.py +503 -0
  17. geoloop/geoloopcore/CustomPipe.py +727 -0
  18. geoloop/geoloopcore/__init__.py +3 -0
  19. geoloop/geoloopcore/b2g.py +739 -0
  20. geoloop/geoloopcore/b2g_ana.py +535 -0
  21. geoloop/geoloopcore/boreholedesign.py +683 -0
  22. geoloop/geoloopcore/getloaddata.py +112 -0
  23. geoloop/geoloopcore/pyg_ana.py +280 -0
  24. geoloop/geoloopcore/pygfield_ana.py +519 -0
  25. geoloop/geoloopcore/simulationparameters.py +130 -0
  26. geoloop/geoloopcore/soilproperties.py +152 -0
  27. geoloop/geoloopcore/strat_interpolator.py +194 -0
  28. geoloop/lithology/__init__.py +3 -0
  29. geoloop/lithology/plot_lithology.py +277 -0
  30. geoloop/lithology/process_lithology.py +697 -0
  31. geoloop/loadflowdata/__init__.py +3 -0
  32. geoloop/loadflowdata/flow_data.py +161 -0
  33. geoloop/loadflowdata/loadprofile.py +325 -0
  34. geoloop/plotting/__init__.py +3 -0
  35. geoloop/plotting/create_plots.py +1137 -0
  36. geoloop/plotting/load_data.py +432 -0
  37. geoloop/utils/RunManager.py +164 -0
  38. geoloop/utils/__init__.py +0 -0
  39. geoloop/utils/helpers.py +841 -0
  40. geoloop-1.0.0b1.dist-info/METADATA +112 -0
  41. geoloop-1.0.0b1.dist-info/RECORD +46 -0
  42. geoloop-1.0.0b1.dist-info/entry_points.txt +2 -0
  43. geoloop-0.0.1.dist-info/licenses/LICENSE → geoloop-1.0.0b1.dist-info/licenses/LICENSE.md +2 -1
  44. geoloop-0.0.1.dist-info/METADATA +0 -10
  45. geoloop-0.0.1.dist-info/RECORD +0 -6
  46. {geoloop-0.0.1.dist-info → geoloop-1.0.0b1.dist-info}/WHEEL +0 -0
  47. {geoloop-0.0.1.dist-info → geoloop-1.0.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,112 @@
1
+ import numpy as np
2
+
3
+ from geoloop.configuration import FlowDataConfig, LoadProfileConfig, SingleRunConfig
4
+ from geoloop.loadflowdata.flow_data import FlowData
5
+ from geoloop.loadflowdata.loadprofile import LoadProfile
6
+
7
+
8
+ class GetLoadData:
9
+ """
10
+ Class to generate time series of flow rates, inlet temperatures, and heat/power loads
11
+ for simulations.
12
+
13
+ It can use constant input values or optional time-dependent variations from either
14
+ a FlowData object (variable flow rates) or a LoadProfile object (variable heat load).
15
+
16
+ Attributes
17
+ ----------
18
+ Tin : np.ndarray
19
+ Inlet temperature array (°C) over the simulation time (constant through time).
20
+ m_flow : np.ndarray
21
+ Mass flow rate array (kg/s) over the simulation time.
22
+ Q : np.ndarray
23
+ Heat load array (W) over the simulation time.
24
+ time : np.ndarray
25
+ Time array in seconds.
26
+ data : FlowData or LoadProfile, optional
27
+ Optional object providing time-dependent flow or load.
28
+ """
29
+
30
+ # model types
31
+ FINVOL = "FINVOL" # finite volume approach with borehole heat exchanger system, can only run with run_type TIN
32
+ ANALYTICAL = (
33
+ "ANALYTICAL" # semi-analytical approach, can run with both TIN and POWER
34
+ )
35
+ PYG = "PYG" # 'standard' pyg approach,
36
+
37
+ # run types
38
+ TIN = "TIN" # input temperature
39
+ POWER = "POWER" # input power
40
+
41
+ def __init__(
42
+ self,
43
+ Tin: float,
44
+ m_flow: float,
45
+ Q: float,
46
+ time: np.ndarray,
47
+ loaddata: LoadProfile | FlowData = None,
48
+ ):
49
+ """
50
+ Initialize the GetLoadData object.
51
+
52
+ Parameters
53
+ ----------
54
+ Tin : float
55
+ Constant inlet temperature (°C) if no LoadData is provided.
56
+ m_flow : float
57
+ Constant mass flow rate (kg/s) if no LoadData is provided.
58
+ Q : float
59
+ Constant heat load (W) if no LoadData is provided.
60
+ time : np.ndarray
61
+ Array of time points (seconds) for the simulation.
62
+ loaddata : FlowData or LoadProfile, optional
63
+ Optional object providing variable flow or load data.
64
+ """
65
+ self.data = loaddata
66
+
67
+ if isinstance(loaddata, FlowData):
68
+ self.Tin = np.ones_like(time) * Tin
69
+ self.Q = np.ones_like(time) * Q
70
+ self.m_flow = loaddata.getflow(time / 3600.0)
71
+ elif isinstance(loaddata, LoadProfile):
72
+ self.Tin = np.ones_like(time) * Tin
73
+ self.Q, self.m_flow = loaddata.getloadflow(time / 3600.0, m_flow)
74
+ elif loaddata is None:
75
+ self.Tin = np.ones_like(time) * Tin
76
+ self.Q = np.ones_like(time) * Q
77
+ self.m_flow = np.ones_like(time) * m_flow
78
+
79
+ @classmethod
80
+ def from_config(cls, config: SingleRunConfig) -> "GetLoadData":
81
+ """
82
+ Factory method to create a GetLoadData object from a configuration dictionary.
83
+
84
+ This method constructs the time array and optionally initializes either
85
+ a FlowData or LoadProfile object depending on the run type.
86
+
87
+ Parameters
88
+ ----------
89
+ config : SingleRunConfig
90
+ Configuration object of main simulation module
91
+
92
+ Returns
93
+ -------
94
+ GetLoadData
95
+ Initialized object with time series for flow, temperature, and heat load.
96
+ """
97
+ # Build time array
98
+ tmax = config.nyear * 8760 * 3600
99
+ dt = 3600 * config.nled
100
+ Nt = int(np.ceil(tmax / dt))
101
+ time = dt * np.arange(1, Nt + 1)
102
+
103
+ # Determine optional subobject
104
+ data = None
105
+ if config.run_type == "POWER" and config.loadprofile:
106
+ loadprofile_config = LoadProfileConfig(**config.loadprofile)
107
+ data = LoadProfile.from_config(loadprofile_config)
108
+ elif config.run_type == "TIN" and config.flow_data:
109
+ flow_data_config = FlowDataConfig(**config.flow_data)
110
+ data = FlowData.from_config(flow_data_config)
111
+
112
+ return cls(config.Tin, config.m_flow, config.Q, time, loaddata=data)
@@ -0,0 +1,280 @@
1
+ import numpy as np
2
+ import pygfunction as gt
3
+ from scipy.constants import pi
4
+
5
+ from geoloop.geoloopcore.CoaxialPipe import CoaxialPipe
6
+ from geoloop.geoloopcore.CustomPipe import CustomPipe
7
+ from geoloop.geoloopcore.simulationparameters import SimulationParameters
8
+ from geoloop.geoloopcore.soilproperties import SoilProperties
9
+
10
+
11
+ class PYG_ana:
12
+ """
13
+ Class to simulate a borehole heat exchanger using pygfunction for the determination of the thermal resistivity
14
+ network of the borehole and analytical g-function.
15
+
16
+ Attributes
17
+ ----------
18
+ custom_pipe : CustomPipe
19
+ Depth-dependent borehole configuration and pipe properties.
20
+ soil_props : SoilProperties
21
+ Depth-dependent soil properties (thermal conductivity, temperature profile).
22
+ sim_params : SimulationParameters
23
+ Simulation parameters including time, flow, and input power or temperature.
24
+
25
+ Notes
26
+ -----
27
+ The simulation can be run for power and calls the appropriate function.
28
+ `runsimulation_power` is called for power simulations.
29
+
30
+ References
31
+ ----------
32
+ .. [#Cimmino2022] Cimmino, M., & Cook, J.C. (2022). pygfunction 2.2: New features and improvements in accuracy and computational efficiency.
33
+ In Research Conference Proceedings, IGSHPA Annual Conference 2022 (pp. 45-52).
34
+ International Ground Source Heat Pump Association. DOI: https://doi.org/10.22488/okstate.22.00001
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ custom_pipe: CustomPipe,
40
+ soil_props: SoilProperties,
41
+ sim_params: SimulationParameters,
42
+ ):
43
+ """
44
+ Constructor for the PYG_ana class.
45
+
46
+ Parameters
47
+ ----------
48
+ custom_pipe : CustomPipe
49
+ CustomPipe object defining borehole and pipe properties.
50
+ soil_props : SoilProperties
51
+ SoilProperties object defining soil thermal properties.
52
+ sim_params : SimulationParameters
53
+ SimulationParameters object defining time step, flow rate, and load.
54
+ """
55
+ self.custom_pipe = custom_pipe
56
+ self.soil_props = soil_props
57
+ self.sim_params = sim_params
58
+
59
+ def runsimulation(self):
60
+ """
61
+ Main entry point to run the simulation.
62
+ """
63
+ if self.sim_params.run_type == SimulationParameters.POWER:
64
+ return self.runsimulation_power()
65
+ else:
66
+ print("run_type 'TIN' not supported for pyg_ana model type")
67
+
68
+ def runsimulation_power(self):
69
+ """
70
+ Run the simulation of the borehole to ground heat exchanger for an input heat demand.
71
+
72
+ Returns
73
+ -------
74
+ tuple
75
+ hours, Q_b, flowrate, qsign, T_fi, T_fo, T_bave, z, zseg, T_b, T_ftimes, -qbzseg, h_fpipes
76
+ """
77
+ sim_params = self.sim_params
78
+ custom_pipe = self.custom_pipe
79
+ soil_props = self.soil_props
80
+
81
+ # Extract fluid properties from custom_pipe object
82
+ cp_f = custom_pipe.cp_f
83
+
84
+ # Convert the custompipe to the pyg singleutube design with adopted parameters
85
+ if isinstance(custom_pipe, CoaxialPipe):
86
+ coaxial = custom_pipe.create_coaxial()
87
+ custom_pipe = coaxial
88
+ elif isinstance(custom_pipe, CustomPipe):
89
+ multiUTube = custom_pipe.create_multi_u_tube()
90
+ custom_pipe = multiUTube
91
+
92
+ # only one segment simulated because no depth-variation in pyg
93
+ sim_params.nsegments = 1
94
+ nsegments = sim_params.nsegments
95
+
96
+ # Load aggregation scheme
97
+ LoadAgg = gt.load_aggregation.ClaessonJaved(sim_params.dt, sim_params.tmax)
98
+
99
+ # g-function set-up
100
+ H = custom_pipe.b.H
101
+ D = custom_pipe.b.D
102
+ Nt = sim_params.Nt
103
+ time = sim_params.time
104
+ m_flow = sim_params.m_flow[0]
105
+ scaleflow = sim_params.m_flow / m_flow
106
+
107
+ dz = H / nsegments
108
+ zmin = D + 0.5 * dz
109
+ zmax = D + H - 0.5 * dz
110
+ zseg = np.linspace(zmin, zmax, nsegments)
111
+ zz = np.linspace(D, D + H, nsegments + 1)
112
+
113
+ k_s = soil_props.get_k_s(zz[0:-1], zz[1:], sim_params.isample)
114
+
115
+ # The field contains only one borehole, but needs one extra at very large distance to be correct, ie. gfunc plateaus at 6.7)
116
+ boreField = [
117
+ gt.boreholes.Borehole(
118
+ custom_pipe.b.H,
119
+ custom_pipe.b.D,
120
+ custom_pipe.b.r_b,
121
+ x=0.0,
122
+ y=0.0,
123
+ tilt=np.radians(0.1),
124
+ ),
125
+ gt.boreholes.Borehole(
126
+ custom_pipe.b.H,
127
+ custom_pipe.b.D,
128
+ custom_pipe.b.r_b,
129
+ x=1000.0,
130
+ y=0.0,
131
+ tilt=np.radians(0.1),
132
+ ),
133
+ ]
134
+
135
+ # Get time values needed for g-function evaluation
136
+ time_req = LoadAgg.get_times_for_simulation()
137
+
138
+ # Calculate g-function
139
+ # g-Function calculation options
140
+ options = {"nSegments": 8, "disp": False}
141
+ np.seterr(under="ignore")
142
+
143
+ alpha = soil_props.alfa
144
+ gFunc = gt.gfunction.gFunction(
145
+ boreField, alpha, time=time_req, options=options, method="similarities"
146
+ )
147
+
148
+ # Initialize load aggregation scheme
149
+ LoadAgg.initialize(gFunc.gFunc / (2 * pi * k_s))
150
+
151
+ Qabsmin = H * 0.1 # assume at least 0.1 W /m to avoid division by zero
152
+
153
+ # Delta temperatures are calculated at the segments
154
+ deltaT_b = np.zeros(Nt)
155
+ deltaT_bk = np.zeros((Nt, nsegments))
156
+ power = np.zeros(Nt)
157
+ Q_b = sim_params.Q
158
+
159
+ for i, (t, Q_b_i) in enumerate(zip(time, Q_b)):
160
+ # Increment time step by (1)
161
+ LoadAgg.next_time_step(t)
162
+
163
+ # avoid the Q_B_i to be zero, it is at least the 10% of H in watts
164
+ if abs(Q_b_i) < Qabsmin:
165
+ Q_b_i = Qabsmin * np.sign(Q_b_i)
166
+
167
+ # Apply current load
168
+ LoadAgg.set_current_load(Q_b_i / H)
169
+
170
+ deltaT_bk[i] = LoadAgg.temporal_superposition()
171
+ deltaT_b[i] = LoadAgg.temporal_superposition()
172
+
173
+ power[i] = Q_b_i
174
+
175
+ ntlow = Nt
176
+ T_b_top = np.zeros(ntlow)
177
+ T_bave = np.zeros(ntlow)
178
+ T_fi = np.zeros(ntlow)
179
+ T_fo = np.zeros(ntlow)
180
+
181
+ Q_b = np.zeros(ntlow)
182
+ deltaT_b_ref = np.zeros(ntlow)
183
+ flowrate = np.zeros(ntlow)
184
+ Rs = np.zeros(ntlow)
185
+ qsign = np.zeros(ntlow)
186
+
187
+ imax = -1
188
+ for i, t in enumerate(time):
189
+ if (i > imax) and (i < ntlow):
190
+ p = power[i]
191
+ Rs[i] = deltaT_b[i] / (p / H)
192
+
193
+ minR = 0.01
194
+ Rs = np.where(np.logical_and(Rs < minR, Rs > -0.5), minR, Rs)
195
+
196
+ iter = 0
197
+ niter = 3
198
+ # Rsz not filled because not used in pyg-function
199
+ qbzseg = np.zeros((len(Rs), nsegments))
200
+ T_b = np.zeros((len(Rs), nsegments))
201
+ h_fpipes = np.zeros(
202
+ (len(Rs), (custom_pipe.nInlets + custom_pipe.nOutlets) * custom_pipe.nPipes)
203
+ )
204
+ # nPipes is defined differently for standard pyg and the analytical model; in pyg nPipes defines the number of utubes,
205
+ # in the analytical model nPipes defines the nr of pipes
206
+ nz = 20
207
+ z = np.linspace(0.0, H, num=nz)
208
+ T_ftimes = np.zeros(
209
+ (
210
+ len(Rs),
211
+ nz,
212
+ (custom_pipe.nInlets + custom_pipe.nOutlets) * custom_pipe.nPipes,
213
+ )
214
+ )
215
+ T_g = soil_props.getTg(zseg)
216
+
217
+ while iter < niter:
218
+ iter += 1
219
+ imax = -1
220
+
221
+ for i, t in enumerate(time):
222
+ if (i > imax) and (i < ntlow):
223
+ # print(il)
224
+ p = power[i]
225
+
226
+ h_f = custom_pipe.h_f
227
+
228
+ T_b[i] = np.asarray(T_g - deltaT_b[i])
229
+
230
+ T_f_in = custom_pipe.get_inlet_temperature(
231
+ p, T_b[i], scaleflow[i] * m_flow, cp_f
232
+ )
233
+
234
+ T_f_out = custom_pipe.get_outlet_temperature(
235
+ T_f_in, T_b[i], scaleflow[i] * m_flow, cp_f
236
+ )
237
+
238
+ # To compare between depth variation between pyg and ana; Evaluate temperatures fro pyg at nz evenly spaced depths along the borehole
239
+ # at the (it+1)-th time step
240
+ T_f = custom_pipe.get_temperature(
241
+ z, T_f_in, T_b[i], scaleflow[i] * m_flow, cp_f
242
+ )
243
+
244
+ qbz = custom_pipe.get_total_heat_extraction_rate(
245
+ T_f_in, T_b[i], scaleflow[i] * m_flow, cp_f
246
+ )
247
+
248
+ Q_b[i] = power[i]
249
+ T_b_top[i] = T_b[i][0]
250
+ deltaT_b_ref[i] = deltaT_b[i]
251
+
252
+ T_bave[i] = np.average(T_b[i, :])
253
+
254
+ T_fi[i] = T_f_in
255
+ T_fo[i] = T_f_out
256
+ h_fpipes[i] = h_f
257
+ flowrate[i] = scaleflow[i] * m_flow
258
+ T_ftimes[i] = T_f # stored index time, depth, pipe
259
+ # zseg 1 value less than z
260
+ qbzseg[i] = qbz / custom_pipe.b.H
261
+ qsign[i] = np.sign(qbz)
262
+ imax = i
263
+
264
+ hours = time / 3600.0
265
+
266
+ return (
267
+ hours,
268
+ Q_b,
269
+ flowrate,
270
+ qsign,
271
+ T_fi,
272
+ T_fo,
273
+ T_bave,
274
+ z,
275
+ zseg,
276
+ T_b,
277
+ T_ftimes,
278
+ -qbzseg,
279
+ h_fpipes,
280
+ )