def load_transformation_map(filename):
    """!
    @brief Load and build the transformation map from configuration file
    @param filename <str>: path to the input file
    @return <tuple>: boolean indicating if the file is valid, and the TransformationMap object
    """
    try:
        with open(filename, 'r') as f:
            labels = f.readline().rstrip().split('|')
            f.readline()  # line for graphical objects
            transformations = {}
            for line in f.readlines():
                i, j, params = line.rstrip().split('|')
                i, j = int(i), int(j)
                angle, scalexy, scalez, dx, dy, dz = map(float, params.split())
                transformations[i, j] = Transformation(angle, scalexy, scalez, dx, dy, dz)
                transformations[j, i] = transformations[i, j].inverse()
        if len(labels) < 2:
            raise ValueError
        if not is_connected(list(range(len(labels))), transformations.keys()):
            raise ValueError
    except (ValueError, IndexError):
        return False, None
    return True, TransformationMap(labels, transformations) SLF_EIT = 'iso-8859-1' def get_available_variables(input_variables, is_2d):
def get_necessary_equations(known_var_IDs, needed_var_IDs, is_2d, us_equation=None):
def do_calculations_in_frame(equations, input_serafin, time_index, selected_output_IDs,
                             output_float_type, is_2d, us_equation, ori_values={}): class MultiNode(QGraphicsItem):
    """!
    Node representing a tool with its state, ports, ... (in Multi tab)
    """
    NOT_CONFIGURED, READY, SUCCESS, PARTIAL_FAIL, FAIL = 'Not configured', 'Ready', 'Success', 'Partial success', 'Fail'
    COLOR = {NOT_CONFIGURED: QColor(220, 255, 255, 255), READY: QColor(250, 220, 165, 255),
             SUCCESS: QColor(180, 250, 165, 255), PARTIAL_FAIL: QColor(255, 190, 160, 255),
             FAIL: QColor(255, 160, 160, 255)}

    def __init__(self, index):
        super().__init__()
        self._index = index
        self.box = Box(self)

        self.setAcceptedMouseButtons(Qt.LeftButton)
        self.setFlag(QGraphicsItem.ItemIsSelectable)

        self.dashed_pen = QPen(Qt.black, 1, Qt.DashLine)

        self.label = ''
        self.category = ''

        self.state = MultiNode.READY
        self.message = ''

        self.expected_input = (0,)
        self.nb_success = 0
        self.nb_fail = 0
        self.input_index = set()
        self.double_input = False
        self.two_in_one_out = False
        self.second_parent = False  # special properties for pre-bifurcation nodes
        self.ports = []
        self.options = tuple()

    def index(self):
        return self._index

    def add_port(self, port):
        self.ports.append(port)
        port.setParentItem(self)

    def boundingRect(self):
        pos = self.box.rect().topLeft()
        return QRectF(pos.x()-5-MultiPort.WIDTH, pos.y()-20, Box.WIDTH+2*MultiPort.WIDTH+10,
                      Box.HEIGHT+2*MultiPort.WIDTH+20)

    def paint(self, painter, options, widget=None):
        painter.fillRect(self.box.rect(), MultiNode.COLOR[self.state])
        painter.drawText(self.box.rect().topLeft()+QPointF(4, -3), self.state)
        painter.setPen(QPen(QColor(0, 0, 0)))
        painter.drawText(self.box.rect(), Qt.AlignCenter, self.label)
        if self.isSelected():
            painter.setPen(self.dashed_pen)
            painter.drawRect(self.boundingRect())

    def name(self):
        return ' '.join(self.label.split())

    def load(self, options):
        pass

    def mark(self, node_index):
        self.input_index.add(node_index)

    def update_input(self, nb_input):
        self.expected_input = (nb_input,)

    def nb_files(self):
        return max(self.expected_input) class MultiOneInOneOutNode(MultiNode):
    """!
    MultiNode with single input and single output
    """
    def __init__(self, index):
        super().__init__(index)
        self.in_port = MultiInputPort(0, -MultiPort.WIDTH, Box.HEIGHT/2-MultiPort.WIDTH/2)
        self.out_port = MultiOutputPort(1, Box.WIDTH/2-MultiPort.WIDTH/2, Box.HEIGHT)
        self.add_port(self.in_port)
        self.add_port(self.out_port)
        self.expected_input = (0,) class MultiSingleInputNode(MultiNode):
    """!
    MultiNode with single input (without output)
    """
    def __init__(self, index):
        super().__init__(index)
        self.in_port = MultiInputPort(0, -MultiPort.WIDTH, Box.HEIGHT/2-MultiPort.WIDTH/2)
        self.add_port(self.in_port) class MultiSingleOutputNode(MultiNode):
    """!
    MultiNode with single output (without input)
    """
    def __init__(self, index):
        super().__init__(index)
        self.out_port = MultiOutputPort(0, Box.WIDTH/2-MultiPort.WIDTH/2, Box.HEIGHT)
        self.add_port(self.out_port) class MultiDoubleInputNode(MultiNode):
    """!
    MultiNode with two inputs (without output)
    """
    def __init__(self, index):
        super().__init__(index)
        self.first_in_port = MultiInputPort(0, -MultiPort.WIDTH, Box.HEIGHT/4-MultiPort.WIDTH/2)
        self.second_in_port = MultiInputPort(1, -MultiPort.WIDTH, 3*Box.HEIGHT/4-MultiPort.WIDTH/2)
        self.add_port(self.first_in_port)
        self.add_port(self.second_in_port)
        self.expected_input = (0, 1)
        self.double_input = True
        self.auxiliary_data = None

    def update_input(self, nb_input):
        self.expected_input = (nb_input, 1)

    def set_auxiliary_data(self, data):
        self.auxiliary_data = data class MultiTwoInOneOutNode(MultiNode):
    """!
    MultiNode with two inputs and single output
    """
    def __init__(self, index):
        super().__init__(index)
        self.first_in_port = MultiInputPort(0, -MultiPort.WIDTH, Box.HEIGHT/4-MultiPort.WIDTH/2)
        self.second_in_port = MultiInputPort(1, -MultiPort.WIDTH, 3*Box.HEIGHT/4-MultiPort.WIDTH/2)
        self.out_port = MultiOutputPort(2, Box.WIDTH/2-MultiPort.WIDTH/2, Box.HEIGHT)
        self.add_port(self.first_in_port)
        self.add_port(self.second_in_port)
        self.add_port(self.out_port)
        self.expected_input = (0, 0)
        self.two_in_one_out = True
        self.first_ids = []  # information about coupled multi-input streams
        self.second_ids = []
        self.pending_data = {}

        self.has_auxiliary = False
        self.auxiliary_data = None

    def set_auxiliary_data(self, data):
        self.has_auxiliary = True
        self.auxiliary_data = data

    def update_input(self, nb_input):
        self.expected_input = (nb_input, nb_input) class MultiLoadSerafinDialog(MultiLoadDialog):
    def __init__(self, old_options):
        super().__init__('Serafin', settings.SERAFIN_EXT, old_options) def validate_input_options(options):
    filename = options[0]
    if not filename:
        return False, ''
    try:
        with open(filename):
            pass
    except FileNotFoundError:
        return False, ''
    return True, filename def validate_output_options(options):
    suffix = options[0]
    in_source_folder = bool(int(options[1]))
    dir_path = options[2]
    double_name = bool(int(options[3]))
    overwrite = bool(int(options[4]))
    if not in_source_folder:
        if not os.path.exists(dir_path):
            return False, ('', True, '', False, True)
    return True, (suffix, in_source_folder, dir_path, double_name, overwrite)
from datetime import datetime
from PyQt5.QtWidgets import QDialog
from pyteltools.geom.transformation import load_transformation_map
from pyteltools.slf.Serafin import SLF_EIT
from pyteltools.slf.variables import get_US_equation
from .MultiNode import MultiNode, MultiOneInOneOutNode, MultiSingleInputNode, \
    MultiSingleOutputNode, MultiDoubleInputNode, MultiTwoInOneOutNode
from .nodes_op import VerticalAggregationNode
from .util import MultiLoadSerafinDialog, validate_input_options, validate_output_options
import pyteltools.slf.misc as operations




class MultiLoadSerafin2DNode(MultiSingleOutputNode):
    def __init__(self, index):
        super().__init__(index)
        self.category = 'Input/Output'
        self.label = 'Load\nSerafin 2D'
        self.state = MultiNode.NOT_CONFIGURED

    def configure(self, old_options):
        dlg = MultiLoadSerafinDialog(old_options)
        if dlg.exec_() == QDialog.Accepted:
            if dlg.success:
                self.state = MultiNode.READY
                return True, [dlg.dir_paths, dlg.slf_name, dlg.job_ids]
        return False, []


class MultiLoadSerafin3DNode(MultiSingleOutputNode):
    def __init__(self, index):
        super().__init__(index)
        self.category = 'Input/Output'
        self.label = 'Load\nSerafin 3D'
        self.state = MultiNode.NOT_CONFIGURED

    def configure(self, old_options):
        dlg = MultiLoadSerafinDialog(old_options)
        if dlg.exec_() == QDialog.Accepted:
            if dlg.success:
                self.state = MultiNode.READY
                return True, [dlg.dir_paths, dlg.slf_name, dlg.job_ids]
        return False, []


class MultiWriteSerafinNode(MultiOneInOneOutNode):
    def __init__(self, index):
        super().__init__(index)
        self.category = 'Input/Output'
        self.label = 'Write\nSerafin'

    def load(self, options):
        success, self.options = validate_output_options(options)
        if not success:
            self.state = MultiNode.NOT_CONFIGURED


class MultiLoadPolygon2DNode(MultiSingleOutputNode):
    def __init__(self, index):
        super().__init__(index)
        self.category = 'Input/Output'
        self.label = 'Load 2D\nPolygons'

    def load(self, options):

        success, filename = validate_input_options(options)