Quickstart
This page shows a minimal end-to-end example: a single borehole with a Single U-tube configuration, one soil layer, and no inter-borehole thermal interference. It is the simplest possible pyCaRM simulation and a good starting point before moving to multi-borehole fields.
Imports
import numpy as np
from pathlib import Path
from carm import (
BoreholeGeometry,
BoreholeMesh,
BoreholeThermalProperties,
SingleUtube,
)
from carm import EnvironmentalProperties, EnvironmentalTimeSeries
from carm import Fluid
from carm import GroundGeometry, GroundMesh
from carm import PhysicalModel
from carm import Simulation
Fluid
Define the thermal and physical properties of the heat carrier fluid:
fluid = Fluid(
k_w=0.5687, # thermal conductivity [W/(m·K)]
rho_w=1000.14, # density [kg/m³]
cp_w=4207.4, # specific heat [J/(kg·K)]
ni_w=1.496e-6, # kinematic viscosity [m²/s]
)
Borehole
Assemble the borehole geometry, mesh, thermal properties, and pipe
configuration into a SingleUtube object:
bore_geom = BoreholeGeometry(Lbore=100, D0=0.15)
bore_mesh = BoreholeMesh(m_mesh=40)
bore_th_props = BoreholeThermalProperties(cp_0=1460, rho_0=1655, k0=1.8)
props_b = SingleUtube(
geom=bore_geom,
mesh=bore_mesh,
thermalprops=bore_th_props,
fluid=fluid,
Rp0=0.25,
RppB=0.72,
pipe_spacing=0.0823,
pipe_thick=0.003,
Dpi=0.026,
n_pipes=2,
)
Ground
Define the ground geometry and radial/axial mesh. stratification is a
list of layers, each specified as (k, rho, cp, permeability):
ground_geom = GroundGeometry(D0=0.15, L=100, L_sup=1, L_inf=10, rn=10)
ground_mesh = GroundMesh(n_mesh=20, m_mesh=40, m_mesh_sup=4, m_mesh_inf=40)
stratification = [(1.8, 947.37, 1900, 111)] # single homogeneous layer
Environmental boundary conditions
Load time-varying surface boundary conditions from an Excel file and define the surface thermal properties:
path = Path("input_env.xlsx")
env_input = EnvironmentalTimeSeries.from_excel(Tm=13, path=path)
env_props = EnvironmentalProperties(
R_ext=0.04,
absorptance=0.7,
eps=0.95,
At=10,
tau=0,
tau_y=365 * 24 * 3600,
tau_shift=210 * 24 * 3600,
)
Physical model
Combine all components into a PhysicalModel. Tg is the undisturbed
ground temperature [°C]:
model = PhysicalModel(
ground_geom=ground_geom,
ground_mesh=ground_mesh,
borehole=props_b,
fluid=fluid,
Tg=13,
stratification=stratification,
)
Simulation
Define the inlet fluid temperature Tf1 and mass flow rate mw_tot
as arrays of shape (n_groups, n_steps). For a single borehole in
parallel (the default), n_groups = 1:
dt = 3600 # timestep [s]
n_steps = 276
Tf1 = np.full((1, n_steps), 2.0) # inlet temperature [°C]
mw_tot = np.full((1, n_steps), 0.1657) # mass flow rate [kg/s]
simulation = Simulation(
model=model,
envinput=env_input,
envprops=env_props,
timesteps=dt,
n_steps=n_steps,
mw_tot=mw_tot,
Tf1=Tf1,
)
T_history = simulation.run()
T_history has shape (n_steps + 1, n_bhes, n_dof), where index 0
along the first axis is the initial condition. See Output for how
to extract temperatures of interest from this array.
Extracting the outlet temperature
As a quick check, extract and plot the outlet fluid temperature over time:
import matplotlib.pyplot as plt
m_mesh_sup = 4
n_mesh = 20
m_mesh = 40
nsup = m_mesh_sup + 1
nground = n_mesh * m_mesh
Tfout = T_history[1:, 0, nsup + nground + (props_b.n_equations - 1)]
time = np.arange(1, n_steps + 1) * dt / 3600 # [h]
plt.plot(time, Tfout)
plt.xlabel("Time [h]")
plt.ylabel("Outlet temperature [°C]")
plt.grid(True, linestyle="--", alpha=0.4)
plt.tight_layout()
plt.show()
Note
For multi-borehole fields in parallel or series, see the examples in
the examples/ directory of the repository.