To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

Commit ca5b2c69 authored by sfritschi's avatar sfritschi
Browse files

Minor changes

parent 05beed26
#!/bin/bash
# Target Euler IV nodes (36 cores)
bsub -n 36 -W 06:00 -R fullnode -R "rusage[mem=2048]" python3 generate.py 36 16 16 16 y
......@@ -131,6 +131,7 @@ class CellList:
self.throatR = throatR
throatTotal = 0
# Iterate over all pores and place them in respective cell
for i, pore in enumerate(pores):
cellIdx = self.pore_to_index(pore)
self.poresSorted[cellIdx].add(pore)
......@@ -243,30 +244,15 @@ class CellList:
base - widthx + widthy + 1, base - widthx - widthy,
base - widthx - widthy - 1, base - widthx - widthy + 1]
# Add index of pores part of throat to respective set of connections
def update_connection(self, poreIdx: int, nborIdx: int):
self.connPores[poreIdx].add(nborIdx)
self.connPores[nborIdx].add(poreIdx)
def nbor_cell_counts(self, neighborhood: List[int]) -> List[int]:
return [len(self.poresSorted[i]) for i in neighborhood]
def cell_stats(self):
cellCounts = [len(self.poresSorted[i]) for i in range(self.totalCells)]
n = sum(cellCounts)
max_ = max(cellCounts)
min_ = min(cellCounts)
avg = n / self.totalCells
return (max_, min_, avg)
# Remove pore from sorted pores
def expel(self, pore: Pore):
poreCellIdx = self.pore_to_index(pore)
self.poresSorted[poreCellIdx].remove(pore)
def is_contained(self, pore: Pore) -> bool:
poreCellIdx = self.pore_to_index(pore)
return pore in self.poresSorted[poreCellIdx]
def distance(p1: List[float], p2: List[float]):
"""Distance between points p1 and p2."""
......@@ -538,98 +524,7 @@ def generate_imperial(basenet: Network, targetsize: List[float], \
pore2=pores[throat[1]], label=throat[2], r=throat[3])
return network
def cell_list_scaling(basenet: Network, targetsize: List[int], \
cutoff: float = float('inf'), sd: int = None, mute: bool = False):
from random import seed, random, randint
from itertools import product
seed(sd)
d = len(targetsize) # number of spatial dimensions
# check target size multiplicator (must be >= 1)
if any([i < 1 for i in targetsize]):
raise NameError('targetsize must be >= 1!')
# size of new network
L = list(map(lambda lb,ub,i: (ub-lb)*i, basenet.lb, basenet.ub, targetsize))
# check target size (must be > basenet.Lmax)
if any([Lc < basenet.Lmax for Lc in L]):
raise NameError('targetsize leads network < Lmax of basenet!')
# pores, distributed based on dendrogram of basenet
if (not mute): print("distributing pores...", end="", flush=True)
# make indexable and discard in-/outflow pores
basepores = [pore for pore in basenet.pores \
if ((pore.label[:len(LABELS[1])] != LABELS[1]) \
and (pore.label[:len(LABELS[2])] != LABELS[2]))]
# dendrogram-based or uniform pore distribution
pores = []
if (cutoff != cutoff): # uniform
print("\b"*21 + "uniform pore distribution")
# loop over network sections & add uniformly distributed pores drawn from basenet
for s in product(*[range(k) for k in targetsize]):
for k, pore in enumerate(basepores):
pos = [random()*l for l in L]
pores.append(Pore(pos=pos, r=pore.r, label=LABELS[0],
throats=pore.throats.copy()))
else: # dendrogram-based
# extract cluster hierarchy from basenet
centroids = [pore.pos for pore in basepores]
from scipy.cluster import hierarchy
clustree = hierarchy.linkage(centroids, method = 'centroid')
# determine cluster centroids and weights
weights = len(basepores)*[1] # pores have weight = 1
for cluster in clustree:
p1 = int(cluster[0]); p2 = int(cluster[1])
w1 = weights[p1]; w2 = weights[p2]
centroids.append(list(map(lambda x1,x2: (w1*x1 + w2*x2)/(w1 + w2),
centroids[p1], centroids[p2])))
weights.append(w1 + w2)
# loop over network sections (twisted basenet copies)
for s in product(*[range(k) for k in targetsize]):
# twist centers of sufficiently small cluster
touched = [False]*len(centroids)
# positions of pores based on rotations of linked cluster-pairs
for k in range(len(centroids)-1,len(basepores)-1,-1):
i = k-len(basepores) # index of cluster in clustree
ctr = centroids[k] # center of rotation
dist = clustree[i,2] # cluster distance
# check if cluster/pore needs to be twisted
if ((dist < cutoff) or touched[k]):
p1 = int(clustree[i,0]); p2 = int(clustree[i,1])
w1 = weights[p1]; w2 = weights[p2]
vec = random_direction(d)
centroids[p1] = \
[ctr[j] + w2*dist/(w1+w2)*vec[j] for j in range(d)]
centroids[p2] = \
[ctr[j] - w1*dist/(w1+w2)*vec[j] for j in range(d)]
touched[p1] = touched[p2] = True
# add section pores in random order to pores of new network
for k, pore in enumerate(basepores):
pos = [c-lb + si*(ub-lb) \
for c,si,lb,ub in zip(centroids[k],s,basenet.lb,basenet.ub)]
pores.insert(randint(0,len(pores)), Pore(pos=pos,
r=pore.r, label=LABELS[0], throats=pore.throats.copy()))
print("\b"*21 + "left {0:d} of {1:d} clusters (incl. {2:d} pores) untouched".\
format(sum([int(not j) for j in touched]), len(touched), len(basepores)))
# flip pores outside back into domain
for pore in pores:
for k in range(d):
if pore.pos[k] < 0:
pore.pos[k] = L[k] + pore.pos[k]
elif pore.pos[k] >= L[k]:
pore.pos[k] = pore.pos[k] - L[k]
# add pore buffer layers for spatial periodicity
n = len(pores)
copies = __add_buffer_layers(pores, L, basenet.Lmax)
# Domain size including periodic buffer layers on all sides
trueDomainSize = [L[i] + 2 * basenet.Lmax for i in range(d)]
# Initialize cell-list; Place each pore in respective cell-set
cellList = CellList(pores, trueDomainSize, basenet.Lmax, basenet.Lmax)
# Print cell stats to console
max_, min_, avg = cellList.cell_stats()
return (max_, min_, avg)
def generate_dendrogram(basenet: Network, targetsize: List[int], \
cutoff: float = float('inf'), sd: int = None, nthreads: int = mp.cpu_count(), \
mute: bool = False) -> Network:
......@@ -807,8 +702,9 @@ def generate_dendrogram(basenet: Network, targetsize: List[int], \
nRemain = len(poresRemain)
if (nRemain > serialThresh):
# Compute loads and displacements for all threads
load = nRemain // nthreads
remainder = nRemain % nthreads
loads = [nRemain // nthreads + (i < remainder) for i in range(nthreads)]
loads = [load + (i < remainder) for i in range(nthreads)]
# Compute inclusive-scan yielding offsets in poresRemain
displs = [0]
......@@ -857,7 +753,6 @@ def generate_dendrogram(basenet: Network, targetsize: List[int], \
if (nbor.label != LABELS[0]):
j, lbl = nbor.label.split(' ',1)
nbor = pores[int(j)]
assert(nborc.originIndex == int(j))
else:
lbl = LABELS[0]
......@@ -915,7 +810,7 @@ def generate_dendrogram(basenet: Network, targetsize: List[int], \
assert(mPore in poresRemain)
"""
# Copy throats back to original pores and update #throats left
# and compute remaining (reduced) pores
# and compute remaining pores
throatCount = 0
nextPores = []
for pore in poresRemain:
......
......@@ -31,7 +31,7 @@ def main():
start = MPI.Wtime()
pA, QA = netflow.solve_flow_periodic(network=net, \
solver=netflow.Solver.PETSC, mu=1e-3, c=1, P2L=-1e3)
solver=netflow.Solver.PETSC, mu=1e-3, c=3, P2L=1e2)
end = MPI.Wtime()
if rank == root:
......@@ -39,7 +39,7 @@ def main():
start = MPI.Wtime()
pB, QB = netflow.solve_flow_periodic(network=net, \
solver=netflow.Solver.CG, mu=1e-3, c=1, P2L=-1e3)
solver=netflow.Solver.CG, mu=1e-3, c=3, P2L=1e2)
end = MPI.Wtime()
if rank == root:
......
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019/Debian) (preloaded format=pdflatex 2021.4.27) 4 DEC 2021 20:30
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019/Debian) (preloaded format=pdflatex 2021.4.27) 4 DEC 2021 23:53
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
......@@ -1089,7 +1089,7 @@ s/type1/public/amsfonts/cm/cmsy7.pfb></usr/share/texlive/texmf-dist/fonts/type1
/public/amsfonts/cm/cmti10.pfb></usr/share/texlive/texmf-dist/fonts/type1/publi
c/amsfonts/cm/cmti9.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfo
nts/cm/cmtt10.pfb>
Output written on thesis.pdf (12 pages, 471472 bytes).
Output written on thesis.pdf (12 pages, 471481 bytes).
PDF statistics:
298 PDF objects out of 1000 (max. 8388607)
251 compressed objects within 3 object streams
......
No preview for this file type
No preview for this file type
......@@ -219,7 +219,7 @@
\begin{equation}
j^* = \argmin_{j \in \mathcal{N}(i)}\Bigr| ||\mathbf{p}_i - \mathbf{p}_j||_2 - L_t \Bigr|
\end{equation}
Where $\mathcal{N}(i)$ is the set of all pores in adjacent cells to pore $i$. Finding $j^*$ for different pores is \emph{embarrassingly parallel} and can therefore be computed by a large number of threads, storing their results in shared memory. Each thread works on an even chunk of the pores located \emph{inside} the domain. Subsequently, these ideal matches are connected by throats, while avoiding \textbf{conflicts} of pores seeking either a neighbor that is already fully-connected or that was previously connected to them. Finally we repeat the two steps from above, now only considering pores that still have throats left in \emph{random} order (for improved \textbf{load balancing}), until \textbf{no more valid matches} can be found. Empirically, this process converges very fast and consistently, for different target sizes of the full domain, after merely 7-8 iterations. The first iteration alone leaves only $\approx 18\%$ of all possible throats left, see Table ~\ref{table:iter}. Final iterations may be performed serially if only few pores remain as to avoid costly spawning of threads without speed gain. The procedure is summarized as pseudo-code in Algorithm ~\ref{alg:connect}.
Where $\mathcal{N}(i)$ is the set of all pores in adjacent cells to pore $i$. Finding $j^*$ for different pores is \emph{embarrassingly parallel} and can therefore be computed by a large number of threads, storing their results in shared memory. Each thread works on an even chunk of the pores located \emph{inside} the domain. Subsequently, these ideal matches are connected by throats, while avoiding \textbf{conflicts} of pores seeking either a neighbor that is already fully-connected or that was previously connected to them. Finally we repeat the two steps from above, now only considering pores that still have throats left in \emph{random} order (for improved \textbf{load balancing}), until \textbf{no more valid matches} can be found. Empirically, this process converges very fast and consistently, for different target sizes of the full domain, after merely 7-8 iterations. The first iteration alone leaves only $\approx 18\%$ of all possible throats unrealized, see Table ~\ref{table:iter}. Final iterations may be performed serially if only few pores remain as to avoid costly spawning of threads without speed gain. The procedure is summarized as pseudo-code in Algorithm ~\ref{alg:connect}.
\end{multicols}
\begin{table}[b]
......@@ -228,7 +228,7 @@
\caption{Sample run of parallel pore-connecting algorithm using 4 threads. The generated network is 3 times as large as the base network in all directions. The maximal feasible number of throats is 103464, of which 27 were not realized due to there being no possible candidates left for these remaining pores.}
\begin{tabular}{|c|c|c|}
\hline
\textbf{Iteration} & \textbf{Throats left} & \textbf{Rel. percentage} \\
\textbf{Iteration} & \textbf{Throats unrealized} & \textbf{Rel. percentage} \\
\hline
0 & 103464 & 100.0\% \\
1 & 18690 & 18.1\% \\
......@@ -334,7 +334,7 @@
\section{Discussion}
\hspace{0.5cm}In this work we have achieved sizeable performance improvements over the previous, serial implementations from \cite{MEYER2021101592}, especially regarding the generation algorithm. This allows us to compute much larger network realizations of improved quality and simulate the flow through them within a reasonable time-frame of a few hours.
\section{Acknowledgments}
\hspace{0.5cm}The author thankfully acknowledges the various discussions held with supervisor Daniel W. Meyer, who has guided the course of this thesis and offered helpful insight during development of the code.
\hspace{0.5cm}The author thankfully acknowledges the various discussions held with supervisor Daniel W. Meyer, who has guided the course of this thesis and offered valuable insight during development of the code.
\end{multicols}
\newpage
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment