Skip to content

Commit

Permalink
Use graphs to model the topology of the network (#22)
Browse files Browse the repository at this point in the history
* Create new network types

* Update plot_borefield

* Modify equations for graph implementation

* Add reverse graph

* Update examples

* Fix docs

* Update docs

* Fix docs

* Fix docs
  • Loading branch information
marcbasquensmunoz authored Oct 24, 2024
1 parent c55fe09 commit b8fc1fd
Show file tree
Hide file tree
Showing 33 changed files with 547 additions and 203 deletions.
2 changes: 2 additions & 0 deletions BNSPlots/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ authors = ["Marc Basquens <[email protected]> and contributors"]
version = "1.0.0-DEV"

[deps]
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"

[compat]
Expand Down
1 change: 1 addition & 0 deletions BNSPlots/src/BNSPlots.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module BNSPlots

using WGLMakie
using GraphMakie

include("results.jl")
include("borefield.jl")
Expand Down
43 changes: 15 additions & 28 deletions BNSPlots/src/borefield.jl
Original file line number Diff line number Diff line change
@@ -1,56 +1,43 @@
using Graphs

make_color_range(color_pair, n) = n == 1 ? [color_pair[1]] : range(color_pair[1], stop=color_pair[2], length=n)

"""
plot_borefield(network, positions; distinguished_branches = [], colors = [])
plot_borefield(network, positions; distinguished_boreholes = [])
Makes a plot of the borefield, showing the boreholes numbered and their connections.
# Arguments
- `network`: Network specifying the connections between boreholes.
- `positions`: The positions of each borehole.
# Optional arguments
- `distinguished_branches`: Vector of `Int`. If specified, the branches corresponding to the given values will be highlighted with the colors of `colors`.
- `colors`: Vector of `Tuples` of `Color`. If specified, uses each pair of colors to define a color gradient to color each of the branches of `distinguished_branches`.
- `distinguished_boreholes`: Vector of `Tuple{Int, Color}`. If specified, the boreholes corresponding to the given values will be highlighted with each of the colors provided.
"""
function plot_borefield(network, positions; distinguished_branches = [], colors = [])

function plot_borefield(network, positions; distinguished_boreholes = [])
scene = Figure()
axis = scene[1, 1] = Axis(scene, ylabel = "y [m]", xlabel = "x[m]", aspect = DataAspect())
min_x = minimum(map(x->x[1], positions))
max_x = maximum(map(x->x[1], positions))
min_y = minimum(map(x->x[2], positions))
max_y = maximum(map(x->x[2], positions))

margin = max(max_x-min_x, max_y-min_y) * 0.1

xlims!(axis, min_x-margin, max_x+margin)
ylims!(axis, min_y-margin, max_y+margin)

scatter!(axis, Makie.Point2.(positions), markersize = 15.)

# Draw boreholes
for (i, n_branch) in enumerate(distinguished_branches)
branch = network.branches[n_branch]
branch_colors = make_color_range(colors[i], length(branch))
for (borehole, color) in zip(branch, branch_colors)
point = [ Makie.Point(positions[borehole]) ]
scatter!(axis, point, color = color, markersize = 24)
end
end

# Write labels with borehole numbers
for i in eachindex(1:length(positions))
text!(axis, "$i", fontsize = 9, position = (positions[i] .+ (0.7 , -0.7)) , color = :blue)
end
borefield = SimpleGraph(network.graph)
rem_vertex!(borefield, nv(borefield))
rem_vertex!(borefield, nv(borefield))

# Draw lines representing connections
for branch in network.branches
for k in 2:length(branch)
s = Makie.Point.([ positions[branch][k-1], positions[branch][k] ] )
linesegments!(axis, s, color = :red, linewidth = 1.5)
end
Nb = nv(borefield)
borehole_colors = [colorant"black" for i in 1:Nb]
borehole_sizes = 12 * ones(Int, Nb)
for (bh, color) in distinguished_boreholes
borehole_colors[bh] = color
borehole_sizes[bh] = 16
end
graphplot!(axis, borefield, layout=positions, nlabels=["$i" for i in 1:Nb], node_color = borehole_colors, node_size = borehole_sizes, nlabels_distance = 5.)

hidespines!(axis)

Expand Down
27 changes: 15 additions & 12 deletions BNSPlots/src/monitor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ end
Creates a plot of the result of the simulation.
# Arguments
- `containers`: The containers (`SimulationContainers`) containing the result of the simulation through `simulate!`.
- `branch`: A vector containing the IDs of the boreholes whose data will be displayed.
- `boreholes`: A vector containing the IDs of the boreholes whose data will be displayed.
- `t`: The times at which the data corresponds. It should normally be `options.t`.
# Optional arguments
- `steps`: Index of the steps to display in the plot.
- `display`: A vector describing which plots that will be generated. If `:Tfin`, `:Tfout` or `:Tb` are specified, a temperature plot will be created showing the inlet fluid temperature,
the outlet fluid temperature, and the borehole wall temperature, respectively. If `:q` is specified, a separate power plot will be created shwoing the heat extracted per meter.
- `Δt`: The scale of the x-axis in the plot. Possible options: `:year`, `:month`, `:hour`.
- `color_pair`: A pair of colors used as extrema to generate a range of colors for each borehole.
- `colors`: A list of colors used for each borehole. If not specified, the colors used will be between colorant"navajowhite2" and colorant"darkgreen".
"""
function monitor(containers, branch, t; steps = 1:length(t), display = [:Tfin, :Tfout, :Tb, :q, :mf], Δt = :year, color_pair = (colorant"navajowhite2", colorant"darkgreen"))
function monitor(containers, boreholes, t; steps = 1:length(t), display = [:Tfin, :Tfout, :Tb, :q, :mf], Δt = :year, colors = [])
if isempty(display)
return
end
Expand All @@ -36,8 +36,12 @@ function monitor(containers, branch, t; steps = 1:length(t), display = [:Tfin, :
grid = scene[1, 1] = GridLayout()
axes = []

color_range = make_color_range(color_pair, length(branch))

if !isempty(colors)
color_range = colors
else
color_range = make_color_range((colorant"navajowhite2", colorant"darkgreen"), length(boreholes))
end

if anyin([:Tfin, :Tfout, :Tb], display)
axis_T = Axis(grid[length(axes)+1, 1], ylabel = L"T \, \left[ °C \right]")
push!(axes, axis_T)
Expand All @@ -51,11 +55,11 @@ function monitor(containers, branch, t; steps = 1:length(t), display = [:Tfin, :
push!(axes, axis_m)
end

Tfin = get_Tfin(containers, branch)
Tfout = get_Tfout(containers, branch)
Tb = get_Tb(containers, branch)
q = get_q(containers, branch)
mf = get_mf(containers, branch)
Tfin = get_Tfin(containers, boreholes)
Tfout = get_Tfout(containers, boreholes)
Tb = get_Tb(containers, boreholes)
q = get_q(containers, boreholes)
mf = get_mf(containers, boreholes)

secs_in_year = 8760*3600
conversion = Dict(:year => 1, :month => 12, :hour => 8760)
Expand All @@ -79,10 +83,9 @@ function monitor(containers, branch, t; steps = 1:length(t), display = [:Tfin, :
end
end


group_color = [PolyElement(color = color, strokecolor = :transparent) for color in color_range]
group_marker = [LineElement(color = :black), LineElement(color = :black, linestyle = :dash), MarkerElement(marker = :circle, color = :black, strokecolor = :transparent, markersize = 5.)]
legend = Legend(scene, [group_color, group_marker], [string.(branch), ["Tfin", "Tfout", "Tb"]], ["Boreholes", "Temperatures"], tellheight = true, tellwidth = false)
legend = Legend(scene, [group_color, group_marker], [string.(boreholes), ["Tfin", "Tfout", "Tb"]], ["Boreholes", "Temperatures"], tellheight = true, tellwidth = false)
legend.titleposition = :top
legend.orientation = :horizontal
legend.nbanks = 1
Expand Down
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ExponentialUtilities = "d4d017d3-3776-5f7e-afef-a10c40355c18"
FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838"
FiniteLineSource = "64cfb1b1-06f0-48af-b80e-43c410dfe9e8"
GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
LegendrePolynomials = "3db4a2ba-fc88-11e8-3e01-49c72059a882"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Expand Down
49 changes: 48 additions & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,65 @@ See [Basic tutorial](@ref) for more details.
BoreholeNetwork
```

Some relevant network related functions:
```@docs
boreholes_in_branch
```

```@docs
first_bhs_in_branch
```

```@docs
source
```
```@docs
sink
```
```@docs
connect!
```
```@docs
connect_to_source!
```
```@docs
connect_to_sink!
```
```@docs
connect_in_series!
```
```@docs
connect_in_parallel!
```

```@docs
BoreholeOperation
```

```@docs
Valve
```

Valve creation:
```@docs
valve
```
```@docs
equal_valve
```
```@docs
absolute_valve
```


```@docs
Operator
```

### Prewritten operator strategies

```@docs
SimpleOperator
ConstantOperator
```

## Simulation Options
Expand Down
7 changes: 4 additions & 3 deletions docs/src/nonhistory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ using BoreholeNetworksSimulator
#

Δt = 3600.
Nt = 2#8760
Nt = 8760

medium = GroundMedium=1e-6, λ=3., T0=10.)
borehole = SingleUPipeBorehole(H=100., D=10.)
Expand All @@ -24,8 +24,9 @@ borefield = EqualBoreholesBorefield(borehole_prototype=borehole, positions=posit
constraint = constant_HeatLoadConstraint(5 .* ones(BoreholeNetworksSimulator.n_boreholes(borefield)), Nt)
fluid = Water()

configurations = [BoreholeNetwork([[1], [2]])]
operator = SimpleOperator(mass_flow = 2., branches = 2)
network = all_parallel_network(2)
configurations = [network]
operator = ConstantOperator(network, mass_flows = 2 * ones(2))

# Now, we define two different options using different `method` parameters,
# one with `ConvolutionMethod` corresponding to the convolution,
Expand Down
13 changes: 7 additions & 6 deletions docs/src/tutorial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,16 @@ borefield = EqualBoreholesBorefield(borehole_prototype=borehole, positions=posit
# In our example, we want to simulate two independent boreholes, so each of them must be in a separate branch.
# Also, for the moment, we are only interested in this configuration, so we define:

configurations = [BoreholeNetwork([[1], [2]])]
network = all_parallel_network(2)

# Even with all these specifications, the evolution of the system is still not fully determined.
# The missing conditions are referred to as constraints, and are modeled by subtypes of [`Constraint`](@ref).
# For instance, if we would like the two boreholes to be connected in parallel, we would still need to
# impose that their inlet temperatures be equal. In our example, since we want out boreholes to be independent,
# we will impose the total amount of heat that we want to extract from each borehole. We will impose a constant
# load, equal for both boreholes. This is specified by
q1 = 5.
q2 = 5.
q1 = H
q2 = H
constraint = constant_HeatLoadConstraint([q1, q2], Nt)

# We can finally create the object with all the options:
Expand All @@ -94,7 +94,7 @@ options = SimulationOptions(
fluid = Water(),
Δt = Δt,
Nt = Nt,
configurations = configurations
configurations = [network]
)

# As we have mentioned, the simulation is designed to allow for a controllable opeartion during its duration.
Expand All @@ -105,9 +105,10 @@ options = SimulationOptions(
# configuration will be used for the next time step. In our case, we only want a static, simple configuration.
# The second specifies the mass flow rate through each branch of the selected configuration, provided as
# a vector. In our example, we will keep this constant through the simulation.
# For this purpose, there exists the type [`SimpleOperator`](@ref), that implements precisely this strategy.
# For this purpose, there exists the type [`ConstantOperator`](@ref), that implements precisely this strategy.

operator = SimpleOperator(mass_flow = 2., branches = 2)
# TODO: Update tutorial with new operation and network
operator = ConstantOperator(network, mass_flows = 2 * ones(2))

# Before simulating, we first need to call [`initialize`](@ref) to run some precomputations
# that will be used throught the simulation and to instantiate containers where the result will be written.
Expand Down
31 changes: 16 additions & 15 deletions examples/Braedstrup/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ borehole_locations = "$(@__DIR__)/data/Braedstrup_borehole_coordinates.txt"
Δt = 8760*3600/12.
Nt = 120

network = BoreholeNetwork([
[35, 36, 29, 37, 30, 22],
[43, 48, 42, 41, 40, 34],
[47, 46, 45, 39, 32, 33],
[44, 38, 31, 24, 25, 26],
[18, 12, 11, 17, 16, 23],
[19, 13, 7, 6, 5, 10],
[20, 14, 8, 3, 2, 1],
[27, 28, 21, 15, 9, 4]
])
network = BoreholeNetwork(48)
connect_to_source!(network, [35, 43, 47, 44, 18, 19, 20, 27])
connect_in_series!(network, [35, 36, 29, 37, 30, 22])
connect_in_series!(network, [43, 48, 42, 41, 40, 34])
connect_in_series!(network, [47, 46, 45, 39, 32, 33])
connect_in_series!(network, [44, 38, 31, 24, 25, 26])
connect_in_series!(network, [18, 12, 11, 17, 16, 23])
connect_in_series!(network, [19, 13, 7, 6, 5, 10])
connect_in_series!(network, [20, 14, 8, 3, 2, 1])
connect_in_series!(network, [27, 28, 21, 15, 9, 4])
connect_to_sink!(network, [22, 34, 33, 26, 23, 10, 1, 4])

configurations = [
network, # Heat extraction
reverse(network) # Heat injection
Expand Down Expand Up @@ -60,7 +62,7 @@ end

function BoreholeNetworksSimulator.operate(operator::SeasonalOperator, i, options, X)
active_network = options.configurations[operator.seasonal_configuration[i]]
BoreholeOperation(active_network, operator.mass_flows)
BoreholeOperation(network=active_network, mass_flows=operator.mass_flows)
end

operator = SeasonalOperator(mass_flows=0.5 .* ones(n_branches(network)), seasonal_configuration=[i%12 in 1:6 ? 2 : 1 for i in 1:Nt])
Expand All @@ -74,11 +76,10 @@ containers.X
############
# Draw plots

monitored_branches = [3, 8]
color_ranges = [(colorant"darkorange", colorant"blue"), (colorant"red", colorant"green")]
monitoring = [(27, colorant"darkorange"), (28, colorant"green")]

borefiled_plot = plot_borefield(network, borehole_positions, distinguished_branches = monitored_branches, colors = color_ranges)
branch1 = monitor(containers, network.branches[monitored_branches[1]], options.t, display = [:Tfin], color_pair=color_ranges[1])
borefield_plot = plot_borefield(network, borehole_positions, distinguished_boreholes = monitoring)
branch1 = monitor(containers, boreholes_in_branch(network, first_bh=19), options.t, display = [:Tfin])

# save("examples/Braedstrup/plots/Braedstrup_borefield.png", borefiled_plot)
# save("examples/Braedstrup/plots/branch1.png", branch1)
Expand Down
4 changes: 2 additions & 2 deletions examples/g-function/main.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function make_plot(axis, d)
α = 1e-6
λ = 3.

network = BoreholeNetwork([[i] for i in 1:n*m])
network = all_parallel_network(n*m)
configurations = [network]

function create_rectangular_field(n, m, d)
Expand All @@ -41,7 +41,7 @@ function make_plot(axis, d)
configurations = configurations
)

operator = SimpleOperator(mass_flow = 1., branches = n_branches(network))
operator = ConstantOperator(network, mass_flows = ones(n*m))
containers = @time initialize(options)

@time simulate!(operator=operator, options=options, containers=containers)
Expand Down
6 changes: 3 additions & 3 deletions examples/tekniska/constant_m.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ using BNSPlots

include("defs.jl")

operator = SimpleOperator(mass_flow=0.5, branches=n_branches(network))
operator = ConstantOperator(network, mass_flows=0.5 * ones(10))
containers = @time initialize(options)
@time simulate!(operator=operator, options=options, containers=containers)

t_range = (5*8760-24*7):5*8760
const_m_plot = monitor(containers, [4, 7], options.t, steps = t_range, color_pair = (colorant"darkgreen", colorant"red"))
const_m_plot = monitor(containers, [4, 7], options.t, steps = t_range, colors = [colorant"darkgreen", colorant"red"])
# save("examples/tekniska/plots/const_m.png", const_m_plot)

const_m_plot_5_year = monitor(containers, [4, 7], options.t, color_pair = (colorant"darkgreen", colorant"red"))
const_m_plot_5_year = monitor(containers, [4, 7], options.t, colors = [colorant"darkgreen", colorant"red"])
# save("examples/tekniska/plots/const_m_5_years.png", const_m_plot_5_year)
4 changes: 2 additions & 2 deletions examples/tekniska/defs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ using WGLMakie
Δt = 3600.
Nt = 8760*5

network = BoreholeNetwork([[i] for i in 1:10])
network = all_parallel_network(10)
configurations = [
network
]
Expand Down Expand Up @@ -60,7 +60,7 @@ options = SimulationOptions(
)

group1 = [1, 6, 2, 7, 3, 8]
borefield_fig = plot_borefield(network, borehole_positions, distinguished_branches=collect(1:10), colors=[i in group1 ? (colorant"red", colorant"red") : (colorant"green", colorant"green") for i in 1:10])
borefield_fig = plot_borefield(network, borehole_positions, distinguished_boreholes=[(i, i in group1 ? colorant"red" : colorant"green") for i in 1:10])

function plot_weekly_Q()
fig = Figure(size=(1500, 400))
Expand Down
Loading

0 comments on commit b8fc1fd

Please sign in to comment.