"""
Copyright (c) 2021, CodeLV.
Distributed under the terms of the GPL v3 License.
The full license is in the file LICENSE, distributed with this software.
Created on Aug 3, 2021
@author: jrm
"""
from typing import Any
from atom.api import (
Atom,
Bool,
Coerced,
Dict,
Enum,
Float,
ForwardTyped,
Int,
List,
Property,
Str,
Typed,
Value,
observe,
)
from enaml.colors import Color, ColorMember
from enaml.core.declarative import d_, d_func
from .shape import Direction, Point, ProxyShape, Shape, coerce_direction, coerce_point
class ProxyNode(Atom):
declaration = ForwardTyped(lambda: Node)
def set_position(self, position: Point):
raise NotImplementedError
def set_color(self, color: Color):
raise NotImplementedError
def set_mass(self, mass: float):
raise NotImplementedError
def set_force(self, force: Direction):
raise NotImplementedError
def set_torque(self, torque: Direction):
raise NotImplementedError
def set_fixed(self, fixed: bool):
raise NotImplementedError
def get_displaced_position(self) -> Point:
raise NotImplementedError
class ProxyElement(Atom):
declaration = ForwardTyped(lambda: Element)
def set_front_color(self, color: Color):
raise NotImplementedError
def set_back_color(self, color: Color):
raise NotImplementedError
def get_stress(self) -> float:
raise NotImplementedError
class ProxyMesh(ProxyShape):
#: A reference to the Shape declaration
declaration = ForwardTyped(lambda: Mesh)
def find_node(self, id) -> "Node":
raise NotImplementedError
def find_element(self, id) -> "Element":
raise NotImplementedError
def find_face(self, id) -> "Element":
raise NotImplementedError
def find_volume(self, id) -> "Element":
raise NotImplementedError
def set_source(self, source: str):
raise NotImplementedError
def set_disabled(self, disabled: bool):
raise NotImplementedError
class ProxyIterator(Atom):
"""Mesh item iterator"""
def __iter__(self):
raise NotImplementedError
def __len__(self):
raise NotImplementedError
def __getitem__(self, key):
raise NotImplementedError
class ProxyMeshTopology(Atom):
#: Reference to the mesh
declaration = ForwardTyped(lambda: Mesh)
def _get_node_iterator(self) -> ProxyIterator:
raise NotImplementedError
def _get_element_iterator(self) -> ProxyIterator:
raise NotImplementedError
def _get_link_iterator(self) -> ProxyIterator:
raise NotImplementedError
def _get_face_iterator(self) -> ProxyIterator:
raise NotImplementedError
def _get_volume_iterator(self) -> ProxyIterator:
raise NotImplementedError
def _get_group_iterator(self) -> ProxyIterator:
raise NotImplementedError
#: Node iterator
nodes = Property(lambda s: s._get_node_iterator())
#: Elements iterator
elements = Property(lambda s: s._get_element_iterator())
#: Links
links = Property(lambda s: s._get_link_iterator())
#: Faces iterator
faces = Property(lambda s: s._get_face_iterator())
#: Volumes iterator
volumes = Property(lambda s: s._get_volume_iterator())
#: Groups iterator
groups = Property(lambda s: s._get_group_iterator())
class Node(Atom):
"""A mesh node.
This is meant to be used as a pass through object.
"""
#: Proxy to the internal object
proxy = Typed(ProxyNode)
#: The mesh this node is connected to
mesh = ForwardTyped(lambda: Mesh)
#: ID within the mesh
id = Int()
#: Color of the node
color = ColorMember()
#: Description displayed when selected
description = Str()
#: Mass of the node
mass = Float(strict=False)
#: Position of the node
position = Coerced(Point, coercer=coerce_point)
#: Displaced position
def _get_displaced_position(self) -> Point:
return self.proxy.get_displaced_position()
#: Displaced position after analysis
displaced_position = Property(_get_displaced_position)
def _get_displacement(self) -> Point:
return self.displaced_position - self.position
#: Get the delta between the original position and the displaced position
displacement = Property(_get_displacement)
#: Force acting on the node
force = Coerced(Direction, coercer=coerce_direction)
#: Torque acting on the node
torque = Coerced(Direction, coercer=coerce_direction)
#: Position is fixed
fixed = Bool()
#: Member to store any relevent data
data = Dict()
@observe("color", "mass", "position", "force", "torque", "fixed")
def _update_proxy(self, change: dict[str, Any]):
setter = getattr(self.proxy, "set_%s" % change["name"])
setter(change["value"])
def __repr__(self) -> str:
return "<Node: x=%s y=%s z=%s>" % self.position[:]
class Element(Atom):
#: Proxy to the internal object
proxy = Typed(ProxyElement)
#: Set of nodes that make up this element
nodes = List(Node)
#: The mesh this node is connected to
mesh = ForwardTyped(lambda: Mesh)
#: ID within the mesh
id = Int()
#: Description displayed when selected
description = Str()
#: Front color of the node
front_color = ColorMember()
#: Back color of the node
back_color = ColorMember()
#: Material definition
material = ForwardTyped(lambda: Material)
def _get_stress(self):
return self.proxy.get_stress()
#: Von mises equivalent of the stress tensor
stress = Property(_get_stress)
def _get_strain(self):
return self.proxy.get_strain()
#: Von mises equivalent of the strain tensor
strain = Property(_get_strain)
#: Member to store any relevent data
data = Dict()
@observe("front_color", "back_color")
def _update_proxy(self, change: dict[str, Any]):
setter = getattr(self.proxy, "set_%s" % change["name"])
setter(change["value"])
class Material(Atom):
#: The internal object
proxy = Value()
#: Density of the material in Kg/m^2
density = Float()
#: Youngs E elastic modulus in PA (N/m^2)
e = Float()
#: Posson V ratio
v = Float()
#: Shear modulus in PA (N/m^2)
g = Float()
#: Rayleigh damping
m = Float()
#: Rayleigh stiffness
k = Float()
[docs]
class Mesh(Shape):
"""A Mesh
Attributes
----------
source: Shape
Either a shape to mesh or a datasource.
Alternatively the shape can be nested.
algorithm: String
The meshing algorithm to use to generate the mesh.
Examples
--------
enamldef Case(Part):
Box: box:
pass
Mesh:
source = box
"""
proxy = Typed(ProxyMesh)
#: May be either a shape to mesh or a numpy array
source = d_(Value())
#: Meshing options
options = d_(Dict())
#: Disable meshing
disabled = d_(Bool())
#: Export type
export_type = d_(Enum("med", "dat", "unv", "stl", "cgns", "gmf", "sauv"))
#: If given, write the mesh to a file
export_filename = d_(Str())
# ---------------------------------------------------------------------
# Mesh display parameters
# ---------------------------------------------------------------------
node_color = d_(ColorMember())
node_size = d_(Float(1.0, strict=False))
node_type = d_(
Enum(
"circle",
"ball",
"dot",
"plus",
"point",
"star",
"cross",
"point-in-circle",
"star-in-circle",
"plus-in-circle",
"cross-in-circle",
"large-ring",
"medium-ring",
"small-ring",
)
)
edge_color = d_(ColorMember("grey"))
edge_size = d_(Float(1.0, strict=False))
beam_color = d_(ColorMember("black"))
beam_size = d_(Float(1.0, strict=False))
# ---------------------------------------------------------------------
# Mesh API
# ---------------------------------------------------------------------
@d_func
def prepare_mesh(self, gen, mesh, shape):
"""This is invoked before the mesh is computed. It should be
defined to prepare the meshing parameters.
Parameters
----------
gen: SMESH_Gen
The mesh generator
mesh: SMESH_Mesh
The mesh handle
shape: TopoDS_Shape
The shape being meshed
"""
raise NotImplementedError
@d_func
def process_mesh(self):
"""Process the generated mesh. This is invoked before colorizing.
If you want to use your own FEA, this is a good place to hook in.
"""
pass
@d_func
def colorize_mesh(self):
"""This is invoked after the mesh is computed. The topology can
be used to apply colors to nodes and elements.
"""
pass
# ---------------------------------------------------------------------
# Mesh lookup API
# ---------------------------------------------------------------------
def find_node(self, id) -> Node:
"""Find the node with the given ID."""
return self.proxy.find_node(id)
def find_element(self, id) -> Element:
"""Find the node with the given ID."""
return self.proxy.find_element(id)
def find_face(self, id) -> Element:
"""Find the face element with the given ID."""
return self.proxy.find_face(id)
def find_volume(self, id) -> Element:
"""Find the volume element with the given ID."""
return self.proxy.find_volume(id)