Проектирование сложных зависимостей структуры данных
Я занимаюсь разработкой библиотеки конечных элементов. Для данной задачи используемая конечная ячейка может иметь элементы разного размера (например, тетраэдры и треугольники), а также возможность комбинирования различных элементов одного и того же размера (например, тетраэдры и гексаэдры). Поэтому мне нужна структура данных, в которой хранится информация о конечных элементах. Самая фундаментальная информация - это связь элементов (идентификаторы node, определяющие элемент). Например, мне нужно каким-то образом сохранить, что треугольный элемент 4 подключен к узлам 5, 6 и 10.
Моя первая попытка состояла в том, чтобы создать список, индекс которого является размером (0,1,2 или 3) и хранит словари. Эти словари имеют строковые ключи (идентификаторы), а значения - массивы numpy (каждая строка представляет собой элементную связность). Мне нужно сделать это, потому что массивы numpy для данного измерения имеют разные формы в зависимости от строковых идентификаторов.
Это класс:
import os
from collections import OrderedDict
import numpy.ma as ma
flatten = lambda l: [item for sublist in l for item in sublist]
class ElementData(list):
def __init__(self, *args, **kwargs):
self.reset()
super(ElementData, self).__init__(*args, **kwargs)
def __iter__(self):
for k, v in self[self.idx].items():
for i, e in enumerate(v):
yield (k,i,e) if not ma.is_masked(e) else (k,i, None)
self.reset()
def __call__(self, idx):
self.idx = idx-1
return self
def __getitem__(self, index):
if index >= len(self):
self.expand(index)
return super(ElementData, self).__getitem__(index)
def __setitem__(self, index, value):
if index >= len(self):
self.expand(index)
list.__setitem__(self, index, value)
def __str__(self):
return "Element dimensions present: {}\n".format([i for i in range(len(self)) if self[i]]) + super(ElementData, self).__str__()
def keys(self):
return flatten([list(self[i].keys()) for i in range(len(self))])
def reset(self):
self.idx = -1
self.d = -1
def expand(self, index):
self.d = max(index, self.d)
for i in range(index + 1 - len(self)):
self.append(OrderedDict())
def strip(self, value=None):
if not callable(value):
saved_value, value = value, lambda k,v: saved_value
return ElementData([OrderedDict({k:value(k, v) for k,v in i.items()}) for i in super(ElementData, self).__iter__()])
def numElements(self, d):
def elementsOfDimension(d):
# loop over etypes
nelems = 0
for v in self[d].values():
nelems += v.shape[0] if not isinstance(v, ma.MaskedArray) else v.shape[0] - v.mask.any(axis=1).sum()
return nelems
# compute the number of all elements
if d == -1:
nelems = 0
for i in range(self.d+1):
nelems += elementsOfDimension(i)
return nelems
else: # of specific dimension only
return elementsOfDimension(d)
Класс работает хорошо, и он позволяет мне легко перемещаться по всем элементам определенного измерения. Однако есть и другие данные, связанные с каждым элементом, который хранится отдельно, например, его материал. Поэтому я решил использовать ту же структуру данных, что и другие свойства. С этой целью я использую функцию strip
для класса, чтобы вернуть мне всю структуру без массивов numpy.
Проблема в том, что исходная структура данных является динамической, и если я ее изменю, я должен изменить любую другую структуру, которая зависит от нее. Я действительно думаю, что я пошел в неправильном направлении при разработке этого класса. Может быть, есть более простой способ подойти к этой проблеме? Я думал о сохранении дополнительной информации рядом с массивами numpy (например, как кортежи), но я не знаю, хорошо это или нет. Выбор, сделанный при разработке программного обеспечения, может действительно сделать нашу жизнь несчастной позже, и я начинаю понимать это сейчас.
UPDATE
Используя вышеприведенный класс, один пример может быть следующим:
Element dimensions present: [0, 1, 2]
[OrderedDict([('n1', array([[0],
[1],
[3]]))]), OrderedDict([('l2', array([[1, 2]]))]), OrderedDict([('q4', array([[0, 1, 5, 4],
[5, 1, 2, 6],
[6, 2, 3, 7],
[7, 3, 0, 4],
[4, 5, 6, 7]]))])]
где структура данных использовалась для хранения элементов размером 0 (node), 1 (строка) и 2 (четырехугольника).
Ответы
Ответ 1
Комментарий: дизайн идет вразрез с логической структурой программы.
Я использовал данный пример данных Element и не ожидал получить всю картину сразу.
Комментарий: каждый элемент имеет уникальное измерение (треугольник всегда имеет размерность 2, поскольку тетраэдр всегда имеет размерность 3 и a node размерность 0).
Извините, я неверно истолковал вопрос "... элементы разных измерений..." с
"Элемент имеет различные размеры". Я адаптировал свое предложение.
Комментарий:... проблема возникает, когда я изменяю структуру данных (например, путем добавления элементов)
Пока вы не показываете пример данных для этой проблемы,
Я не могу думать над решением.
Мое предложение:
class Dimension(object):
"""
Base class for all Dimensions
Dimension has no knowledge to what the data belongs to
"""
def __init__(self, data=None):
pass
class Element(object):
"""
Base class for all Elements
Hold on class Dimension(object):
"""
def __init__(self, dimension=None):
pass
class Triangle(Element):
def __init__(self, dimension):
super().__init__(dimension=dimension)
class Tetrahedron(Element):
def __init__(self, dimension):
super().__init__(dimension=dimension)
class Node(Element):
def __init__(self, dimension):
super().__init__(dimension=dimension)
Использование: создание вашего примера Элемент
node = Node(dimension=[0,1,3])
line = Triangle(dimension=[0,2])
quad = Tetrahedron(dimension=[[0, 1, 5, 4], [5, 1, 2, 6], [6, 2, 3, 7], [7, 3, 0, 4], [4, 5, 6, 7]])
Новый элемент будущего, только для отображения class Element
может быть расширен:
# New future dimensions - No changes to class Element() required
class FutureNodTriTet(Element):
def __init__(self, dimension):
super().__init__(dimension=dimension)
future_NTT = FutureNodTriTet(dimension=[0, (1,3), (4,5,6)])
Прокомментируйте, если это ближе к вашим потребностям.
Ответ 2
Когда указано "Сложные зависимости структуры данных", вы, вероятно, можете говорить о "графе", вы дважды проверяли, существует ли какая-либо существующая теория графиков соответствуют вашим потребностям? (например, "Проблемы с маршрутом" )