# @Author: Felix Kramer <kramer>
# @Date: 24-02-2022
# @Email: felixuwekramer@proton.me
# @Last modified by: kramer
# @Last modified time: 08-07-2022
import networkx as nx
import numpy as np
from scipy.spatial import Voronoi
import random as rd
from dataclasses import dataclass, field
[docs]def init_graph_from_random(random_type, periods, sidelength):
"""
Initialize a random spatial graph customized by type and size.
Args:
random_type (string):\n
The type of random lattice to be constructed(voronoi_planar,
voronoi_volume).
periods (int):\n
Number of random points.
sidelength (float):\n
The box size into which random points in space are generated.
Returns:
nx.Graph: A random spatial graph.
"""
choose_constructor_option = {
'default': NetworkxVoronoiPlanar,
'voronoi_planar': NetworkxVoronoiPlanar,
'voronoi_volume': NetworkxVoronoiVolume,
}
if random_type in choose_constructor_option:
random = choose_constructor_option[random_type](
periods,
sidelength
)
else:
print('Warning, crystal type unknown, set default: simple')
random = choose_constructor_option['default'](periods, sidelength)
return random.G
[docs]@dataclass
class NetworkxRandom():
"""
A base class for spatial, random networks.
Attributes
----------
num_periods(int):\n
Number of points for internal Voronoi construction.
sidelength (float):\n
Box length for spatial initialization.
G (dictionary):\n
An internal simple graph.
"""
num_periods: int = 0
sidelength: float = 0
G: nx.Graph = field(default_factory=nx.Graph, repr=False, init=False)
[docs] def mirror_boxpoints(self, points, sl):
"""
Periodically mirror points in 2D for voronoi construction.
Args:
points (list):\n
The original list of points (x,y)
sl (float):\n
The sidelength, acting as scale for the transition vector.
Returns:
ndarray: A matrix of all points and their mirrors
"""
points_matrix = points
intervall = [-1, 0, 1]
for i in intervall:
for j in intervall:
if (i != 0 or j != 0):
points_matrix = np.concatenate(
(points_matrix, points+(i*sl, j*sl))
)
return points_matrix
[docs] def mirror_cubepoints(self, points, sl):
"""
Periodically mirror points in 3D for voronoi construction.
Args:
points (list):\n
The original list of points (x,y,z)
sl (float):\n
The sidelength, acting as scale for the transition vector.
Returns:
ndarray: A matrix of all points and their mirrors
"""
points_matrix = points
intervall = [-1, 0, 1]
for i in intervall:
for j in intervall:
for k in intervall:
if (i != 0 or j != 0 or k != 0):
points_matrix = np.concatenate(
(points_matrix, points+(i*sl, j*sl, k*sl))
)
return points_matrix
# construct random 3d graph, confined in a box
[docs] def is_in_box(self, v, sl):
"""
Test whether new random point is inside the constraining boundaries.
Args:
v (list):\n
The point vector to be tested.
sl (float):\n
The sidelength, acting as scale for the transition vector.
Returns:
bool: The boolean value, True if inside the original volume.
"""
answer = True
if (v[0] > sl) or (v[0] < -sl):
answer = False
if (v[1] > sl) or (v[1] < -sl):
answer = False
if (v[2] > sl) or (v[2] < -sl):
answer = False
return answer
[docs]@dataclass
class NetworkxVoronoiPlanar(NetworkxRandom):
"""
A class algorithms to generate spatial, 2D random networks, generated via
voronoi tesselation of a periodic plane.
Attributes
----------
num_periods(int):\n
Number of points for internal Voronoi construction.
sidelength (float):\n
Box length for spatial initialization.
G (dict):\n
An internal simple graph.
"""
def __post_init__(self):
self.random_voronoi_periodic(self.num_periods, self.sidelength)
# construct random 2d graph, confined in a certain spherical boundary,
# connections set via voronoi tesselation
[docs] def construct_voronoi_periodic(self, number, sidelength):
"""
Generate random points in 2D, mirror them periodically and perform a
Voronoi tesselation for a 2D random point set.
Args:
number (int):\n
The number of points to be generated.
sidelength (float):\n
The sidelength of the box which hold points
Returns:
Voronoi: A scipy.spatial.Voronoi output object
"""
V = 0
# create points for voronoi tesselation
XY = []
for i in range(number):
x = rd.uniform(0, sidelength)
y = rd.uniform(0, sidelength)
XY.append((x, y))
self.XY = XY
XY = self.mirror_boxpoints(np.array(XY), sidelength)
self.XY_periodic = XY
V = Voronoi(XY)
return V
[docs] def random_voronoi_periodic(self, number, sidelength):
"""
Build a spatially embedded in 2D, random networkx internally for
given size parameters.
Args:
number (int):\n
The number of points to be generated.
sidelength (float):\n
The sidelength of the box which hold points
"""
# construct a core of reandom points in 2D box for voronoi tesselation,
# mirror the primary box so a total of 9 cells is created with the
# initial as core
V = self.construct_voronoi_periodic(number, sidelength)
# pick up the face of the core which correspond to a periodic voronoi
# lattice
faces = []
for j, i in enumerate(V.point_region):
faces.append(np.asarray(V.regions[i]))
if j == number-1:
break
# use periodic kernel to construct the correponding network
faces = np.asarray(faces)
f = faces[0]
for i in range(len(faces[:])):
if i+1 == len(faces[:]):
break
f = np.concatenate((f, faces[i+1]))
for i in faces:
for j in i:
v = V.vertices[j]
self.G.add_node(j, pos=v, lablel=j)
k = 0
for i in V.ridge_vertices:
mask = np.in1d(i, f)
if np.all(mask):
for j in range(len(i)):
h = len(i)-1
options = {
# 'slope': (V.vertices[i[h-(l+1)]],
# V.vertices[i[h-l]]),
'label': k,
}
self.G.add_edge(i[h-(j+1)], i[h-j], **options)
k += 1
if len(i) == 2:
break
[docs]@dataclass
class NetworkxVoronoiVolume(NetworkxRandom):
"""
A class algorithms to generate spatial, 3D random networks, generated via
voronoi tesselation of a periodic volume.
Attributes
----------
num_periods(int):\n
Number of points for internal Voronoi construction.
sidelength (float):\n
Box length for spatial initialization.
G (dict):\n
An internal simple graph.
"""
def __post_init__(self):
self.random_voronoi_periodic(self.num_periods, self.sidelength)
# construct random 3d graph, confined in a certain spherical boundary,
# connections set via voronoi tesselation
[docs] def construct_voronoi_periodic(self, number, sidelength):
"""
Generate random points in 3D, mirror them periodically and perform a
Voronoi tesselation for a 3D random point set.
Args:
number (int): The number of points to be generated.
sidelength (float): The sidelength of the box which hold points
Returns:
Voronoi: A scipy.spatial.Voronoi output object
"""
V = 0
# create points for voronoi tesselation
XYZ = []
for i in range(number):
x = rd.uniform(0, sidelength)
y = rd.uniform(0, sidelength)
z = rd.uniform(0, sidelength)
XYZ.append((x, y, z))
self.XYZ = XYZ
XYZ = self.mirror_cubepoints(np.array(XYZ), sidelength)
self.XYZ_periodic = XYZ
V = Voronoi(XYZ)
return V
[docs] def random_voronoi_periodic(self, number, sidelength):
"""
Build a spatially embedded in 3D, random networkx internally for given
size parameters.
Args:
number (int): The number of points to be generated.
sidelength (float): The sidelength of the box which hold points
"""
# construct a core of reandom points in 2D box for voronoi tesselation,
# mirror the primary box so a total of 9 cells is created with the
# initial as core
V = self.construct_voronoi_periodic(number, sidelength)
# pick up the face of the core which correspond to a periodic
# voronoi lattice
faces = []
for j, i in enumerate(V.point_region):
faces.append(np.asarray(V.regions[i]))
if j == number-1:
break
# use periodic kernel to construct the correponding network
faces = np.asarray(faces)
f = faces[0]
for i in range(len(faces[:])):
if i+1 == len(faces[:]):
break
f = np.concatenate((f, faces[i+1]))
for i in faces:
for j in i:
v = V.vertices[j]
self.G.add_node(j, pos=v, lablel=j)
k = 0
for i in V.ridge_vertices:
mask = np.in1d(i, f)
if np.all(mask):
for j in range(len(i)):
h = len(i)-1
options = {
# 'slope': (V.vertices[i[h-(l+1)]],
# V.vertices[i[h-l]]),
'label': k
}
self.G.add_edge(i[h-(j+1)], i[h-j], **options)
k += 1
if len(i) == 2:
break