#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
#
# _____ _ _ _ _____ _____
# | ____|__| | ___| |_ _____(_)___ ___| ___| ____|
# | _| / _` |/ _ \ \ \ /\ / / _ \ / __/ __| |_ | _|
# | |__| (_| | __/ |\ V V / __/ \__ \__ \ _| | |___
# |_____\__,_|\___|_| \_/\_/ \___|_|___/___/_| |_____|
#
#
# Unit of Strength of Materials and Structural Analysis
# University of Innsbruck,
# 2017 - today
#
# Matthias Neuner matthias.neuner@uibk.ac.at
#
# This file is part of EdelweissFE.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The full text of the license can be found in the file LICENSE.md at
# the top level directory of EdelweissFE.
# ---------------------------------------------------------------------
# Created on Tue Dec 18 09:18:25 2018
# @author: Matthias Neuner
import numpy as np
from edelweissfe.points.node import Node
from edelweissfe.sets.elementset import ElementSet
from edelweissfe.sets.nodeset import NodeSet
class NodeFieldSubset:
pass
[docs]class NodeField:
"""
This class represents a node field.
A node field associates every node with multiple entries (e.g., flux and effort) of a field variable.
Furthermore, for convencience, it allows to get fast access to values for individual nodes or node sets.
The purpose is to store field data in an efficient, contiguos manner rather than distributing it across
all individual nodes.
.. code-block:: console
Example:
NodeField 'Displacement'
values: {'U' : [[0,0,0], # Node (1)
[1,0,0], # Node (2)
[0,1,0]]} # Node (3)
Spatial representation:
(1) --------- (2)
| * *
| *+---------+ *+---------+
| | [0,0,0] | | [1,0,0] |
(3) +---------+ +---------+
*
* +---------+
| [0,1,0] |
+---------+
Parameters
----------
fieldName
The name of the field.
dimension
The dimension of the field.
nodes
The associated nodes. Only nodes with active fields are considered.
"""
def __init__(self, fieldName: str, dimension: int, nodeSet: NodeSet):
self.name = fieldName
self.associatedSet = nodeSet
self.nodes = [n for n in nodeSet if fieldName in n.fields]
self.dimension = dimension
self._indicesOfNodesInArray = {n: i for i, n in enumerate(self.nodes)}
self._subsetCache = dict()
self._values = dict()
def _getNodeFieldSubsetClass(
self,
):
return NodeFieldSubset
def __getitem__(self, key):
return self._values[key]
def __contains__(self, key):
return key in self._values
[docs] def createFieldValueEntry(self, name: str) -> np.ndarray:
"""
Add an empty entry with given name for the field, e.g, 'U' or 'P' for flux or effort entries.
Parameters
----------
name
The name of the entry.
Returns
-------
np.ndarray
The new entry
"""
self._values[name] = np.zeros((len(self.nodes), self.dimension), dtype=float)
return self[name]
[docs] def subset(self, subset) -> NodeFieldSubset:
"""
Get a view on a subset of the field.
Parameters
----------
subset
The subset, e.g., a single :class:`Node, or a :class:`NodeSet or :class:`ElementSet.
Returns
-------
NodeFieldSubset
The subset of the present NodeField.
"""
return self._getSubsetFromCache(subset)
def _getSubsetFromCache(self, subset) -> NodeFieldSubset:
"""
Exploit a cache to reuse already constructed NodeFieldSubsets.
If the subset does not exist, it will be created here.
Parameters
----------
subset
The subset, e.g., a single Node, or a NodeSet or ElementSet.
Returns
-------
NodeFieldSubset
The subset of the present NodeField.
"""
if subset in self._subsetCache:
return self._subsetCache[subset]
else:
self._subsetCache[subset] = self._getNodeFieldSubsetClass()(self, subset)
return self._subsetCache[subset]
[docs] def copyEntriesFromOther(self, other, fieldValueEntries: list[str] = None):
"""
Copy values from another NodeField.
If the fields differ, the intersection is considered.
Parameters
----------
subset
The sub NodeField.
fieldValueEntries
The list of entries which should be copied. Default: all entries are copied.
"""
if not fieldValueEntries:
fieldValueEntries = self._values.keys() & other._values.keys()
commonNodes = self._indicesOfNodesInArray.keys() & other._indicesOfNodesInArray.keys()
for fieldValueEntry in fieldValueEntries:
self[fieldValueEntry][:] = 0.0
idcsHere = [self._indicesOfNodesInArray[n] for n in commonNodes]
idcsOther = [other._indicesOfNodesInArray[n] for n in commonNodes]
self[fieldValueEntry][idcsHere] = other[fieldValueEntry][idcsOther]
[docs] def addEntriesFromOther(self, other, fieldValueEntries: list[str] = None):
"""
Add values from another NodeField.
If the fields differ, the intersection is considered.
Parameters
----------
subset
The sub NodeField.
fieldValueEntries
The list of entries which should be copied. Default: all entries are copied.
"""
if not fieldValueEntries:
fieldValueEntries = self._values.keys() & other._values.keys()
commonNodes = self._indicesOfNodesInArray.keys() & other._indicesOfNodesInArray.keys()
for fieldValueEntry in fieldValueEntries:
idcsHere = [self._indicesOfNodesInArray[n] for n in commonNodes]
idcsOther = [other._indicesOfNodesInArray[n] for n in commonNodes]
self[fieldValueEntry][idcsHere] += other[fieldValueEntry][idcsOther]
[docs]class NodeFieldSubset(NodeField):
def __init__(self, parentNodeField, subset):
self.parentNodeField = parentNodeField
self.associatedSet = subset
self.nodes = self._getSubsetNodes(subset)
self._indicesOfNodesInParentArray = np.array([parentNodeField._indicesOfNodesInArray[n] for n in self.nodes])
def __getitem__(self, key):
return self.parentNodeField[key][self._indicesOfNodesInParentArray]
def __contains__(self, key):
return key in self.subsetNodes
[docs] def createFieldValueEntry(self, name):
raise Exception("Invalid operation on subset of a NodeField")
[docs] def subset(self, subset):
raise Exception("Subsets of subsets are not yet implemented!")
def _getSubsetNodes(self, subset) -> list[Node]:
"""
Get the nodes associated with a subset.
Only nodes with the active field are considered.
Parameters
----------
subset
The subset, e.g., a single Node, a NodeSet or ElementSet.
Returns
-------
list[Node]
The list of subset nodes.
"""
if type(subset) is Node:
nodeCandidates = [
subset,
]
elif type(subset) is ElementSet:
nodeCandidates = subset.extractNodeSet()
elif type(subset) is NodeSet:
nodeCandidates = subset
else:
raise Exception("Invalid subset")
return [n for n in nodeCandidates if n in self.parentNodeField._indicesOfNodesInArray]