# -*- encoding: utf-8 -*-
# region gplv3preamble
# The Medical Simulation Markup Language (MSML) - Simplifying the biomechanical modeling workflow
#
# MSML has been developed in the framework of 'SFB TRR 125 Cognition-Guided Surgery'
#
# If you use this software in academic work, please cite the paper:
# S. Suwelack, M. Stoll, S. Schalck, N.Schoch, R. Dillmann, R. Bendl, V. Heuveline and S. Speidel,
# The Medical Simulation Markup Language (MSML) - Simplifying the biomechanical modeling workflow,
# Medicine Meets Virtual Reality (MMVR) 2014
#
# Copyright (C) 2013-2014 see Authors.txt
#
# If you have any questions please feel free to contact us at suwelack@kit.edu
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# endregion
"""
Sorts logic.
Factory and Cache for Sorts in MSML.
Currently there are two disjoint sorts hierarchies.
1. format
2. type
`format` describes the kind of storage (e.g. file format)
`type` is the definition of the ground data type (e.g. vector of ints)
sortdef
## example
vector.int + file.vtk
result in: file_vtk__vector_int as subtype of file and vector
sortsdef => name of a sort, characterize by multiple path, seperated with »+«
path => name/path in the class hierarchy, each class is seperated with ».«
"""
# __all__ = ["__author__", "__date__", "SortsDefinition", "get_sort", "default_sorts_definition"]
from msml.sortdef import *
from . import log
from .exceptions import MSMLException
__author__ = "Alexander Weigl"
__date__ = "2014-01-25, 2014-02-23"
[docs]class MSMLMissingConversionException(MSMLException): pass
[docs]class SortsDefinition(object):
def __init__(self, default_sorts=True):
self.logical_cache = {}
self.physical_cache = {}
self.sort_cache = {}
if default_sorts:
self._bulk_sorts_load(DEFAULTS_SORTS)
[docs] def get_sort(self, physical, logical=None):
pythontypes = {str: MSMLString, int: MSMLInt, float: MSMLFloat}
if physical in pythontypes:
physical = pythontypes[physical]
if type(physical) is str:
physical = self._find_physical(physical)
if logical and type(logical) is str:
logical = self._find_logical(logical)
try:
return self.sort_cache[physical, logical]
except:
s = Sort(physical, logical)
self.sort_cache[physical, logical] = s
return s
def _find_logical(self, typestr):
try:
return self.logical_cache[typestr]
except KeyError as e:
log.warn("_logical type %s requested, but does not exist" % typestr)
return None
def _find_physical(self, fmtstr):
try:
return self.physical_cache[fmtstr]
except KeyError as e:
s = "physical type %s requested, but does not exist" % fmtstr
log.error(s)
raise BaseException(s)
[docs] def register_logical(self, clazz, name=None):
if not name: name = clazz.__name__
self.logical_cache[name] = clazz
return clazz
[docs] def register_physical(self, clazz, name=None):
if not name: name = clazz.__name__
self.physical_cache[name] = clazz
return clazz
[docs] def register_type_with_name(self, name):
def fn(clazz):
return self.register_type(clazz, name)
return fn
def _bulk_sorts_load(self, defs):
temp = (('_logical', self.register_logical),
('physical', self.register_physical))
for tp, register in temp:
for d in defs[tp]:
if isinstance(d, (tuple, list)):
clazz = d[0]
names = d[1:]
else:
clazz = d
names = tuple()
if len(names) == 0:
names = (clazz.__name__,)
for n in names:
register(clazz, n)
DEFAULTS_SORTS = {
'_logical': [
(MSMLLTop, "top", "object", "*"),
Index,
IndexSet,
NodeSet,
FaceSet,
ElementSet,
Mesh,
VolumeMesh,
TetrahedralVolume,
HexahedralVolume,
QuadraticTetrahedral,
SurfaceMesh,
TriangularSurface,
SquareSurface,
QuadraticTriangularSurface,
Image3D,
Image2D,
SegmentationImage3D,
VectorImage3D,
Scalar,
Indices,
VonMisesStress,
Vector,
Force,
Velocity,
Tensor,
Stress,
Displacement,
Position,
],
'physical': [
(InFile, 'file'),
(MSMLString, "str", "string", "s"),
(MSMLFloat, "float"),
(MSMLInt, "int"),
(bool, "bool"),
(MSMLUInt, "uint"),
(MSMLListF, "ListF", "vector.float"),
(MSMLListUI, "ListUI", "vector.uint"),
(MSMLListI, "ListI", "vector.int"),
(VTK, "VTK", "vtk", "file.vtk"),
(VTU, "VTU", "vtu", "file.vtu"),
(VTI, "VTI", "vti", "file.vti"),
(VTP, "VTP", "vtp", "file.vtp"), # TODO: add Hiflow3-InputFormat inp (including material IDs).
DICOM,
HDF5,
(STL, "STL", "stl"),
PNG,
ctx,
vdx,
(TXT, 'TXT', 'txt'),
(InFile, 'NRRD', 'nrrd'), # TODO
(GenericMesh, 'mesh'),
],
}
DEFAULT_SORTS_DEFINITION = SortsDefinition()
[docs]def default_sorts_definition():
"return default sorts definition"
return DEFAULT_SORTS_DEFINITION
[docs]def get_sort(t, f=None):
"""
returns the type object for the given sort definition
"""
return default_sorts_definition().get_sort(t, f)
[docs]def is_sort(x):
return isinstance(x, type) or isinstance(x, Sort)
# #####################################################################################
# conversion
#
#
#
#
import networkx
def _ptype(o):
return o if isinstance(o, type) else o.physical
[docs]class ConversionNetwork(networkx.DiGraph):
[docs] def register_conversion(self, a, b, fn, precedence):
assert is_sort(a) or isinstance(a, type)
assert is_sort(b) or isinstance(b, type)
assert callable(fn)
a, b = _ptype(a), _ptype(b)
self.add_node(a)
self.add_node(b)
self.add_edge(a, b, fn=fn, precedence=precedence)
[docs] def converter(self, a, b):
"""returns a function that converts elements of type `a` to element with type `b`.
"""
def c(n1, n2):
data = self.get_edge_data(n1, n2)
return data['fn']
import inspect, itertools
from_type = _ptype(a)
to_type = _ptype(b)
mro = inspect.getmro(from_type) # inherited from
resolve_order = [from_type] + list(mro)
for start in resolve_order:
try:
(length, paths) = networkx.single_source_dijkstra(self, start, to_type, 'precedence')
if to_type not in length:
continue # not found a valid path, try super class
path = paths[to_type]
edges = zip(path[:-1], path[1:])
converters = list(itertools.starmap(c, edges))
def fn(val):
return reduce(lambda val, convert: convert(val),
converters, val)
return fn
except KeyError as e:
# : Unknown node
pass
raise MSMLMissingConversionException("Could not find a conversion for %s -> %s" % (from_type, to_type))
DEFAULT_CONVERSION_NETWORK = ConversionNetwork()
[docs]def default_conversion_network():
return DEFAULT_CONVERSION_NETWORK
register_conversion = DEFAULT_CONVERSION_NETWORK.register_conversion
conversion = DEFAULT_CONVERSION_NETWORK.converter
# #######################################################################################################################
# # Default Conversions!
#
def _bool(s):
if isinstance(s, bool):
return s
if s is None:
return False
if isinstance(s, str):
return s.lower() in ('True', 'TRUE', 'true', 'on', 'yes')
return bool(s)
def _list_of_type(s, t):
"""Convert an input `s` into a list of `t`
:param s: string, list or tuple
:param t: type of elements
:type s: str
:type t: type
:return:
:rtype: list[t]
"""
if isinstance(s, str):
s = map(lambda x: x.strip(), filter(lambda x: x != "", s.split(" ")))
return map(t, s)
def _list_float(s):
return _list_of_type(s, MSMLFloat)
def _list_integer(s):
return _list_of_type(s, lambda x: MSMLInt(float(x)))
def _list_uinteger(s):
return _list_of_type(s, lambda x: MSMLUInt(float(x)))
def _single_float_list(f):
return list([f])
def _single_int_list(i):
return list([i])
register_conversion(float, get_sort('vector.float'), _single_float_list, 100)
register_conversion(float, int, int, 100)
register_conversion(get_sort('float'), get_sort('int'), int, 100)
register_conversion(get_sort('int'), get_sort('str'), str, 100)
register_conversion(get_sort('int'), get_sort('str'), str, 100)
register_conversion(get_sort('vector.float'), get_sort('vector.int'), _list_integer, 100)
register_conversion(get_sort('vector.int'), get_sort('vector.float'), _list_float, 100)
register_conversion(int, get_sort('vector.int'), _single_int_list, 100)
register_conversion(list, get_sort('vector.float'), _list_float, 100)
register_conversion(list, get_sort('vector.int'), _list_float, 100)
register_conversion(str, get_sort("STL"), STL, 100)
register_conversion(str, get_sort("VTK"), VTK, 100)
register_conversion(str, get_sort("bool"), _bool, 100)
register_conversion(str, get_sort("ctx"), ctx, 100)
register_conversion(str, get_sort("float"), float, 100)
register_conversion(str, get_sort("int"), int, 100)
register_conversion(str, get_sort("str"), MSMLString, 100)
register_conversion(str, get_sort("vdx"), vdx, 100)
register_conversion(str, get_sort('VTI'), VTI, 100)
register_conversion(str, get_sort('vector.float'), _list_float, 100)
register_conversion(str, get_sort('vector.int'), _list_integer, 100)
register_conversion(tuple, get_sort('vector.float'), _list_float, 100)
register_conversion(tuple, get_sort('vector.int'), _list_float, 100)
register_conversion(unicode, get_sort("STL"), STL, 100)
register_conversion(unicode, get_sort("VTK"), VTK, 100)
register_conversion(unicode, get_sort("bool"), _bool, 100)
register_conversion(unicode, get_sort("float"), float, 100)
register_conversion(unicode, get_sort("int"), int, 100)
register_conversion(unicode, get_sort("str"), MSMLString, 100)
register_conversion(unicode, get_sort('VTI'), VTI, 100)
register_conversion(unicode, get_sort('vector.float'), _list_float, 100)
register_conversion(unicode, get_sort('vector.int'), _list_integer, 100)
register_conversion(type(None), bool, _bool, 100)
register_conversion(str, PNG, PNG, 100)
register_conversion(str, get_sort('NRRD'), InFile, 100)
register_conversion(str, TXT, InFile, 100)
# register_conversion(VTK, MSMLString, lambda x: MSMLString(x.filename + ";" + x.partname), 100)
try:
from msml.ext.converters_python import vtk_mesh2generic_mesh
register_conversion(VTK, get_sort('mesh'), vtk_mesh2generic_mesh, 100)
except:
log.error("No Conversion VTK to GenericMesh avaible. Abaqus may not useable")
try:
from msml.ext.misc import ConvertVTKToVTU
import os.path
def convert_vtk_to_vtu(vtk):
"""Convert VTK to VTU file format.
:param vtk:
:type vtk: VTK
:return:
:rtype: VTU
"""
name = "%s_auto_converted.vtu" % vtk
ConvertVTKToVTU(vtk, name)
return VTU(name)
register_conversion(VTK, VTU, convert_vtk_to_vtu, 100)
except:
log.error("No Conversion VTK to VTU avaaible. Hiflow3 may not useable")