A small, self-contained OpenGeoSys (HEAT_TRANSPORT_BHE) example for the
summer school: a Borehole Thermal Energy Storage field of 37 borehole heat
exchangers in a two-layer ground model, meshed with the ogstools BHE
mesher and driven with the ogstools Project (ogs6py) API.
Create a virtual environment and install the pinned dependencies (so everyone runs the exact versions this example was built and tested with):
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS / Linux: source .venv/bin/activate
pip install -r requirements.txtThe ogs package ships the OpenGeoSys solver itself (no separate install); the
ogs executable lands inside the environment, where 02_create_run_prj.py finds
it automatically. Quick check that everything imports and the solver is present:
python -c "import ogstools, gmsh, pyvista; print(ogstools.__version__)"
ogs --version| BHE field | 37 boreholes, hexagonal grid (3 rings), 5 m spacing |
| Domain | 120 × 120 m, 500 m deep |
| BHEs | 400 m long, coaxial (CXA) |
| Geology | weathered basalt 0…−200 m (MaterialID 0), granodiorite −200…−500 m (MaterialID 1) |
| Initial state | geothermal gradient (10 °C at surface → 25 °C at −500 m) |
| Boundaries | fixed temperature on top & bottom, insulated sides |
| Operation | 5 yearly cycles: inject 90 °C for half a year, then extract 30 °C, at 4 L/s per BHE |
Run them in order with the project's Python environment (.venv):
python 01_create_mesh.py # ogstools BHE mesher -> model/{domain,top,bottom}.vtu
python 02_create_run_prj.py # ogstools -> model/btes_model.prj, then runs OGS
python 03_visualize_results.py # reads model/results/btes.pvd -> figures-
01_create_mesh.py— builds the mesh withgen_bhe_mesh. The bulkdomain.vtuholds both the soil prisms (MaterialIDs 0, 1) and the 37 BHE line elements (MaterialIDs 2…38);top.vtu/bottom.vtuare the boundary meshes for the surface and base temperature BCs. The sides are insulated (the natural zero-flux BC), so nosidesmesh is needed. Also writesmodel/mesh_overview.png, a rendered preview of the mesh (MaterialIDs on a vertical clip + the BHE lines) for a quick visual check. -
02_create_run_prj.py— assembles the project file with theot.Project(ogs6py) API and runs OGS. The coaxial (CXA) BHEs are driven by inlet temperature (InflowTemperature) on a seasonal curve repeated for 5 years. The adaptive (iteration-number-based) time stepper has a 5-day cap and is forced — viafixed_output_times— to land exactly on every inject↔extract switch. Output is written every time step tomodel/results/. -
03_visualize_results.py— reads thebtes.pvdtime series and produces six figures: vertical & horizontal temperature sections (colour scale capped at the 90 °C injection temperature), soil temperature vs. time at several radii, BHE inlet vs. outlet temperature, the storage heat rate, and the cumulative energy injected / extracted. The BHE outlet temperature is read as component 1 oftemperature_BHE{i}at each borehole's top node.
All outputs go to the model/ sub-folder (created on first run).
- Mesh (top of
01_create_mesh.py):bhe_spacing,n_rings,field_center, domainwidth/length/depth,bhe_length,bhe_radius, and the layer thicknesses. SetSHOW_MESH = Trueto inspect the mesh. - Physics & run (top of
02_create_run_prj.py): soil thermal properties (WEATHERED_BASALT,GRANODIORITE), coaxial pipe geometry, grout, refrigerant,BHE_FLOW_RATE, the inject/extract temperatures, the geothermal gradient,N_YEARS, and the time-stepping bounds (MAX_DT, …). The material/operational values shipped here are placeholders for the demo — replace them with your own data. - Visualization (top of
03_visualize_results.py):RADII, the colour scaleT_SCALE_MIN/T_SCALE_MAX. The model constants near the top (BHE_LENGTH,N_BHE,BHE_FLOW_RATE, refrigerant properties) must match02_create_run_prj.py— they are used for the heat-rate/energy evaluation.
- The BHE length is set in the project file (
<borehole><length>) and must equal the meshed BHE length (BHE_LENGTH==bhe_length= 400 m). 02writes the current OGS 6.5.x form of the BHE blocks (inlet-temperature controltype = InflowTemperaturewith the inflow as a time-curve parameter; coaxial<pipes>with<outer>/<inner>, nodistance_between_pipes). The high-level ogstools BHE helper targets an older schema, so the BHE blocks are written with the genericadd_element/add_blockmethods instead.- Runtime. The full 5-year run takes ~2.5 h (401 time steps, output every
step → ~400 VTUs, ~1–2 GB) on a typical laptop. This is too long to run live
in a workshop — use the pre-computed results in
model/results/and run only03_visualize_results.py. For a quick hands-on run, lowerN_YEARS(e.g. 1) or coarsen the mesh in01(largerinner_mesh_size/outer_mesh_size, fewern_rings); the script structure is unchanged.