Skip to content

Commit

Permalink
OutStreams as RAVEN entity (#1329)
Browse files Browse the repository at this point in the history
* updated HERON submodule

* converted printing to an OutStreamBase type, also made OutStreamBase type

* accidental deletion recant

* plotting seems to be restored

* wip

* preserving var capitalization better

* fixed unit tests for dataobjects

* updated error message check for bad input node in strict mode

* using dir option correclty

* reverting heron version change

* cleanup

* HERON submodule update

* comments addressed
  • Loading branch information
PaulTalbot-INL authored Oct 3, 2020
1 parent 614348c commit 1ce4703
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 195 deletions.
4 changes: 2 additions & 2 deletions framework/BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def readXML(self,xmlNode,messageHandler,variableGroups={},globalAttributes=None)
self.name = xmlNode.attrib['name']
else:
self.raiseAnError(IOError,'not found name for a '+self.__class__.__name__)
self.type = xmlNode.tag
self.type = xmlNode.tag
if globalAttributes is not None:
self.globalAttributes = globalAttributes
if 'verbosity' in xmlNode.attrib.keys() or 'verbosity' in self.globalAttributes:
Expand Down Expand Up @@ -114,7 +114,7 @@ def replaceVariableGroups(node):
self.raiseADebug('------Reading Completed for:')
self.printMe()

def handleInput(self,paramInput,messageHandler,variableGroups={},globalAttributes=None):
def handleInput(self, paramInput, messageHandler, variableGroups={}, globalAttributes=None):
"""
provide a basic reading capability from the xml input file for what is common to all types in the simulation than calls _handleInput
that needs to be overloaded and used as API. Each type supported by the simulation should have: name (xml attribute), type (xml tag),
Expand Down
12 changes: 6 additions & 6 deletions framework/DataObjects/DataSet.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def sliceByIndex(self,index):
# NOTE: The slice may include NaN if a variable does not have a value along a different index for this snapshot along "index"
return slices

def write(self,fileName,style='netCDF',**kwargs):
def write(self, fileName, style='netCDF', **kwargs):
"""
Writes this dataset to disk based on the format.
@ In, fileName, str, path and name of file to write
Expand All @@ -703,12 +703,12 @@ def write(self,fileName,style='netCDF',**kwargs):
if style.lower() == 'netcdf':
self._toNetCDF(fileName,**kwargs)
elif style.lower() == 'csv':
if len(self)==0: #TODO what if it's just metadata?
if len(self) == 0:
self.raiseAWarning('Nothing to write to CSV! Checking metadata ...')
else:
#first write the CSV
firstIndex = kwargs.get('firstIndex',0)
self._toCSV(fileName,start=firstIndex,**kwargs)
self._toCSV(fileName, start=firstIndex, **kwargs)
# then the metaxml
if len(self._meta):
self._toCSVXML(fileName,**kwargs)
Expand Down Expand Up @@ -1681,14 +1681,14 @@ def _getRealizationFromDataByValue(self, match, noMatch, tol=1e-15, unpackXArray
return len(self),None
return idx,self._getRealizationFromDataByIndex(idx,unpackXArray)

def _getRequestedElements(self,options):
def _getRequestedElements(self, options):
"""
Obtains a list of the elements to be written, based on defaults and options[what]
@ In, options, dict, general list of options for writing output files
@ Out, keep, list(str), list of variables that will be written to file
"""
if 'what' in options.keys():
elements = options['what'].split(',')
elements = options['what']
keep = []
for entry in elements:
small = entry.strip().lower()
Expand Down Expand Up @@ -1867,7 +1867,7 @@ def _setScalingFactors(self,var=None):
except Exception:
self.raiseADebug('Had an issue with setting scaling factors for variable "{}". No big deal.'.format(name))

def _toCSV(self,fileName,start=0,**kwargs):
def _toCSV(self, fileName, start=0, **kwargs):
"""
Writes this data object to CSV file (except the general metadata, see _toCSVXML)
@ In, fileName, str, path/name to write file
Expand Down
35 changes: 14 additions & 21 deletions framework/OutStreams/Factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,22 @@

################################################################################
from utils import utils
from .OutStreamManager import OutStreamManager
from .OutStreamPlot import OutStreamPlot
from .OutStreamPrint import OutStreamPrint
## [ Add new class here ]
################################################################################
## Alternatively, to fully automate this file:
# from OutStreamManagers import *
################################################################################

"""
Interface Dictionary (factory) (private)
"""
# This machinery will automatically populate the "knownTypes" given the
# imports defined above.
__base = 'OutStreamManager'
__interFaceDict = {}
from .OutStreamBase import OutStreamBase

from .FilePrint import FilePrint
from .GeneralPlot import GeneralPlot
# from .DataMining import DataMining
# from .VDCComparison import VDCComparison

for classObj in utils.getAllSubclasses(eval(__base)):
## As long as these subclasses follow the pattern of starting with OutStream
## this will appropriately key them to a more user-friendly name without the
## need for them to redudantly prepend "X" as "OutStreamX"
key = classObj.__name__.replace('OutStream','')
__interFaceDict[key] = classObj
# Interface Dictionary (factory) (private)
__base = 'OutStreamBase'
__interFaceDict = {
'Print': FilePrint,
'Plot': GeneralPlot,
# 'DataMining': DataMining,
# 'VDCComparison': VDCComparison,
}

def knownTypes():
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,14 @@
@author: alfoa
"""
#for future compatibility with Python 3-----------------------------------------
from __future__ import division, print_function, unicode_literals, absolute_import
#End compatibility block for Python 3-------------------------------------------
#External Modules---------------------------------------------------------------
import os
#External Modules End-----------------------------------------------------------

#Internal Modules---------------------------------------------------------------
from utils import InputData, InputTypes
import DataObjects
from .OutStreamManager import OutStreamManager
from .OutStreamBase import OutStreamBase
from ClassProperty import ClassProperty
#Internal Modules End-----------------------------------------------------------

class OutStreamPrint(OutStreamManager):
class FilePrint(OutStreamBase):
"""
Class for managing the printing of files as an output stream.
"""
Expand All @@ -46,26 +40,83 @@ def availableOutStreamTypes(cls):
"""
A class level constant that tells developers what outstreams are
available from this class
@ In, cls, the OutStreamPrint class of which this object will be a type
@ In, cls, the OutStreams class of which this object will be a type
"""
return cls._availableOutStreamTypes

@classmethod
def getInputSpecification(cls):
"""
Method to get a reference to a class that specifies the input data for class "cls".
@ In, cls, the class for which we are retrieving the specification
@ Out, inputSpecification, InputData.ParameterInput, class to use for specifying the input of cls.
"""
spec = OutStreamBase.getInputSpecification()

types = InputTypes.makeEnumType('FilePrintTypes', 'FilePrintTypes', cls._availableOutStreamTypes)
spec.addSub(InputData.parameterInputFactory('type', contentType=types))
spec.addSub(InputData.parameterInputFactory('source', contentType=InputTypes.StringListType))
spec.addSub(InputData.parameterInputFactory('what', contentType=InputTypes.StringListType))
spec.addSub(InputData.parameterInputFactory('filename', contentType=InputTypes.StringType))
spec.addSub(InputData.parameterInputFactory('clusterLabel', contentType=InputTypes.StringType))

# these are in user manual or code, but don't appear to be used/documented ...
# spec.addSub(InputData.parameterInputFactory('target', contentType=InputTypes.StringListType))
# spec.addSub(InputData.parameterInputFactory('directory',
# contentType=InputTypes.StringListType))
return spec

def __init__(self):
"""
Constructor
@ In, None
@ Out, None
"""
OutStreamManager.__init__(self)
OutStreamBase.__init__(self)
self.type = 'OutStreamFilePrint'
self.printTag = 'OUTSTREAM PRINT'
OutStreamManager.__init__(self)
self.sourceName = []
self.sourceData = None
self.what = None
# dictionary of what indices have already been printed, so we don't duplicate writing efforts
self.indexPrinted = {} # keys are filenames, which should be reset at the end of every step
self.subDirectory = None # subdirectory where to store the outputs

def _handleInput(self, spec):
"""
Loads the input specs for this object.
@ In, spec, InputData.ParameterInput, input specifications
@ Out, None
"""
typ = spec.findFirst('type')
if typ is None:
self.raiseAnError(IOError, f'OutStream.Print "{self.name}" is missing the "type" node!')
self.options['type'] = typ.value

source = spec.findFirst('source')
if source is None:
self.raiseAnError(IOError, f'OutStream.Print "{self.name}" is missing the "source" node!')
self.sourceName = source.value

# FIXME this is a terrible name
what = spec.findFirst('what')
if what is not None:
self.what = what.value # [x.lower() for x in what.value]

fname = spec.findFirst('filename')
if fname is not None:
self.filename = fname.value

cluster = spec.findFirst('clusterLabel')
if cluster is not None:
self.options['clusterLabel'] = cluster.value

# checks
if self.options['type'] == 'csv' and self.what is not None:
for target in [x.lower() for x in self.what]:
if not target.startswith(('input', 'output', 'metadata')):
self.raiseAnError(IOError, f'<what> requests must start with "input", "output", or "metadata"! See OutStream.Print "{self.name}"')

def localGetInitParams(self):
"""
This method is called from the base function. It retrieves the initial
Expand All @@ -78,7 +129,7 @@ def localGetInitParams(self):
for index in range(len(self.sourceName)):
paramDict['Source Name ' + str(index) + ' :'] = self.sourceName[index]
if self.what:
for index, var in enumerate(self.what.split(',')):
for index, var in enumerate(self.what):
paramDict['Variable Name ' + str(index) + ' :'] = var
return paramDict

Expand All @@ -91,35 +142,7 @@ def initialize(self, inDict):
@ Out, None
"""
# the linking to the source is performed in the base class initialize method
OutStreamManager.initialize(self, inDict)

def localReadXML(self, xmlNode):
"""
This Function is called from the base class, It reads the parameters that
belong to a plot block (outputs by filling data structure (self))
@ In, xmlNode, xml.etree.ElementTree.Element, Xml element node
@ Out, None
"""
self.type = 'OutStreamPrint'
for subnode in xmlNode:
if subnode.tag not in ['type','source','what','filename','target','clusterLabel','directory']:
self.raiseAnError(IOError, ' Print Outstream object ' + str(self.name) + ' contains the following unknown node: ' + str(subnode.tag))
if subnode.tag == 'source':
self.sourceName = subnode.text.split(',')
elif subnode.tag == 'filename':
self.filename = subnode.text
else:
self.options[subnode.tag] = subnode.text
if 'type' not in self.options.keys():
self.raiseAnError(IOError, 'type tag not present in Print block called ' + self.name)
if self.options['type'] not in self.availableOutStreamTypes:
self.raiseAnError(TypeError, 'Print type ' + self.options['type'] + ' not available yet. ')
if 'what' in self.options.keys():
self.what = self.options['what']
if self.options['type'] == 'csv':
for elm in self.what.lower().split(","):
if not elm.startswith("input") and not elm.startswith("output") and not elm.startswith("metadata"):
self.raiseAnError(IOError, 'Not recognized request in "what" node <'+elm.strip()+'>. The request must begin with one of "input", "output" or "metadata" or it could be "all" for ROMs!')
OutStreamBase.initialize(self, inDict)

def addOutput(self):
"""
Expand Down
Loading

0 comments on commit 1ce4703

Please sign in to comment.