Source code for daetools.dae_simulator.simulation_inspector

"""
***********************************************************************************
                            simulation_inspector.py
                 DAE Tools: pyDAE module, www.daetools.com
                 Copyright (C) Dragan Nikolic
***********************************************************************************
DAE Tools is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License version 3 as published by the Free Software
Foundation. DAE Tools 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 the
DAE Tools software; if not, see <http://www.gnu.org/licenses/>.
***********************************************************************************
"""

import sys, tempfile, numpy
from daetools.pyDAE import *
from .tree_item import *

def _collectParameters(nodeItem, model_or_port, dictParameters):
    """
    Recursively looks for parameters in the model/port and all its child-models and
    adds a new treeItem object to the parent item 'nodeItem'.
    """
    if not isinstance(model_or_port, (daePort, daeModel)):
        raise RuntimeError('Invalid type of object in _collectDOFs function')

    # Process parameters in the current port/model
    parameters = model_or_port.Parameters

    for obj in parameters:
        name = obj.Name
        if obj.NumberOfPoints == 1 and len(obj.Domains) == 0:
            value = float(obj.GetValue())
        else:
            value = obj.npyValues.tolist()
        units = obj.Units
        description = obj.Description
        item = treeItem_Quantity(nodeItem, name, description, value, units, checkIfItemsAreFloats = True)
        dictParameters[obj.CanonicalName] = (obj, item)

    if isinstance(model_or_port, daeModel):
        model = model_or_port
        for port in model.Ports:
            portItem = treeItem(nodeItem, port.Name, treeItem.typeNone)
            _collectParameters(portItem, port, dictParameters)
        for component in model.Components:
            componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
            _collectParameters(componentItem, component, dictParameters)

def _collectDomains(nodeItem, model_or_port, dictDomains):
    """
    Recursively looks for domains in the 'model' and all its child-models and
    adds a new treeItem object to the parent item 'nodeItem'.
    """
    if not isinstance(model_or_port, (daePort, daeModel)):
        raise RuntimeError('Invalid type of object in _collectDOFs function')

    # Process domains in the current port/model
    domains = model_or_port.Domains

    for obj in domains:
        name  = obj.Name
        type  = obj.Type 
        units = obj.Units
        description = obj.Description
        if obj.Type == eArray:
            numberOfPoints = obj.NumberOfPoints
            item = treeItem_Domain(nodeItem, type, name, description, numberOfPoints=numberOfPoints, units=units)
        elif obj.Type == eStructuredGrid:
            #discrMethod        = obj.DiscretizationMethod # not edited
            #order              = obj.DiscretizationOrder  # not edited
            numberOfIntervals  = obj.NumberOfIntervals    # not edited
            points             = obj.Points.tolist()
            item = treeItem_Domain(nodeItem, type, name, description, numberOfIntervals = numberOfIntervals, points = points, units = units)
        elif obj.Type == eUnstructuredGrid:
            numberOfPoints = obj.NumberOfPoints       # not edited
            points         = obj.Coordinates
            item = treeItem_Domain(nodeItem, type, name, description, numberOfPoints=numberOfPoints, units=units)
        
        dictDomains[obj.CanonicalName] = (obj, item)

    if isinstance(model_or_port, daeModel):
        model = model_or_port
        for port in model.Ports:
            portItem = treeItem(nodeItem, port.Name, treeItem.typeNone)
            _collectDomains(portItem, port, dictDomains)
        for component in model.Components:
            componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
            _collectDomains(componentItem, component, dictDomains)

def _collectOutputVariables(nodeItem, model_or_port, dictOutputVariables):
    """
    Recursively looks for variables in the 'model' and all its child-models and
    adds a new treeItem object to the parent item 'nodeItem'.
    """
    if not isinstance(model_or_port, (daePort, daeModel)):
        raise RuntimeError('Invalid type of object in _collectDOFs function')

    # Process variables/parameters in the current port/model
    variables = model_or_port.Variables
    parameters = model_or_port.Parameters

    for obj in variables:
        name  = obj.Name
        value = obj.ReportingOn
        item  = treeItem_OutputVariable(nodeItem, name, value)
        dictOutputVariables[obj.CanonicalName] = (obj, item)
    
    for obj in parameters:
        name  = obj.Name
        value = obj.ReportingOn
        item  = treeItem_OutputVariable(nodeItem, name, value)
        dictOutputVariables[obj.CanonicalName] = (obj, item)

    if isinstance(model_or_port, daeModel):
        model = model_or_port
        for port in model.Ports:
            portItem = treeItem(nodeItem, port.Name, treeItem.typeNone)
            _collectOutputVariables(portItem, port, dictOutputVariables)
        for component in model.Components:
            componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
            _collectOutputVariables(componentItem, component, dictOutputVariables)

def _checkIfAnyOfNestedSTNsIsdaeSTN(state):
    # Returns True if there is any daeSTN type of STN in any of nested STNs
    # or in any of states in those nestedSTNs etc...
    
    # If there are no  nested STNs return False
    if len(state.NestedSTNs) == 0:
        return False
    
    # Iterate over nested STNs
    for nestedSTN in state.NestedSTNs:
        # If a nested STN is daeSTN return true
        if nestedSTN.Type == eSTN:
            return True
        
        # Iterate over states in the current nested state and check
        # if nested STNs within it have daeSTN type of STNs 
        for nested_state in nestedSTN:
            if _checkIfAnyOfNestedSTNsIsdaeSTN(nested_state):
                return True
    
    return False
    
def _processSTN(nodeItem, stn, dictSTNs):
    """
    Recursively process states in the STN and all nestedSTNs that exist in those states
    and adds new treeItem objects to the parent item 'nodeItem'.
    """
    if stn.Type == eSTN:
        # Add an empty item for the STN
        stnName = stn.Name
        stnItem = treeItem(nodeItem, stnName, treeItem.typeNone)
        
        # Iterate over states and add a checkable item for each of them 
        listStates = []
        dictSTNs[stn.CanonicalName] = (stn, listStates)
        for state in stn.States:
            name        = state.Name
            description = state.Description
            isActive    = True if stn.ActiveState == state.Name else False
            item        = treeItem_State(stnItem, name, description, isActive)
            listStates.append((state, item))
            
            # Process nested STNs (if existing)
            for nestedSTN in state.NestedSTNs:
                _processSTN(item, nestedSTN, dictSTNs)
    
    elif stn.Type == eIF:
        # Here we process only Nested states (if existing)        
        # If there is a nested STN in any of states add the ifItem
        # which represents a parent to states with nested STNs
        ifItem = None
        for state in stn.States:
            if _checkIfAnyOfNestedSTNsIsdaeSTN(state):
                stnName = stn.Name
                ifItem = treeItem(nodeItem, stnName, treeItem.typeNone)
                break
        
        # If ifItem is None that means there are no nested STNs in any of states - immediately return
        if not ifItem:
            return
            
        # Now we have a parent for states with nested STNs
        # Iterate over states and add an item for each of them 
        for state in stn.States:
            if _checkIfAnyOfNestedSTNsIsdaeSTN(state):
                # If there are STNs of type daeSTN in the current state add a new empty item
                stateName = state.Name
                stateItem = treeItem(ifItem, stateName, treeItem.typeNone)
                
                # Process nested STNs (if existing)
                for nestedSTN in state.NestedSTNs:
                    _processSTN(stateItem, nestedSTN, dictSTNs)

def _collectStates(nodeItem, model, dictSTNs):
    """
    Recursively looks for STNs in the 'model' and all its child-models
    and process them accordingly.
    """
    for stn in model.STNs:
        _processSTN(nodeItem, stn, dictSTNs)
        
    for component in model.Components:
        componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
        _collectStates(componentItem, component, dictSTNs)

def _collectInitialConditions(nodeItem, model_or_port, dictInitialConditions, IDs):
    """
    Recursively looks for variables in the model/port and all models child-models and
    adds a new treeItem object to the parent item 'nodeItem' for all those
    who are differential and need an initial conditions to be set.
    """
    if not isinstance(model_or_port, (daePort, daeModel)):
        raise RuntimeError('Invalid type of object in _collectDOFs function')

    # Process variables in the current port/model
    variables = model_or_port.Variables

    for var in variables:
        name              = var.Name
        domainsIndexesMap = var.GetDomainsIndexesMap(indexBase = 0)
        units             = var.VariableType.Units
        description       = var.Description
        
        # Check if there is diff. flag set for any point in the variable
        # If there is not - skip the variable
        if not cnDifferential in IDs[var.OverallIndex : var.OverallIndex + var.NumberOfPoints]:
            continue
        
        if var.NumberOfPoints == 1 and len(var.Domains) == 0:
            value = var.GetValue()
        else:
            values = numpy.array(var.npyValues, dtype=object)
            # Iterate over points and set None for the points which are *not* differential
            for var_index, domainIndexes in list(domainsIndexesMap.items()):
                #print var_index, domainIndexes, values[tuple(domainIndexes)]
                if IDs[var.OverallIndex + var_index] != cnDifferential:
                    values[tuple(domainIndexes)] = None
            value = values.tolist()
        
        item = treeItem_Quantity(nodeItem, name, description, value, units, checkIfItemsAreFloats = False)
        dictInitialConditions[name] = (var, item)

    if isinstance(model_or_port, daeModel):
        model = model_or_port
        for port in model.Ports:
            portItem = treeItem(nodeItem, port.Name, treeItem.typeNone)
            _collectInitialConditions(portItem, port, dictInitialConditions, IDs)
        for component in model.Components:
            componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
            _collectInitialConditions(componentItem, component, dictInitialConditions, IDs)
    
def _collectDOFs(nodeItem, model_or_port, dictDOFs, IDs):
    """
    Recursively looks for variables in the 'model' and all its child-models and
    adds a new treeItem object to the parent item 'nodeItem' for all those
    who are differential and need an initial conditions to be set.
    ACHTUNG, ACHTUNG!!!!!
    DOFs should be collected from models and ports.
    """

    if not isinstance(model_or_port, (daePort, daeModel)):
        raise RuntimeError('Invalid type of object in _collectDOFs function')

    # Process variables in the current port/model
    variables = model_or_port.Variables
    
    for var in variables:
        name              = var.Name
        domainsIndexesMap = var.GetDomainsIndexesMap(indexBase = 0)
        units             = var.VariableType.Units
        description       = var.Description

        # Check if there is assigned flag set for any point in the variable
        # If there is not then skip the variable
        if not cnAssigned in IDs[var.OverallIndex : var.OverallIndex + var.NumberOfPoints]:
            continue

        if var.NumberOfPoints == 1 and len(var.Domains) == 0:
            value = var.GetValue()
        else:
            values = numpy.array(var.npyValues, dtype=object)
            # Iterate over points and set None for the points which are *not* differential
            for var_index, domainIndexes in list(domainsIndexesMap.items()):
                if IDs[var.OverallIndex + var_index] != cnAssigned:
                    values[tuple(domainIndexes)] = None
            value = values.tolist()

        item = treeItem_Quantity(nodeItem, name, description, value, units, checkIfItemsAreFloats = False)
        dictDOFs[name] = (var, item)

    if isinstance(model_or_port, daeModel):
        model = model_or_port
        for port in model.Ports:
            portItem = treeItem(nodeItem, port.Name, treeItem.typeNone)
            _collectDOFs(portItem, port, dictDOFs, IDs)
        for component in model.Components:
            componentItem = treeItem(nodeItem, component.Name, treeItem.typeNone)
            _collectDOFs(componentItem, component, dictDOFs, IDs)
        
[docs]class daeSimulationInspector(object): categoryParameters = '___PARAMETERS___' categoryInitialConditions = '___INITIAL_CONDITIONS___' categoryActiveStates = '___ACTIVE_STATES___' categoryDOFs = '___DOFS___' categoryDomains = '___DOMAINS___' categoryVariablesToReport = '___VARIABLES_TO_REPORT___' def __init__(self, simulation): self.simulation = simulation self.domains = {} self.parameters = {} self.initial_conditions = {} self.dofs = {} self.stns = {} self.output_variables = {} self.treeDomains = None self.treeParameters = None self.treeInitialConditions = None self.treeDOFs = None self.treeStates = None self.treeOutputVariables = None IDs = self.simulation.VariableTypes self.treeDomains = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectDomains(self.treeDomains, self.simulation.m, self.domains) self.treeParameters = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectParameters(self.treeParameters, self.simulation.m, self.parameters) self.treeInitialConditions = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectInitialConditions(self.treeInitialConditions, self.simulation.m, self.initial_conditions, IDs) self.treeDOFs = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectDOFs(self.treeDOFs, self.simulation.m, self.dofs, IDs) self.treeStates = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectStates(self.treeStates, self.simulation.m, self.stns) # Checks whether there are some unchecked items that have some of its children checked and corrects it correctSelections(self.treeStates) self.treeOutputVariables = treeItem(None, self.simulation.m.Name, treeItem.typeNone) _collectOutputVariables(self.treeOutputVariables, self.simulation.m, self.output_variables) def _generateHTMLForm(self): content = '' content += '<fieldset>' content += '<legend>General</legend>' content += '<label for="testName">Test name</label>' content += '<input class="ninemlString required" id="id_testName" type="text" name="testName" value=""/><br/>' content += '<label for="testDescription">Test description</label>' content += '<textarea class="ninemlMultilineString" id="id_testDescription" name="testDescription" rows="2" cols="2"></textarea><br/>' content += '</fieldset>\n' content += '<fieldset>' content += '<legend>Simulation</legend>' #content += '<label for="timeHorizon">Time horizon</label>' #content += '<input class="ninemlFloat required number" id="id_timeHorizon" type="text" name="timeHorizon" value="{0}"/><br/>'.format(self.timeHorizon) #content += '<label for="reportingInterval">Reporting interval</label>' #content += '<input class="ninemlFloat required number" id="id_reportingInterval" type="text" name="reportingInterval" value="{0}"/><br/>'.format(self.reportingInterval) content += '</fieldset>\n' if self.treeParameters: content += '<fieldset>' content += '<legend>Parameters</legend>\n' content += self._generateHTMLFormTree(self.treeParameters, daeSimulationInspector.categoryParameters) content += '</fieldset>\n' if self.treeDomains: content += '<fieldset>' content += '<legend>Domains</legend>\n' content += self._generateHTMLFormTree(self.treeDomains, daeSimulationInspector.categoryDomains) content += '</fieldset>\n' if self.treeInitialConditions: content += '<fieldset>' content += '<legend>Initial conditions</legend>\n' content += self._generateHTMLFormTree(self.treeInitialConditions, daeSimulationInspector.categoryInitialConditions) + '\n' content += '</fieldset>\n' if self.treeDOFs: content += '<fieldset>' content += '<legend>Degrees Of Freedom</legend>\n' content += self._generateHTMLFormTree(self.treeDOFs, daeSimulationInspector.categoryDOFs) content += '</fieldset>\n' if self.treeStates: content += '<fieldset>' content += '<legend>Initially active states</legend>\n' content += self._generateHTMLFormTree(self.treeStates, daeSimulationInspector.categoryActiveStates) content += '</fieldset>\n' if self.treeOutputVariables: content += '<fieldset>' content +='<legend>Output variables</legend>\n' content += self._generateHTMLFormTree(self.treeOutputVariables, daeSimulationInspector.categoryVariablesToReport) + '\n' content += '</fieldset>\n' return content def _generateHTMLFormTree(self, item, category = '', required = False): """ Internal function to recursively generate a HTML form for the given treeItem root item. :param item: treeItem object :param category: string :param required: boolean :rtype: string :raises: """ if category == '': inputName = item.canonicalName else: inputName = category + '.' + item.canonicalName content = '<ul>' if item.itemType == treeItem.typeQuantity: content += '<li><label for="{1}">{0}</label><input class="ninemlFloat required number" type="text" name="{1}" value="{2}"/></li>'.format(item.name, inputName, item.getValueAsText()) elif item.itemType == treeItem.typeDomain: content += '<li><label for="{1}">{0}</label><input class="ninemlString" type="text" name="{1}" value="{2}"/></li>'.format(item.name, inputName, item.getValueAsText()) elif item.itemType == treeItem.typeState: # if isinstance(item.data, collections.Iterable) and len(item.data) > 0: # content += '<li><label for="{1}">{0}</label> <select class="ninemlComboBox" name="{1}">'.format(item.name, inputName) # for available_regime in item.data: # if available_regime == item.value: # content += '<option value="{0}" selected>{0}</option>'.format(available_regime) # else: # content += '<option value="{0}">{0}</option>'.format(available_regime) # content += '</select></li>' # else: # content += '<li>{0}</li>'.format(item.name) if required: content += '<li><label for="{1}">{0}</label><input class="ninemlString required" type="text" name="{1}" value="{2}"/></li>'.format(item.name, inputName, item.getValueAsText()) else: content += '<li><label for="{1}">{0}</label><input class="ninemlString" type="text" name="{1}" value="{2}"/></li>'.format(item.name, inputName, item.getValueAsText()) elif item.itemType == treeItem.typeOutputVariable: if item.getValue(): content += '<li><label for="{1}">{0}</label><input class="ninemlCheckBox" type="checkbox" name="{1}" checked/></li>'.format(item.name, inputName) else: content += '<li><label for="{1}">{0}</label><input class="ninemlCheckBox" type="checkbox" name="{1}"/></li>'.format(item.name, inputName) else: content += '<li>{0}</li>'.format(item.name) for child in item.children: content += self._generateHTMLFormTree(child, category, required) content += '</ul>' return content