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

Add autodoc tool for the user manual

This creates a python tool that will extract information from the
inupt.sch file to create a user manual for the input file.  The
input.sch needs to be updated to include descriptions for all input
parameters for this to be compelte.  I also updated the input file to be
relevant for the new input file format.
parent a2b55be0
Loading
Loading
Loading
Loading
+0 −37
Original line number Diff line number Diff line
@@ -24,43 +24,6 @@ from wasp2py import get_json_dict, get_wasp_utility_path
class InputError(Exception):
   pass

def _extraKeys(dic, keys):
    """ Returns keys that exist in the dict that do not exist in the keys list

    Args:
       dic (dict): Dictionary to be checked
       keys (list): List of keys to check

    Returns:
       List of keys in the dict that were not in the keys list
    """
    assert(isinstance(dic, dict))
    assert(isinstance(keys, list))
    extra = []
    for key in dic.keys():
       if key not in keys:
          extra.append(key)
    return extra

def _missingKeys(dic, keys):
    """ Returns a list of keys missing from the dictionary

    Args:
       dic (dict): The dictionary to be checked
       keys (list): List of keys to check in the dictionary.

    Returns:
       List of keys that were in the keys list but are not in the dict
    """
    assert(isinstance(dic, dict))
    assert(isinstance(keys, list))
    missing = []
    for key in keys:
      if key not in dic:
         missing.append(key)
    return missing


class InpParse(object):
    """ Parses the input file

+45 −23
Original line number Diff line number Diff line
solution{
   Description="Declares solution type to solve."
   ### Appearance="Optional. Defaults to steady-state solve.  If specified, must enter at least one child."
   MinOccurs=0
   MaxOccurs=1
   ChildExactlyOne=[steady, transient]
   steady{
      Description="Specify this group to model a steady-state solution."
      ### Appearance="Optional."
      MinOccurs=0
      MaxOccurs=1
      p_l2_norm{
         Description="Stopping criteria for the l2 norm of relative pressure changes between solution checkpoints."
         ### Appearance="Optional."
         ### Restrictions=">0.0"
         MinOccurs=0
         MaxOccurs=1
         value{
            MinOccurs=1
            MaxOccurs=1
            ValType=Real
            MinValExc=0.0
         }
      }
      p_linf_norm{
         Description="Stopping criteria for the l:math:`\infty`-norm of relative pressure changes between solution checkpoints."
         ### Appearance="Optional."
         ### Restrictions=">0.0"
         MinOccurs=0
         MaxOccurs=1
         value{
@@ -130,6 +141,7 @@ solution{
         MinOccurs=0
         MaxOccurs=NoLimit
         dtmin{
            ### Units="s"
            MinOccurs=0
            MaxOccurs=1
            value{
@@ -247,16 +259,20 @@ initial{

# IDs have to go from 1 to N without skips.  This ensures 1 and N are there.
# Waiting on wasp feature for the coniguous part.
ChildExactlyOne=["ch/id/value"=1]
ChildUniqueness="ch/id/value"
ch{
   MinOccurs=1
   MaxOccurs=NoLimit
   ChildExactlyOne=["/ch/id/value"=1]
   id{
      MinValInc=1
      MinOccurs=1
      MaxOccurs=1
      value{
         ValType=Int
      ChildUniqueness="/ch/id"
         MinValInc=1
         MinOccurs=1
         MaxOccurs=1
      }
   }
   ChildExactlyOne=[area, geo]
   ChildExactlyOne=[pw, geo]
@@ -408,15 +424,15 @@ ch{

# Need to ensure there are no duplicate section ids
# Need to ensure there are no skipped section ids
ChildExactlyOne=["section/id/value"=1]
ChildUniqueness="section/id"
section{
   MinOccurs=1
   MaxOccurs=NoLimit
   ChildExactlyOne=["/section/id/value"=1]
   id{
      MinOccurs=1
      MaxOccurs=1
      ValType=Int
      ChildUniqueness="/section/id"
   }
   ChildExactlyOne=[num_levels, dz]
   ChildExactlyOne=[height, dz]
@@ -453,10 +469,11 @@ section{
   }
}

ChildUniqueness="gap/id/value"
ChildAtMostOne=["gap/id/value"=1]
gap{
   MinOccurs=0
   MaxOccurs=NoLimit
   ChildExactlyOne=["/gap/id/value"=1]
   id{
      MinOccurs=1
      MaxOccurs=1
@@ -464,7 +481,6 @@ gap{
         MinOccurs=1
         MaxOccurs=1
         ValType=Int
         ChildUniqueness="/gap/id/value"
      }
   }
   chconn{
@@ -496,18 +512,18 @@ gap{
   }
}

ChildAtMostOne=["solid_geo/id/value"=1]
ChildUniqueness="solid_geo/id"
solid_geo{
   MinOccurs=0
   MaxOccurs=NoLimit
   id{
      MinOccurs=1
      MaxOccurs=1
      ChildExactlyOne=["/solid_geo/id/value"=1]
      value{
         MinOccurs=1
         MaxOccurs=1
         ValType=Int
         ChildUniqueness="/solid_geo/id"
      }
   }
   type{
@@ -559,19 +575,19 @@ solid_geo{
   }
}

ChildAtMostOne=["solid/id"=1]
ChildUniqueness="solid/id"
solid{
   MinOccurs=0
   MaxOccurs=NoLimit
   id{
      MinOccurs=1
      MaxOccurs=1
      ChildExactlyOne=["/solid/id"=1]
      value{
         MinOccurs=1
         MaxOccurs=1
         MinValInc=1
         ValType=Int
         ChildUniqueness="/solid/id"
      }
   }
   geo{
@@ -594,7 +610,7 @@ solid{
         # Every id in this list must match a declared channel ID
         ExistsIn="/ch/id/value"
         # The length of this list must match that of pct
         ChildCountEqual(EvenNone)="../pct"
         ChildCountEqual(EvenNone)="../../pct"
         ValType=Int
      }
   }
@@ -605,7 +621,7 @@ solid{
         MinOccurs=1
         MaxOccurs=NoLimit
         # This list size must match that of the ch list
         ChildCountEqual(EvenNone)="../ch"
         ChildCountEqual(EvenNone)="../../chconn"
         ValType=Real
      }
   }
@@ -622,18 +638,18 @@ solid{
   }
}

ChildAtMostOne=["axialpow/id/value"=1]
ChildUniqueness="axialpow/id"
axialpow{
   MinOccurs=0
   MaxOccurs=NoLimit
   id{
      MinOccurs=1
      MaxOccurs=1
      ChildExactlyOne=["/axialpow/id/value"=1]
      value{
         MinOccurs=1
         MaxOccurs=1
         ValType=Int
         ChildUniqueness="/axialpow/id"
      }
   }
   z{
@@ -644,7 +660,7 @@ axialpow{
         MinOccurs=1
         MaxOccurs=NoLimit
         ValType=Real
         ChildCountEqual(EvenNone)="../pow"
         ChildCountEqual(EvenNone)="../../pow"
      }
   }
   # The user may either specify z/pow if not transient, or z and a series of time groups
@@ -657,7 +673,7 @@ axialpow{
         MinOccurs=1
         MaxOccurs=NoLimit
         ValType=Real
         ChildCountEqual(EvenNone)="../z/value"
         ChildCountEqual(EvenNone)="../../z/value"
      }
   }
   time{
@@ -676,7 +692,7 @@ axialpow{
            MinOccurs=1
            MaxOccurs=NoLimit
            ValType=Real
            ChildCountEqual(EvenNone)="../../z/value"
            ChildCountEqual(EvenNone)="../../../z/value"
         }
      }

@@ -685,7 +701,11 @@ axialpow{
}

loss{
   MinOccurs=0
   MaxOccurs=NoLimit
   Description="Specifies form loss information for a location or set of locations in the model."
   chans{
      Description="This may be a single channel or a list of channels to which the form losses defined in this 'loss' group will be applied."
      MinOccurs=0
      MaxOccurs=1
      value{
@@ -698,6 +718,7 @@ loss{
   levels{
      MinOccurs=1
      MaxOccurs=1
      Description="This may be a single level or a list of levels to which the form losses defined in this 'loss' group will be applied."
      value{
         MinOccurs=1
         MaxOccurs=NoLimit
@@ -708,6 +729,7 @@ loss{
   k{
      MinOccurs=1
      MaxOccurs=1
      Description="This may be a single form loss or a list of form losses for this group.  If a list is given, the size must match the size of the 'levels' list."
      value{
         MinOccurs=1
         MaxOccurs=NoLimit
@@ -718,10 +740,11 @@ loss{
   }
}

ChildAtMostOne=["domain/id/value"=1]
ChildUniqueness="domain/id/value"
domain{
   MinOccurs=0
   MaxOccurs=NoLimit
   ChildExactlyOne=["/domain/id/value"=1]
   id{
      MinOccurs=1
      MaxOccurs=1
@@ -729,7 +752,6 @@ domain{
         MinOccurs=1
         MaxOccurs=1
         ValType=Int
         ChildUniqueness="/domain/id/value"
         #IncreaseOver("../..")=Strict
      }
   }
@@ -750,7 +772,7 @@ domain{
         MinOccurs=1
         MaxOccurs=NoLimit
         ValType=Int
         ChildCountEqual(EvenNone)="../solid_owners"
         ChildCountEqual(EvenNone)="../../solid_owners"
         ExistsIn="/solid/id/value"
      }
   }
@@ -761,7 +783,7 @@ domain{
         MinOccurs=1
         MaxOccurs=NoLimit
         ValType=Int
         ChildCountEqual(EvenNone)="../solids"
         ChildCountEqual(EvenNone)="../../solids/value"
         ExistsIn="/domain/id/value"
      }
   }
+1 −0
Original line number Diff line number Diff line
@@ -17,4 +17,5 @@ help:
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	python docprint.py
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+28 −22
Original line number Diff line number Diff line
.. _ctf_basics:

==============================
##############################
Basics of building a CTF model
==============================
##############################

This section is intended to cover the fundamentals of how a model is constructed in CTF and
introduce terminology that will be used in the remainder of the documentation.
@@ -11,8 +11,9 @@ are connected by "gaps" in the lateral direction. The user is responsible for d
they would like to discretize the domain they intend to model and then add channels one-by-one
to form the mesh in the lateral direction of the domain.

********
Channels
--------
********

The user has flexibility in determining how to define a channel.
Subchannel codes are traditionally used to model rod-bundle geometry with flow primarily
@@ -48,8 +49,9 @@ In fact, the domain to be modeled may not be a rod bundle geometry at all. The
CTF does not include an advanced turbulence model or even viscous shear between channels, so it is best
to orgainze channels so that they are somewhat confined at their boundaries.

****
Gaps
----
****

After defining the channels in the model, the user can define gaps to connect them in the lateral
direction.  It is not a requirement, but there will be no flow of mass, momentum, or energy between
@@ -65,8 +67,9 @@ Example of a gap between two adjacent subchannels
Gaps can also lump several physical gaps into one.  The geometry of one gap is specified and a multiplier
is applied to denote how many physical gaps it represents in this case.

********
Sections
--------
********

Up until this point, models were presented in 2D form; however, CTF models 3D geometry.  Therefore,
an axial mesh must also be supplied.  Prior to discussing the axial mesh in the channels, it is necessary
@@ -88,8 +91,9 @@ It is actually a requirement for channels to be organized into sections, so even
connecting in the axial direction, a single section must first be created and channels are then added to
the section.

*******************
Boundary conditions
-------------------
*******************

While it is not necessary to declare any boundary conditions in a model (boundary values will take on
reference pressure, reference temperature, and initial mass flow rate by default), it is likely they will
@@ -123,8 +127,9 @@ It is important to note that the default boundary condition is "no boundary cond
may move freely across the boundary, so if a user intends for flow not to cross a plane, they must
specify a mass flow rate boundary condition at that location with a value of zero flow.

******
Solids
------
******

Like most subchannel codes, CTF also allows the user to define solid objects in the model.  The process
of adding solids is a two-step process which involves first declaring the geometry of the solid and then
@@ -148,7 +153,8 @@ Channel 1 connects to 25% of the rod, Channel 2 connects to 25% of the rod, and
.. image:: media/rod_connection.png
   :align: center

***************
Parallelization
---------------
***************

doc/docprint.py

0 → 100644
+120 −0
Original line number Diff line number Diff line
f = open('../SMBuilder/input.sch')

attr = ["ChildExactlyOne",
        "ChildCountEqual",
        "ExistsIn", "ChildUniqueness", "ChildAtMostOne", "IncreaseOver",
        "ChildAtLeastOne"]
names = []
group = []
descriptions = {}
minOccurs = {}
appearance = {}
units = {}
types = {}
restrictions = {}
minOccurs = {}
maxOccurs = {}

indent = 0
for line in f.readlines():
    attrLine = False
    for param in attr:
       if param in line:
           attrLine = True
    if attrLine:
        continue
    elif line.lstrip().startswith("###"):
        splitline = line.split("###")[1].strip()
        if "Appearance" in splitline:
            appearance[name+str(group[-1])] = splitline.split("=")[1].strip()[1:-1]
        elif "Units" in splitline:
            units[name+str(group[-1])] = splitline.split("=")[1].strip()[1:-1]
    elif line.lstrip().startswith('#'):
        continue
    elif not line.strip():
        continue
    elif "value{" in line:
        indent = indent+1
    elif '}' in line:
        indent = indent-1
    elif "Description" in line:
        splitline = line.split("=")
        descriptions[name+str(group[-1])] = splitline[1].strip()[1:-1]
    elif "ValType" in line:
        splitline = line.split("=")
        types[name+str(group[-1])] = splitline[1].strip()
    elif "MinOccurs" in line:
        splitline = line.split("=")
        minOccurs[name+str(group[-1])] = splitline[1].strip()
    elif "MaxOccurs" in line:
        splitline = line.split("=")
        maxOccurs[name+str(group[-1])] = splitline[1].strip()
    elif "MinValExc" in line:
        val = line.split("=")[1].strip()
        restrictions[name+str(group[-1])] = ">"+str(val)
    elif "MinValInc" in line:
        val = line.split("=")[1].strip()
        restrictions[name+str(group[-1])] = ">="+str(val)
    elif "MaxValExc" in line:
        val = line.split("=")[1].strip()
        restrictions[name+str(group[-1])] = "<"+str(val)
    elif "MaxValInc" in line:
        val = line.split("=")[1].strip()
        restrictions[name+str(group[-1])] = "<="+str(val)
    elif "ValEnums" in line:
        val = line.split("=")[1].strip()
        restrictions[name+str(group[-1])] = val
    else:
        splitline = line.split('{')
        name = splitline[0].strip()
        names.append(name)
        group.append(indent)
        indent = indent+1

preamble = open('user_manual_preamble')
pream = preamble.readlines()
preamble.close()

rst = open("input_file.rst", "w")

# Copy the preamble over to the new file
for line in pream:
    rst.write(line)

rst.write('\n')

# Write out the auto-extracted information from the input.sch file
header = ["=", "-", "^", '"']
for i, param in enumerate(names):
    rst.write(param+"\n")
    rst.write(len(param)*header[group[i]]+"\n")
    uniqueName = param+str(group[i])
    rst.write(":Description: ")
    if uniqueName in descriptions:
        rst.write(descriptions[uniqueName]+"\n")
    else:
        rst.write("n/a\n")
    rst.write(":Min Occurance: "+str(minOccurs[uniqueName])+"\n")
    rst.write(":Max Occurance: "+str(maxOccurs[uniqueName])+"\n")
    rst.write(":Units: ")
    if uniqueName in units:
        rst.write(units[uniqueName]+"\n")
    else:
        rst.write("n/a\n")
    rst.write(":Type: ")
    if uniqueName in types:
        rst.write(types[uniqueName]+"\n")
    else:
        rst.write("n/a\n")
    rst.write(":Range: ")
    if uniqueName in restrictions:
        rst.write(restrictions[uniqueName]+"\n")
    else:
        rst.write("n/a\n")
    rst.write(":Note: ")
    if uniqueName in appearance:
        rst.write(appearance[uniqueName]+"\n")
    else:
        rst.write("n/a\n")
    rst.write("\n")
Loading