Commit 5e963e2f authored by Salko Jr, Robert's avatar Salko Jr, Robert
Browse files

Add new plotting scripts

Add scripts for plotting coupling mesh data and add some tests.
Also refactor the tests so they can be run in parallel.
Make a small update to the documentation.
parent 40f2d244
Loading
Loading
Loading
Loading
+140 −0
Original line number Diff line number Diff line
@@ -135,6 +135,18 @@ class Hdf5Interface(object):
        if pin < 1 or pin > me.getNumPin():
            raise ValueError("Pin index "+str(pin)+" is not found in the model")

    def _pinHasCouplingData(me, pin):
       """ Checks if the pin has coupling mesh data """
       if 'reconstruction' in me.h5['CORE/mesh/PIN_{:05d}'.format(pin)]:
          if 'axial_nodes' not in me.h5['CORE/mesh/PIN_{:05d}/reconstruction'.format(pin)]:
             return False
          elif 'azimuthal_nodes' not in me.h5['CORE/mesh/PIN_{:05d}/reconstruction'.format(pin)]:
             return False
          else:
             return True
       else:
          return False

    def _pinLevelValid(me, pin, level):
        """ Used for checking validity of pin level """
        me._pinIndexValid(pin)
@@ -201,6 +213,27 @@ class Hdf5Interface(object):
        me._pinIndexValid(pin)
        return me.pinNumAxial[pin]

    def getPinDz(me, pin):
        """ Returns a vector of the heights of each level in the pin mesh

        Args:
           pin (int) : Pin index (1-based)

        """
        me._pinIndexValid(pin)
        return me.h5['/CORE/mesh/PIN_{:05d}/axial_heights'.format(pin)][()]

    def getPinZ(me, pin):
       """ Returns a vector of the axial locations of each level in the pin mesh

       Args:
          pin (int) : Pin index (1-based)

       """
       me._pinIndexValid(pin)
       return me.h5['/CORE/mesh/PIN_{:05d}/axial_nodes'.format(pin)][()]


    def getChanArea(me, ch):
        """ Returns the nominal channel flow area [m**2]

@@ -222,6 +255,19 @@ class Hdf5Interface(object):
        section = me.getPinSection(pin, level)
        return me.h5['CORE/mesh/PIN_{:05d}/azimuthal_fraction'.format(pin)][section-1, :][()]

    def getDatasetUnits(me, dataset):
        """ Returns the units for a passed dataset name in the file.

        Returns None if no units are specified.  Throws error if dataset does not exist.

        Args:
           dataset (str) : Valid dataset name in the h5 file

        """
        me._datasetValid(dataset)
        if 'physical_units' in me.h5['STATE_0001/'+dataset].attrs:
           return me.h5['STATE_0001/'+dataset].attrs['physical_units'][0]

    def getChanDatasetNumLev(me, chan, dataset):
        """ Returns the number of axial elements in this channel dataset

@@ -458,6 +504,100 @@ class Hdf5Interface(object):
        s = me.getPinNumSector(pin)
        return me.h5['/STATE_{:04d}/{:s}'.format(state, dataset)][0:n, 0:s, pin-1][()]

    def getPinSurfaceTemp(me, state, pin):
        """ Returns a 2D array containing the solid surface temperature distribution

        The solid temperature dataset contains the full 3D temperature distribution of
        each solid.  This returns just the surface temperature distribution as a 2D array
        which takes (level, azimuthal index).

        Args:
            state (int) : The state number (1-based)
            pin (int) : The pin index (1-based)

        """

        me._stateValid(state)
        me._pinIndexValid(pin)
        n = me.pinNumAxial[pin]
        s = me.getPinNumSector(pin)
        return me.h5['/STATE_{:04d}/pin_temp'][0:s, 0:n, -1, pin-1][()]

    def getCouplingThetaBounds(me, dataset, pin):
       """ Returns the boundaries of the azimuthal sectors in the reconstruction mesh

       Must have a reconstruction dataset present or returns None

       Args:
          dataset (str) : Name of the dataset in the file
          pin (int) : Pin index (1-based)
       """
       me._datasetValid(dataset)
       me._pinIndexValid(pin)
       if me._pinHasCouplingData(pin):
           azimuthal_nodes = me.h5['/CORE/mesh/PIN_{:05d}/reconstruction/azimuthal_nodes'.format(pin)][()]
           # Assume the azimuthal mesh is uniform
           daz = azimuthal_nodes[1]-azimuthal_nodes[0]
           bounds = [azimuthal_nodes[0]-daz/2]
           for a in azimuthal_nodes:
              bounds.append(bounds[-1]+daz)
           return bounds
       else:
           return None

    def getCouplingAxialBounds(me, dataset, pin):
       """ Returns the boundaries of the axial levels in the reconstruction mesh

       Must have a reconstruction dataset present or returns None

       Args:
          dataset (str) : Name of the dataset in the file
          pin (int) : Pin index (1-based)
       """
       me._datasetValid(dataset)
       me._pinIndexValid(pin)
       if me._pinHasCouplingData(pin):
           # Assume that each CTF level will be subdivided equally
           # Use the reconstruction axial mesh to figure out how many times they are divided
           axial_nodes = me.h5['/CORE/mesh/PIN_{:05d}/reconstruction/axial_nodes'.format(pin)][()]
           base_dz = me.h5['/CORE/mesh/PIN_{:05d}/axial_heights'.format(pin)][()]
           assert(len(axial_nodes)%len(base_dz)==0)
           numDiv = len(axial_nodes)/len(base_dz)
           bounds = [axial_nodes[0]]
           for dz in base_dz:
              mini_dz = dz/numDiv
              for j in range(numDiv):
                 bounds.append(bounds[-1]+mini_dz)
           return bounds
       else:
           return None

    def getCouplingDatasetAndMesh(me, dataset, state, pin):
        """ Returns both the selected dataset and its mesh

        A coupling dataset is one which the grid effects multipliers are applied to.
        This dataset has associated azimuthal locations.  If the pin does not have coupling
        data, None is returned for each return value.

        Args:
             dataset (str) : The name of the dataset
             state (int) : The state number (1-based)
             pin (int) : The pin index (1-based)

        Returns:
           axial_nodes (float) : Vector of axial node locations
           azimuthal_nodes (float) : Vector of azimuthal locations
           dataset (float) : 2D array of the data (takes level, sector)
        """
        me._stateValid(state)
        me._datasetValid(dataset)
        me._pinIndexValid(pin)
        if me._pinHasCouplingData(pin):
           axial_nodes = me.h5['/CORE/mesh/PIN_{:05d}/reconstruction/axial_nodes'.format(pin)][()]
           azimuthal_nodes = me.h5['/CORE/mesh/PIN_{:05d}/reconstruction/azimuthal_nodes'.format(pin)][()]
           return (axial_nodes, azimuthal_nodes, me.h5['/STATE_{:04d}/{:s}'.format(state, dataset)][0:len(axial_nodes), 0:len(azimuthal_nodes), pin-1][()])
        else:
           return (None, None, None)

    def getAveTemp(me, section=1, level=None, state=1):
        """ Returns the average temperature (flow weighted)
+69 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from Hdf5Tools import Hdf5Interface
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import numpy as np

class PlotTools(object):
   """ A class for making plots of SubKit HDF5 file data
@@ -232,3 +233,71 @@ class PlotTools(object):
       """
       me._chanPlotMaxWrapper('massflux_tot', "Mass flux [kg/m**2/s]", state, ch, figname)


   def plotPinCoupling(me, pin, dataset, state=None, figname=None, axialBounds=None, maxTemp=None):
      """ Makes a contour plot of rod surface data

      Passed dataset must be a coupling dataset (has prefix of "coupling")

      Args:
         pin (int) : Pin index to plot (1-based)
         dataset (str) : Name of dataset in HDF5 file (must be a valid name in file)
         state (int) : State number to plot (will select last by default)
         figname (str) : Name of the figure (will default if not provided)
         axialBounds (float) : List of 2 values.  Specify min and max axial locations to include
            in plot.  Specify "None" to use the dataset min or max.  First value is the min and
            second is the max.
         maxTemp (float) : Maximum temp to show in contour plot [C]

      """
      if state is None:
         state_ = me.h5obj.getNumState()
      else:
         state_ = state
      axial, azimuthal, data = me.h5obj.getCouplingDatasetAndMesh(dataset, state_, pin)
      units = me.h5obj.getDatasetUnits(dataset)

      location = "S{:d}_P{:d}_{:s}".format(state_, pin, dataset)
      if figname is None:
         figname_ = location+'.png'
      else:
         figname_ = figname

      if axialBounds is None:
         axialBounds_ = [None, None]
      else:
         assert(len(axialBounds)==2)
         assert((isinstance(axialBounds[0], float) or axialBounds[0] is None) and (isinstance(axialBounds[1], float) or axialBounds[1] is None))
         axialBounds_ = axialBounds

      if axialBounds_[0] is None:
         axialBounds_[0] = min(axial)

      if axialBounds_[1] is None:
         axialBounds_[1] = max(axial)

      if maxTemp is None:
         maxTemp_ = np.max(data)
      else:
         maxTemp_ = maxTemp
      minTemp_ = np.min(data)

      fig, ax = plt.subplots()
      plt.title(location)
      ax.set_xlabel('Azimuthal location [rad]')
      ax.set_ylabel('Axial location [m]')
      ax.set_ylim(axialBounds_)
      #v = np.linspace(minTemp_, maxTemp_, 7, endpoint=True)
      #c1 = ax.contourf(azimuthal, axial, data, v)
      #cbar = plt.colorbar(c1)
      im = ax.pcolor(azimuthal, axial, data, vmin=minTemp_, vmax=maxTemp_)
      ax.grid()
      cbar = fig.colorbar(im)
      dataset = dataset.replace('_',' ')
      if units:
         units_ = ' ['+units+']'
      else:
         units_ = ""
      cbar.ax.set_ylabel(dataset+units_)
      plt.savefig(figname_, dpi=150)
      plt.close(fig)
+20 −0
Original line number Diff line number Diff line
import argparse
from PlotTools import PlotTools

def main():
   parser = argparse.ArgumentParser(description="Plot the surface TKE from the coupling mesh (file must contain the coupling mesh surface TKE dataset).")
   parser.add_argument('h5name', type=str, help="HDF5 file")
   parser.add_argument('pin', type=int, help="Pin to plot.")
   parser.add_argument('--state', type=int, nargs="?", help="State to plot.  Defaults to last state.")
   parser.add_argument('--figname', type=str, nargs="?", help="Name of figure.  Defaults to name based on location.")
   parser.add_argument('--min_axial', type=float, help="Lower axial bound of plot (defaults to minimum axial location).")
   parser.add_argument('--max_axial', type=float, help="Upper axial bound of plot (defaults to maximum axial location).")
   parser.add_argument('--max', type=float, help="Upper bound for data")
   args = parser.parse_args()

   plotter = PlotTools(args.h5name)

   plotter.plotPinCoupling(args.pin, 'coupling_surface_tke', args.state, args.figname, [args.min_axial, args.max_axial], args.max)

if __name__=='__main__':
   main()
+20 −0
Original line number Diff line number Diff line
import argparse
from PlotTools import PlotTools

def main():
   parser = argparse.ArgumentParser(description="Plot the surface temperature from the coupling mesh (dataset must contain the coupling mesh surface temperatures).")
   parser.add_argument('h5name', type=str, help="HDF5 file")
   parser.add_argument('pin', type=int, help="Pin to plot.")
   parser.add_argument('--state', type=int, nargs="?", help="State to plot.  Defaults to last state.")
   parser.add_argument('--figname', type=str, nargs="?", help="Name of figure.  Defaults to name based on location.")
   parser.add_argument('--min_axial', type=float, help="Lower axial bound of plot (defaults to minimum axial location).")
   parser.add_argument('--max_axial', type=float, help="Upper axial bound of plot (defaults to maximum axial location).")
   parser.add_argument('--max', type=float, help="Upper bound for temperature data [C]")
   args = parser.parse_args()

   plotter = PlotTools(args.h5name)

   plotter.plotPinCoupling(args.pin, 'coupling_surface_temperature', args.state, args.figname, [args.min_axial, args.max_axial], args.max)

if __name__=='__main__':
   main()
+2 −0
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@ setup(name='SubKit',
            'skplot_pin_dnbr=SubKit.process.plotPinDNBR:main',
            'skplot_chan_void=SubKit.process.plotChanVoid:main',
            'skplot_chan_quality=SubKit.process.plotChanQuality:main',
            'skplot_pin_coupling_tke=SubKit.process.plotCouplingSurfTKE:main',
            'skplot_pin_coupling_temp=SubKit.process.plotCouplingSurfTemp:main',
            'sk_gen_from_template=SubKit.utils.gen_from_template:main',
            'sksummary_dnb=SubKit.process.genDNBSummary:main',]
      },
Loading