Skip to content

Commit

Permalink
Mandd/pareto frontier pp (#1196)
Browse files Browse the repository at this point in the history
Addition of PARETO FRONTIER post processor
  • Loading branch information
mandd authored Apr 21, 2020
1 parent a27882a commit ade7321
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 2 deletions.
55 changes: 54 additions & 1 deletion doc/user_manual/postprocessor.tex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ \subsection{PostProcessor}
\item \textbf{ValueDuration}
\item \textbf{FastFourierTransform}
\item \textbf{SampleSelector}
\item \textbf{MCSImporter}
%\item \textbf{PrintCSV}
%\item \textbf{LoadCsvIntoInternalObject}
\end{itemize}
Expand Down Expand Up @@ -1803,10 +1804,62 @@ \subsubsection{RavenOutput}
\end{lstlisting}
The resulting PointSet has \emph{time} as an input and \emph{first} as an output.
%%%%%%%%%%%%%% ParetoFrontier PP %%%%%%%%%%%%%%%%%%%
\subsubsection{ParetoFrontier}
\label{ParetoFrontierPP}
The \textbf{ParetoFrontier} post-processor is designed to identify the points lying on the Pareto Frontier in a cost-value space.
This post-processor receives as input a \textbf{DataObject} (a PointSet only) which contains all data points in the cost-value space and it
returns the subset of points lying in the Pareto Frontier as a PointSet.
It is here assumed that each data point of the input PointSet is a realization of the system under consideration for a
specific configuration to which corresponds a cost and a value.
%
\ppType{ParetoFrontier}{ParetoFrontier}
%
\begin{itemize}
\item \xmlNode{costID},\xmlDesc{string, required parameter}, ID of the input PointSet variable that is considered the cost variable
\item \xmlNode{valueID},\xmlDesc{string, required parameter}, ID of the input PointSet variable that is considered the value variable
\end{itemize}
The following is an example where a set of realizations (the ``candidates'' PointSet) has been generated by changing two parameters
(var1 and var2) which produced two output variables (cost and value).
The \textbf{ParetoFrontier} post-processor takes the ``candidates'' PointSet and populates a Point similar in structure
(the ``paretoPoints'' PointSet).
\textbf{Example:}
\begin{lstlisting}[style=XML,morekeywords={anAttribute},caption=ParetoFrontier input example (no expand)., label=lst:ParetoFrontier_PP_InputExample]
<Models>
<PostProcessor name="paretoPP" subType="ParetoFrontier">
<costID>cost</costID>
<valueID>value</valueID>
</PostProcessor>
</Models>
<Steps>
<PostProcess name="PP">
<Input class="DataObjects" type="PointSet" >candidates</Input>
<Model class="Models" type="PostProcessor" >paretoPP</Model>
<Output class="DataObjects" type="PointSet" >paretoPoints</Output>
</PostProcess>
</Steps>
<DataObjects>
<PointSet name="candidates">
<Input>var1,var2</Input>
<Output>cost,value</Output>
</PointSet>
<PointSet name="paretoPoints">
<Input>var1,var2</Input>
<Output>cost,value</Output>
</PointSet>
</DataObjects>
\end{lstlisting}
%%%%%%%%%%%%%% MCSImporter PP %%%%%%%%%%%%%%%%%%%
\subsubsection{MCS Importer}
\subsubsection{MCSImporter}
\label{MCSimporterPP}
The \textbf{MCSImporter} post-processor has been designed to import Minimal Cut Sets (MCSs) into RAVEN.
This post-processor reads a csv file which contain the list of MCSs and it save this list as a DataObject
Expand Down
131 changes: 131 additions & 0 deletions framework/PostProcessors/ParetoFrontierPostProcessor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Copyright 2017 Battelle Energy Alliance, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Created on March 25, 2020
@author: mandd
"""

#External Modules---------------------------------------------------------------
import numpy as np
#External Modules End-----------------------------------------------------------

#Internal Modules---------------------------------------------------------------
from .PostProcessor import PostProcessor
from utils import utils
from utils import InputData, InputTypes
import Runners
#Internal Modules End-----------------------------------------------------------

class ParetoFrontier(PostProcessor):
"""
This postprocessor selects the points that lie on the Pareto frontier
The postprocessor acts only on PointSet and return a subset of such PointSet
"""

@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 input of cls.
"""
inputSpecification = super(ParetoFrontier, cls).getInputSpecification()
inputSpecification.addSub(InputData.parameterInputFactory('costID' , contentType=InputTypes.StringType))
inputSpecification.addSub(InputData.parameterInputFactory('valueID', contentType=InputTypes.StringType))
return inputSpecification

def _localReadMoreXML(self, xmlNode):
"""
Function to read the portion of the xml input that belongs to this specialized class
and initialize some stuff based on the inputs got
@ In, xmlNode, xml.etree.Element, Xml element node
@ Out, None
"""
paramInput = ParetoFrontier.getInputSpecification()()
paramInput.parseNode(xmlNode)
self._handleInput(paramInput)

def _handleInput(self, paramInput):
"""
Function to handle the parsed paramInput for this class.
@ In, paramInput, ParameterInput, the already-parsed input.
@ Out, None
"""
costID = paramInput.findFirst('costID')
self.costID = costID.value
valueID = paramInput.findFirst('valueID')
self.valueID = valueID.value

def inputToInternal(self, currentInp):
"""
Method to convert an input object into the internal format that is
understandable by this pp.
In this case, we only want data objects!
@ In, currentInp, list, an object that needs to be converted
@ Out, currentInp, DataObject.HistorySet, input data
"""
if len(currentInp) > 1:
self.raiseAnError(IOError, 'ParetoFrontier postprocessor {} expects one input DataObject, but received {} inputs!".'
.format(self.name,len(currentInp)))
currentInp = currentInp[0]
if currentInp.type not in ['PointSet']:
self.raiseAnError(IOError, 'ParetoFrontier postprocessor "{}" requires a DataObject input! Got "{}".'
.format(self.name, currentInput.type))
return currentInp

def run(self, inputIn):
"""
This method executes the postprocessor action.
@ In, inputIn, DataObject, point set that contains the data to be processed
@ Out, paretoFrontierDict, dict, dictionary containning the Pareto Frontier information
"""
inData = self.inputToInternal(inputIn)
data = inData.asDataset()
sortedData = data.sortby(self.costID)

coordinates = np.zeros(1,dtype=int)
for index,elem in enumerate(sortedData[self.costID].values):
if (index>1) and (sortedData[self.valueID].values[index]>sortedData[self.valueID].values[coordinates[-1]]):
coordinates = np.append(coordinates,index)

selection = sortedData.isel(RAVEN_sample_ID=coordinates).to_array().values
paretoFrontierData = np.transpose(selection)

paretoFrontierDict = {}
for index,varID in enumerate(sortedData.data_vars):
paretoFrontierDict[varID] = paretoFrontierData[:,index]
return paretoFrontierDict

def collectOutput(self, finishedJob, output):
"""
Function to place all of the computed data into the output object
@ In, finishedJob, JobHandler External or Internal instance, A JobHandler object that is in charge of running this post-processor
@ In, output, DataObject.DataObject, The object where we want to place our computed results
@ Out, None
"""
evaluation = finishedJob.getEvaluation()

outputDict ={}
outputDict['data'] = evaluation[1]

if output.type in ['PointSet']:
outputDict['dims'] = {}
for key in outputDict.keys():
outputDict['dims'][key] = []
output.load(outputDict['data'], style='dict', dims=outputDict['dims'])
else:
self.raiseAnError(RuntimeError, 'ParetoFrontier failed: Output type ' + str(output.type) + ' is not supported.')
4 changes: 3 additions & 1 deletion framework/PostProcessors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from .DataClassifier import DataClassifier
from .ComparisonStatisticsModule import ComparisonStatistics
from .RealizationAverager import RealizationAverager
from .ParetoFrontierPostProcessor import ParetoFrontier
from .MCSimporter import MCSImporter
# from .RavenOutput import RavenOutput # deprecated for now

Expand Down Expand Up @@ -88,5 +89,6 @@
'DataClassifier',
'SampleSelector',
'ETImporter',
'RealizationAverager'] + additionalModules
'RealizationAverager',
'ParetoFrontier'] + additionalModules
# 'RavenOutput', # deprecated for now
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var1,var2,cost,value
0.610859173,2.654360106,0.119184948,0.258905327
1.000062354,0.378710223,0.55643265,0.566231764
0.192283951,0.689577052,0.060905454,0.18054485
2.540589393,1.903305493,0.687883548,0.813536866
0.379652948,3.670016433,0.71839606,0.066388849
0.018649501,2.419989092,0.848137196,0.132795653
1.330707892,3.30304347,0.920517482,0.016587155
1.124432133,1.292992123,0.569248014,0.470709174
0.113252566,1.809448753,0.158429805,0.058030974
0.791262635,2.240760603,0.784246453,0.37114112
2.301074231,3.766843205,0.755648003,0.731028739
1.586862706,2.728372649,0.540216204,0.109898232
0.7791167,0.166729443,0.733693488,0.090933319
2.089949241,2.85512794,0.81212754,0.897978379
1.4476716,1.507081576,0.590949365,0.670322915
1.086484674,3.14355567,0.433299584,0.08645684
0.211447941,0.001989369,0.29248432,0.513449866
2.202997279,0.434109143,0.511187573,0.330193147
1.915360888,1.476389367,0.943463507,0.882958351
2.061818372,0.278500517,0.006895977,0.071068495
0.216133506,0.320616331,0.044354112,0.068657568
2.713265808,2.358974745,0.522916259,0.126625517
0.596297114,2.368502385,0.846362805,0.386032272
1.655405307,0.816356271,0.93359994,0.329798081
0.77173529,3.400705844,0.561187856,0.375795736
0.5696295,1.055822709,0.873151817,0.160984792
0.828752896,1.715167593,0.520305981,0.719334099
2.719657094,0.486305191,0.133546208,0.202038808
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
var1,var2,cost,value
2.061818372,0.278500517,0.006895977,0.071068495
0.192283951,0.689577052,0.060905454,0.18054485
0.610859173,2.654360106,0.119184948,0.258905327
0.211447941,0.001989369,0.29248432,0.513449866
0.828752896,1.715167593,0.520305981,0.719334099
2.540589393,1.903305493,0.687883548,0.813536866
2.089949241,2.85512794,0.81212754,0.897978379
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<Simulation verbosity="debug">
<TestInfo>
<name>framework/PostProcessors/ParetoFrontierPostProcessor.ParetoFrontier</name>
<author>mandd</author>
<created>2020-03-25</created>
<classesTested>Models.PostProcessors.ParetoFrontier</classesTested>
<description>
This post-processor identifies the points lying on the Pareto Frontier in a cost-value space
</description>
</TestInfo>

<Files>
<Input name="data" type="">data.csv</Input>
</Files>

<RunInfo>
<WorkingDir>ParetoFrontier</WorkingDir>
<Sequence>LoadPS,PP</Sequence>
<batchSize>1</batchSize>
</RunInfo>

<Models>
<PostProcessor name="paretoPP" subType="ParetoFrontier">
<costID>cost</costID>
<valueID>value</valueID>
</PostProcessor>
</Models>

<Steps>
<IOStep name='LoadPS'>
<Input class='Files' type='' >data</Input>
<Output class='DataObjects' type='PointSet' >candidates</Output>
</IOStep>
<PostProcess name="PP">
<Input class="DataObjects" type="PointSet" >candidates</Input>
<Model class="Models" type="PostProcessor" >paretoPP</Model>
<Output class="DataObjects" type="PointSet" >paretoPoints</Output>
<Output class="OutStreams" type="Print" >PrintPareto</Output>
</PostProcess>
</Steps>

<OutStreams>
<Print name="PrintPareto">
<type>csv</type>
<source>paretoPoints</source>
</Print>
</OutStreams>

<DataObjects>
<PointSet name="candidates">
<Input>var1,var2</Input>
<Output>cost,value</Output>
</PointSet>
<PointSet name="paretoPoints">
<Input>var1,var2</Input>
<Output>cost,value</Output>
</PointSet>
</DataObjects>

</Simulation>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Tests]
[./ParetoFrontier]
type = 'RavenFramework'
input = 'test_paretoFrontier.xml'
csv = 'ParetoFrontier/PrintPareto.csv'
[../]
[]


0 comments on commit ade7321

Please sign in to comment.