设计复杂数据结构的依赖

时间:2017-04-01 21:14:27

标签: python design-patterns

我正在设计有限元素库。对于给定的问题,所使用的有限元网格可以具有不同尺寸的元素(例如四面体和三角形),并且也可以组合相同尺寸的不同元素(例如四面体和六面体)。因此,我需要一个存储有限元信息的数据结构。最基本的信息是元素的连接性(定义元素的节点ID)。例如,我需要以某种方式存储三角形元素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数组旁边存储额外的信息(例如,作为元组),但我不知道这是否好。在设计软件时做出的选择可以让我们的生活在以后变得悲惨,现在我开始意识到这一点。

更新

使用上面的类,可以是以下一个例子:

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(节点),1(线)和2(四边形)维度的元素。

2 个答案:

答案 0 :(得分:2)

  

评论:设计违背了程序的逻辑结构。

我使用了给定的元素数据示例,并没有期望立刻得到整个图片。

  

评论:每个元素都有唯一的维度       (三角形总是尺寸2,        因为四面体总是尺寸为3和        节点维度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)])

请评论这是否更接近您的需求。

答案 1 :(得分:0)

当说“复杂数据结构依赖”时,你可能会谈到“图形”,你是否仔细检查现有的graph theory是否适合你的需求? (如“路线问题”)