diff --git a/doc/conf.py b/doc/conf.py index 8a8840d..ae1564d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -20,4 +20,4 @@ extensions.extend(["numpydoc"]) # noqa: F405 # mock import for autodoc -autodoc_mock_imports = ["numpy", "mpi4py", "petsc4py"] +autodoc_mock_imports = ["numpy", "mpi4py", "petsc4py", "baseclasses"] diff --git a/doc/options.rst b/doc/options.rst index efd379e..d270a2c 100644 --- a/doc/options.rst +++ b/doc/options.rst @@ -3,72 +3,4 @@ Options ======= -Here are the options currently available in IDWarp. - -====================================== ========== =========================================== ================================================================================================================================================================================ -Parameter Type Default Description -====================================== ========== =========================================== ================================================================================================================================================================================ -`gridFile` `str` `None` This is the grid file to use. It must always be spcified. It may be a structured or - unstructured CGNS file or it may be an OpenFOAM directory containing a mesh specification. - -`fileType` `str` `CGNS` Specify the type of grid file. Valid options are 'CGNS' or 'OpenFOAM' - -`specifiedSurfaces` `list` `None` This option is used to specify which surfaces are used to build the surface tree where - deformations are to be specified. The default is 'None' which will automatically use all wall-type surfaces - in the grid file. For CGNS files this corresponds to the following boundary condtiions: - 'BCWall', 'BCWallViscous', 'BCWallViscousHeatFlux', 'BCWallViscousAdiabatic', 'BCWallInviscid'. - For OpenFOAM files, all 'Patch' and 'Wall' surfaces are assumed by default. If a non-None value - is given it should be list of families the use wants to use to generate the surface definition. - -`symmetrySurfaces` `list` `None` This option is used to specify which surfaces are used to determine symmetry planes. The default - is 'None' which will automatically use the 'BCSymmetryPlane' contidions found the CGNS files. - This option is only valid for structured CGNS files. If a non-None value is given it should be - a list of families. - -`symmetryPlanes` `list` `None` This is sort of a "last-resort" option. It is used to overwrite and explicitly define symmetry conditions - IDWarp is to use. It is the only method for specifying symmetry for unstructured CGNS files and OpenFOAM files. - For the 'symmetrySurfaces' option to be active, 'symmetryPlanes' must be None. If 'symmetryPlanes' is not 'None' it - is expected to be a list of the following form: [ [[x1, y1, z1], [nx1, ny1, nz1]], [[x2, y2, z2], [nx2, ny2, nz2]] ]. - The previous example defines two symmetry planes using a point-normal approach. The first plane is defined by pt=(x1,y1,z1) with - normal=(nx1, ny1, nz1) and the second plane is defined with pt=(x2,y2,z2), normal=(nx2, ny2, nz2). The normal direction should be - normalized to unit magnitude. Note that no symmetry may be specified with the option 'symmetryPlanes':[]. - -`aExp` `float` `3.0` This is the first exponent in the inverse distance calc. However, for efficiency reasons this value is - hard-coded and not currently available to be changed by the user. - -`bExp` `float` `5.0` This is the second exponent in the inverse distance calc. However, for efficiency reasons this value is - hard-coded and not currently available to be changed by the user. - -`LdefFact` `float` `1.0` This parameter is used to determine how "far" into the field a surface deformation propagates before it is attenuated. - For small shape modifications, such as small changes to the shape of a airfoil, the default value of 1.0 is likely to be - sufficient. However, for much larger changes in the surface such as wing planform changes, much larger values tend to be more - robust. For these cases, values in the range 50-100 are common. - -`alpha` `float` `0.25` This value determines how the two different exponent terms are blended. It determines how much of the higher exponent bExp - term is used. Typical values are between 0.1 and 0.3. A lower value - prioritizes full blending and may result in quality reduction in the near-wall boundary layer. Higher values of alpha will - tend maintain near wall quality better, but may give unacceptable skewness in the transition region between where bExp is most - significant to where aExp is more significant. - -`errTol` `float` `0.0001` This is the relative tolerance used to the fast sum approximation. A larger tolerance is faster, but may result in small - mesh imperfections away from the surface. If mesh edge lengths grow uniformly away from the body, small "errors" is the node - position are not an issue. However, if the mesh has small edge lengths a great distance from the body, these imperfections may cause - issues and it may be required to lower the tolerance by an order of magnitude or two at the cost of more computational time. - -`evalMode` `str` `fast` How to compute the sums. The default which should be used at all times is 'fast'. The other option is 'exact' which is only - typically used for debugging or comparison purposes. - -`useRotations` `boolean` `True` Flag specifying if rotations are to be interpolated in addition to displacements. For small mesh changes it may not be necessary to - interpolate rotations. However, if the surface is undergoing large changes in orientation, using rotations will help preseve - boundary layer orthogonality which is generally desirable. - -`zeroCornerRotations` `boolean` `True` Flag specifying if rotations at sharp corners (cornerAngle defines "sharp") are to be zeroed and not contribute to the deformation. - Since the normal direction is not well defined at a corner point, including them may cause issues on some grids. - -`cornerAngle` `float` `30.0` The minimum deviation between surface normals surrounding a node for it to be considered a corner point. - -'bucketSize` `int` `8` The size of the "buckets" at the last level of the KD-tree. A large bucket size reduces the number of levels in the tree and the - overall tree size but may require more computation since a less fine granularity of leaves are available. Experiments have indicated - there is little difference in run time for bucket sizes 1, 2, 4 and 8. - -====================================== ========== =========================================== ================================================================================================================================================================================ +.. optionstable:: idwarp.USMesh diff --git a/doc/options.yaml b/doc/options.yaml new file mode 100644 index 0000000..ed06516 --- /dev/null +++ b/doc/options.yaml @@ -0,0 +1,112 @@ +gridFile: + desc: > + This is the grid file to use. + It must always be specified. + It may be a structured or unstructured grid file or it may be an OpenFOAM directory containing a mesh specification. + +fileType: + desc: Specify the type of grid file. + CGNS: CGNS file + OpenFOAM: OpenFOAM directory + PLOT3D: PLOT3D file + +specifiedSurfaces: + desc: > + This option is used to specify which surfaces are used to build the surface tree where deformations are to be specified. + The default is None which will automatically use all wall-type surfaces in the grid file. + For CGNS files this corresponds to the following boundary condtiions: ``BCWall``, ``BCWallViscous``, ``BCWallViscousHeatFlux``, ``BCWallViscousAdiabatic``, ``BCWallInviscid``. + For OpenFOAM files, all ``patch`` and ``wall`` surfaces are assumed by default. + If a non-None value is given it should be list of families the use wants to use to generate the surface definition. + +symmetrySurfaces: + desc: > + This option is used to specify which surfaces are used to determine symmetry planes. + If None, IDwarp will automatically use the ``BCSymmetryPlane`` conditions in the CGNS files. + This option is only valid for structured CGNS files. + If a non-None value is given, it should be a list of families. + +symmetryPlanes: + desc: > + This is sort of a "last-resort" option. + It is used to overwrite and explicitly define symmetry conditions IDWarp is to use. + It is the only method for specifying symmetry for unstructured CGNS files and OpenFOAM files. + For the ``symmetrySurfaces`` option to be active, ``symmetryPlanes`` must be None. + If ``symmetryPlanes`` is not None, it is expected to be a list of the following form: + [ [[x1, y1, z1], [nx1, ny1, nz1]], [[x2, y2, z2], [nx2, ny2, nz2]] ]. + The previous example defines two symmetry planes using a point-normal approach. + The first plane is defined by pt=(x1,y1,z1) with normal=(nx1, ny1, nz1) and the second plane is defined with pt=(x2,y2,z2), normal=(nx2, ny2, nz2). + The normal direction should be normalized to unit magnitude. + Note that no symmetry may be specified with an empty list. + +aExp: + desc: > + This is the first exponent in the inverse distance calc. + However, for efficiency reasons this value is hard-coded and not currently available to be changed by the user. + +bExp: + desc: > + This is the second exponent in the inverse distance calc. + However, for efficiency reasons this value is hard-coded and not currently available to be changed by the user. + +LdefFact: + desc: > + This parameter is used to determine how "far" into the field a surface deformation propagates before it is attenuated. + For small shape modifications, such as small changes to the shape of a airfoil, the default value of 1.0 is likely to be sufficient. + However, for much larger changes in the surface such as wing planform changes, much larger values tend to be more robust. + For these cases, values in the range 50-100 are common. + +alpha: + desc: > + This value determines how the two different exponent terms are blended. + It determines how much of the higher exponent ``bExp`` term is used. + Typical values are between 0.1 and 0.3. + A lower value prioritizes full blending and may result in quality reduction in the near-wall boundary layer. + Higher values will tend maintain near wall quality better, but may give unacceptable skewness in the transition region between where ``bExp`` is most significant to where ``aExp`` is more significant. + +errTol: + desc: > + This is the relative tolerance used for the fast sum approximation. + A larger tolerance is faster, but may result in small mesh imperfections away from the surface. + If mesh edge lengths grow uniformly away from the body, small "errors" in the node position are not an issue. + However, if the mesh has small edge lengths a great distance from the body, these imperfections may cause issues and it may be required to lower the tolerance by an order of magnitude or two at the cost of more computational time. + +evalMode: + desc: How to compute the sums. + fast: This should be used for almost all purposes. + exact: This is typically used only for debugging or comparison purposes. + +symmTol: + desc: > + Possibly deprecated option. + IDWarp currently uses the BC info in the CGNS meshes to figure out which nodes are on the symmetry plane. + This option seems to be a leftover from older implementations. + It was used to specify the distance from the symmetry plane for a node to tag it as a symmetry plane node. + +useRotations: + desc: > + Flag specifying if rotations are to be interpolated in addition to displacements. + For small mesh changes it may not be necessary to interpolate rotations. + However, if the surface is undergoing large changes in orientation, using rotations will help preserve boundary layer orthogonality which is generally desirable. + +zeroCornerRotations: + desc: > + Flag specifying if rotations at corners (defined by ``cornerAngle``) are to be zeroed and not contribute to the deformation. + Since the normal direction is not well defined at a corner point, including them may cause issues on some grids. + +cornerAngle: + desc: > + The minimum deviation in degrees between surface normals surrounding a node for it to be considered a corner point. + +restartFile: + desc: > + A restart file can be used to load the node coefficients to IDWarp to speed up initialization. + There are 3 execution paths: + If no restart file is provided, the code performs a full initialization. + If the restart file is provided but the file does not exist or does not match with some internal checks, the code performs a full initialization and writes the load balancing and the coefficients to this restart file for future use. + Finally, if the restart file is provided and it is good, the code loads the initialized coefficients and does not need to do a full initialization. + +bucketSize: + desc: > + The size of the "buckets" at the last level of the KD-tree. + A large bucket size reduces the number of levels in the tree and the overall tree size but may require more computation since a less fine granularity of leaves are available. + Experiments have indicated there is little difference in run time for bucket sizes 1, 2, 4 and 8. \ No newline at end of file diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 4607192..db6fa4e 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -12,7 +12,7 @@ warping script and explaining the various parts. :: options = { 'gridFile':'../../input_files/o_mesh.cgns', - 'fileType':'CGNS', + 'fileType':'cgns', 'specifiedSurfaces':None, 'symmetrySurfaces':None, 'symmetryPlanes':[], @@ -58,7 +58,7 @@ explained in :ref:`idwarp_options`. :: options = { 'gridFile':'../../input_files/o_mesh.cgns', - 'fileType':'CGNS', + 'fileType':'cgns', 'specifiedSurfaces':None, 'symmetrySurfaces':None, 'symmetryPlanes':[], diff --git a/examples/AhmedBodyCoarse/run_warp.py b/examples/AhmedBodyCoarse/run_warp.py index f5fe17f..d6734d3 100644 --- a/examples/AhmedBodyCoarse/run_warp.py +++ b/examples/AhmedBodyCoarse/run_warp.py @@ -10,10 +10,10 @@ meshOptions = { "gridFile": os.getcwd(), - "fileType": "openFoam", + "fileType": "OpenFOAM", "symmetryPlanes": [[[0, 0, 0], [0, 1, 0]]], - "aExp": 3, - "bExp": 5, + "aExp": 3.0, + "bExp": 5.0, "alpha": 1.0, "LdefFact": 0.20, } diff --git a/idwarp/MultiUnstructuredMesh.py b/idwarp/MultiUnstructuredMesh.py index 02a6c26..6e3bd6c 100644 --- a/idwarp/MultiUnstructuredMesh.py +++ b/idwarp/MultiUnstructuredMesh.py @@ -28,9 +28,9 @@ # ============================================================================= import os import numpy as np -from pprint import pprint from mpi4py import MPI from .MExt import MExt +from baseclasses.utils import Error try: from cgnsutilities import cgnsutilities as cs @@ -38,27 +38,6 @@ cs = None -class Error(Exception): - """ - Format the error message in a box to make it clear this - was a expliclty raised exception. - """ - - def __init__(self, message): - msg = "\n+" + "-" * 78 + "+" + "\n" + "| IDWarp Error: " - i = 22 - for word in message.split(): - if len(word) + i + 1 > 78: # Finish line and start new one - msg += " " * (78 - i) + "|\n| " + word + " " - i = 1 + len(word) + 1 - else: - msg += word + " " - i += len(word) + 1 - msg += " " * (78 - i) + "|\n" + "+" + "-" * 78 + "+" + "\n" - print(msg) - Exception.__init__(self) - - class Warning(object): """ Format a warning message @@ -66,7 +45,7 @@ class Warning(object): def __init__(self, message): msg = "\n+" + "-" * 78 + "+" + "\n" + "| IDWarp Warning: " - i = 24 + i = 17 for word in message.split(): if len(word) + i + 1 > 78: # Finish line and start new one msg += " " * (78 - i) + "|\n| " + word + " " @@ -79,15 +58,14 @@ def __init__(self, message): # ============================================================================= -# UnstructuredMesh class +# MultiUnstructuredMesh class # ============================================================================= class MultiUSMesh(object): """ - This is the main Unstructured Mesh. This mesh object is designed - to interact with an structured or unstructured CFD solver though a - variety of interface functions. + This mesh object is designed to support independent deformation of + multiple overset component meshes. """ def __init__(self, CGNSFile, optionsDict, comm=None, dtype="d", debug=False): @@ -1005,23 +983,6 @@ def writeGrid(self, baseName=None): # Utility functions # ========================================================================= - def _printCurrentOptions(self): - """Prints a nicely formatted dictionary of all the current options to - the stdout on the root processor - """ - meshCounter = 0 - for mesh in self.meshes(): - if self.comm.rank == 0: - print("") - print(" Options of mesh", meshCounter, "of", len(self.meshes)) - print("") - print("+---------------------------------------+") - print("| All IDWarp Options: |") - print("+---------------------------------------+") - pprint(mesh.solverOptions) - print("") - meshCounter += 1 - def __del__(self): """Release all the mesh warping memory. This should be called automatically when the object is garbage collected.""" diff --git a/idwarp/UnstructuredMesh.py b/idwarp/UnstructuredMesh.py index 1512767..adb9f74 100755 --- a/idwarp/UnstructuredMesh.py +++ b/idwarp/UnstructuredMesh.py @@ -27,30 +27,10 @@ import os import shutil import numpy as np -from pprint import pprint from mpi4py import MPI from .MExt import MExt - - -class Error(Exception): - """ - Format the error message in a box to make it clear this - was a expliclty raised exception. - """ - - def __init__(self, message): - msg = "\n+" + "-" * 78 + "+" + "\n" + "| IDWarp Error: " - i = 22 - for word in message.split(): - if len(word) + i + 1 > 78: # Finish line and start new one - msg += " " * (78 - i) + "|\n| " + word + " " - i = 1 + len(word) + 1 - else: - msg += word + " " - i += len(word) + 1 - msg += " " * (78 - i) + "|\n" + "+" + "-" * 78 + "+" + "\n" - print(msg) - Exception.__init__(self) +from baseclasses import BaseSolver +from baseclasses.utils import Error class Warning(object): @@ -60,7 +40,7 @@ class Warning(object): def __init__(self, message): msg = "\n+" + "-" * 78 + "+" + "\n" + "| IDWarp Warning: " - i = 24 + i = 17 for word in message.split(): if len(word) + i + 1 > 78: # Finish line and start new one msg += " " * (78 - i) + "|\n| " + word + " " @@ -77,7 +57,7 @@ def __init__(self, message): # ============================================================================= -class USMesh(object): +class USMesh(BaseSolver): """ This is the main Unstructured Mesh. This mesh object is designed to interact with an structured or unstructured CFD solver though a @@ -103,27 +83,42 @@ def __init__(self, options=None, comm=None, debug=False): This needs to be true ONLY when a symbolic debugger is used. """ + name = "IDWarp" + category = "Volume mesh warping" + if comm is None: - self.comm = MPI.COMM_WORLD - else: - self.comm = comm + comm = MPI.COMM_WORLD - # Check if warp has already been set if this is this has been - # interhited to complex version - try: - self.warp - except AttributeError: - curDir = os.path.dirname(os.path.realpath(__file__)) - self.warp = MExt("idwarp", [curDir], debug=debug)._module - if options is not None: - self.solverOptions = options - else: + # Default options for mesh warping + defOpts = self._getDefaultOptions() + + if options is None: raise Error( "The 'options' keyword argument is *NOT* " "optional. An options dictionary must be passed upon " "creation of this object" ) + # Initialize the inherited BaseSolver + super().__init__(name, category, defaultOptions=defOpts, options=options, comm=comm) + + # aExp and bExp are not fully implemented + if self.getOption("aExp") != 3.0 or self.getOption("bExp") != 5.0: + raise Error( + "The aExp and bExp options are currently not implemented " + "and should not be modified from their default values." + ) + + self.printCurrentOptions() + + # Check if warp has already been set if this has been + # inherited to complex version + try: + self.warp + except AttributeError: + curDir = os.path.dirname(os.path.realpath(__file__)) + self.warp = MExt("idwarp", [curDir], debug=debug)._module + # Initialize PETSc if not done so self.warp.initpetsc(self.comm.py2f()) @@ -131,52 +126,47 @@ def __init__(self, options=None, comm=None, debug=False): # UnstructuredMesh_C.py self.dtype = "d" - # Defalut options for mesh warping - self.solverOptionsDefault = { - "gridFile": None, - "fileType": "cgns", - "specifiedSurfaces": None, - "symmetrySurfaces": None, - "symmetryPlanes": None, - "aExp": 3.0, - "bExp": 5.0, - "LdefFact": 1.0, - "alpha": 0.25, - "errTol": 0.0005, - "evalMode": "fast", - "symmTol": 1e-6, - "useRotations": True, - "zeroCornerRotations": True, - "cornerAngle": 30.0, - "restartFile": None, - "bucketSize": 8, - } - - # Set remaining default values - self._checkOptions(self.solverOptions) + # Set Fortran options values self._setMeshOptions() - self._printCurrentOptions() # Initialize various bits of stored information self.OFData = {} self.warpInitialized = False self.faceSizes = None - self.meshType = None - fileName = self.solverOptions["gridFile"] - self.meshType = self.solverOptions["fileType"] - - # Determine how to read: - if self.meshType is not None: - if self.meshType.lower() == "cgns": - # Determine type of CGNS mesh we have: - self.warp.readcgns(fileName) - - elif self.meshType.lower() == "openfoam": - self._readOFGrid(fileName) - elif self.meshType.lower() == "plot3d": - self.warp.readplot3d(fileName) - else: - raise Error("Invalid input file type. valid options are: " "CGNS" or "OpenFOAM.") + self.fileType = self.getOption("fileType") + fileName = self.getOption("gridFile") + + # Determine how to read + if self.fileType == "CGNS": + # Determine type of CGNS mesh we have + self.warp.readcgns(fileName) + elif self.fileType == "OpenFOAM": + self._readOFGrid(fileName) + elif self.fileType == "PLOT3D": + self.warp.readplot3d(fileName) + + @staticmethod + def _getDefaultOptions(): + defOpts = { + "gridFile": [(str, type(None)), None], + "fileType": [str, ["CGNS", "OpenFOAM", "PLOT3D"]], + "specifiedSurfaces": [(list, type(None)), None], + "symmetrySurfaces": [(list, type(None)), None], + "symmetryPlanes": [(list, type(None)), None], + "aExp": [float, 3.0], + "bExp": [float, 5.0], + "LdefFact": [float, 1.0], + "alpha": [float, 0.25], + "errTol": [float, 0.0005], + "evalMode": [str, ["fast", "exact"]], + "symmTol": [float, 1e-6], + "useRotations": [bool, True], + "zeroCornerRotations": [bool, True], + "cornerAngle": [float, 30.0], + "restartFile": [(str, type(None)), None], + "bucketSize": [int, 8], + } + return defOpts def getSurfaceCoordinates(self): """Returns all defined surface coordinates on this processor @@ -251,10 +241,10 @@ def setSurfaceDefinition(self, pts, conn, faceSizes, cgnsBlockID=None): # Call the fortran initialze warping command with the # coordinates and the patch connectivity given to us. - if self.solverOptions["restartFile"] is None: + if self.getOption("restartFile") is None: restartFile = "" else: - restartFile = self.solverOptions["restartFile"] + restartFile = self.getOption("restartFile") # The symmetry conditions but be set before initializing the # warping. @@ -297,14 +287,14 @@ def setSurfaceDefinition(self, pts, conn, faceSizes, cgnsBlockID=None): def setSurfaceDefinitionFromFile(self, surfFile): """Set the surface definition for the warping from a - multiblock plot3d surface file + multiblock PLOT3D surface file Parameters ---------- - surfFile : filename of multiblock plot3d surface file. + surfFile : filename of multiblock PLOT3D surface file. """ - # Read the plot3d surface file + # Read the PLOT3D surface file self.warp.readplot3dsurface(surfFile) if self.comm.rank == 0: @@ -328,7 +318,7 @@ def setSurfaceDefinitionFromFile(self, surfFile): def setSurfaceFromFile(self, surfFile): """Update the internal surface surface coordinates using an - external plot3d surface file. This can be used in an analogous + external PLOT3D surface file. This can be used in an analogous way to setSurfaceDefinitionFromFile. The 'sense' of the file must be same as the file used with setSurfaceDefinitionFromFile. That means, the same number of @@ -336,10 +326,10 @@ def setSurfaceFromFile(self, surfFile): Parameters ---------- - surfFile: filename of multiblock plot3d surface file' + surfFile: filename of multiblock PLOT3D surface file' """ - # Read the plot3d surface file + # Read the PLOT3D surface file self.warp.readplot3dsurface(surfFile) if self.comm.rank == 0: @@ -389,7 +379,7 @@ def getWarpGrid(self): def getCommonGrid(self): """Return the grid in the original ordering. This is required for the - openFOAM tecplot writer since the connectivity is only known + OpenFOAM tecplot writer since the connectivity is only known in this ordering. """ return self.warp.getcommonvolumecoordinates(self.warp.griddata.commonmeshdof) @@ -520,30 +510,29 @@ def writeGrid(self, fileName=None): fileName : str or None Filename for grid. Should end in .cgns for CGNS files. For - plot3d whatever you want. It is not optional for - cgns/plot3d. It is not required for openFOAM meshes. This + PLOT3D whatever you want. It is not optional for + CGNS/PLOT3D. It is not required for OpenFOAM meshes. This call will update the 'points' file. """ - mt = self.meshType.lower() - if mt in ["cgns", "plot3d"]: + if self.fileType in ["CGNS", "PLOT3D"]: if fileName is None: - raise Error("fileName is not optional for writeGrid with " "gridType of cgns or plot3d") + raise Error("fileName is not optional for writeGrid with " "gridType of CGNS or PLOT3D") - if mt == "cgns": + if self.fileType == "CGNS": # Copy the default and then write if self.comm.rank == 0: - shutil.copy(self.solverOptions["gridFile"], fileName) + shutil.copy(self.getOption("gridFile"), fileName) self.warp.writecgns(fileName) - elif mt == "plot3d": + elif self.fileType == "PLOT3D": self.warp.writeplot3d(fileName) - elif mt == "openfoam": + elif self.fileType == "OpenFOAM": self._writeOpenFOAMVolumePoints(self.getCommonGrid()) def writeOFGridTecplot(self, fileName): """ - Write the current openFOAM grid to a Tecplot FE polyhedron file. + Write the current OpenFOAM grid to a Tecplot FE polyhedron file. This is generally used for debugging/visualization purposes. Parameters @@ -552,7 +541,7 @@ def writeOFGridTecplot(self, fileName): Filename to use. Should end in .dat for tecplot ascii file. """ if not self.OFData: - Warning("Cannot write OpenFoam tecplot file since there is " "no openFoam ata present") + Warning("Cannot write OpenFOAM tecplot file since there is " "no OpenFOAM data present") return if self.comm.size == 1: @@ -655,7 +644,7 @@ def _setInternalSurface(self): pts = [] faceSizes = [] - if self.meshType.lower() == "cgns": + if self.fileType == "CGNS": if self.comm.rank == 0: # Do the necessary fortran preprocessing @@ -676,14 +665,14 @@ def _setInternalSurface(self): # ones to use depending on what the user has # told us. surfaceFamilies = set() - if self.solverOptions["specifiedSurfaces"] is None: + if self.getOption("specifiedSurfaces") is None: # Use all wall surfaces: for i in range(len(self.warp.cgnsgrid.surfaceiswall)): if self.warp.cgnsgrid.surfaceiswall[i]: surfaceFamilies.add(fullPatchNames[i].lower()) else: # The user has supplied a list of surface families - for name in self.solverOptions["specifiedSurfaces"]: + for name in self.getOption("specifiedSurfaces"): surfaceFamilies.add(name.lower()) usedFams = set() @@ -777,7 +766,7 @@ def _setInternalSurface(self): "list of families is %s." % (repr(list(fullPatchNames))) ) - elif self.meshType.lower() == "openfoam": + elif self.fileType == "OpenFOAM": faceSizes, conn, pts = self._computeOFConn() @@ -800,7 +789,7 @@ def _setSymmetryConditions(self): """ - symmList = self.solverOptions["symmetryPlanes"] + symmList = self.getOption("symmetryPlanes") if symmList is not None: # The user has explictly supplied symmetry planes. Use those pts = [] @@ -812,7 +801,7 @@ def _setSymmetryConditions(self): else: # Otherwise generate from the geometry. planes = [] - if self.meshType.lower() == "cgns": + if self.fileType == "CGNS": if self.comm.rank == 0: # Do the necessary fortran preprocessing @@ -829,14 +818,14 @@ def _setSymmetryConditions(self): for i in range(nPatch): fullPatchNames.append(self.warp.cgnsgrid.getsurf(i + 1).strip().lower()) symmFamilies = set() - if self.solverOptions["symmetrySurfaces"] is None: + if self.getOption("symmetrySurfaces") is None: # Use all symmetry surfaces: for i in range(len(self.warp.cgnsgrid.surfaceissymm)): if self.warp.cgnsgrid.surfaceissymm[i]: symmFamilies.add(fullPatchNames[i].lower()) else: # The user has supplied a list of surface families - for name in self.solverOptions["symmetrySurfaces"]: + for name in self.getOption("symmetrySurfaces"): symmFamilies.add(name.lower()) usedFams = set() @@ -907,14 +896,14 @@ def _setSymmetryConditions(self): "The families not found are %s." % (repr(missing)) ) - elif self.meshType.lower() in ["openfoam", "plot3d"]: + elif self.fileType in ["OpenFOAM", "PLOT3D"]: # We could probably implement this at some point, but # it is not critical raise Error( "Automatic determine of symmetry surfaces is " - "not supported for OpenFoam or Plot3d meshes. Please " + "not supported for OpenFOAM or PLOT3D meshes. Please " "specify the symmetry planes using the " "'symmetryPlanes' option. See the _setSymmetryConditions()" " documentation string for the option prototype." @@ -1000,13 +989,13 @@ def checkPlane(p1, n1, p2, n2): self.warp.setsymmetryplanes(pts.T, normals.T) # ========================================================================= - # Local OpenFoam Functions + # Local OpenFOAM Functions # ========================================================================= def _computeOFConn(self): """ - The user has specified an openfoam mesh. Loop through the mesh data and + The user has specified an OpenFOAM mesh. Loop through the mesh data and create the arrays necessary to initialize the warping. """ @@ -1022,7 +1011,7 @@ def _computeOFConn(self): bType = self.OFData["boundaries"][bName]["type"].lower() if bType in ["patch", "wall", "slip"]: - # Apparently openfoam will list boundaries with zero + # Apparently OpenFOAM will list boundaries with zero # faces on them: nFace = len(self.OFData["boundaries"][bName]["faces"]) if nFace > 0: @@ -1052,7 +1041,7 @@ def _readOFGrid(self, caseDir): Parameters ---------- caseDir : str - The directory containing the openFOAM Mesh files + The directory containing the OpenFOAM mesh files """ # import the pyOFM repo @@ -1089,55 +1078,23 @@ def _readOFGrid(self, caseDir): def _setMeshOptions(self): """Private function to set the options currently in - self.solverOptions to the corresponding place in Fortran""" - o = self.solverOptions - self.warp.gridinput.ldeffact = o["LdefFact"] - self.warp.gridinput.alpha = o["alpha"] - self.warp.gridinput.aexp = o["aExp"] - self.warp.gridinput.bexp = o["bExp"] - self.warp.gridinput.symmtol = o["symmTol"] - self.warp.gridinput.userotations = o["useRotations"] - self.warp.gridinput.zerocornerrotations = o["zeroCornerRotations"] - self.warp.gridinput.cornerangle = o["cornerAngle"] - self.warp.gridinput.errtol = o["errTol"] - self.warp.kd_tree.bucket_size = o["bucketSize"] - if o["evalMode"].lower() == "fast": + self.options to the corresponding place in Fortran""" + + self.warp.gridinput.ldeffact = self.getOption("LdefFact") + self.warp.gridinput.alpha = self.getOption("alpha") + self.warp.gridinput.aexp = self.getOption("aExp") + self.warp.gridinput.bexp = self.getOption("bExp") + self.warp.gridinput.symmtol = self.getOption("symmTol") + self.warp.gridinput.userotations = self.getOption("useRotations") + self.warp.gridinput.zerocornerrotations = self.getOption("zeroCornerRotations") + self.warp.gridinput.cornerangle = self.getOption("cornerAngle") + self.warp.gridinput.errtol = self.getOption("errTol") + self.warp.kd_tree.bucket_size = self.getOption("bucketSize") + if self.getOption("evalMode") == "fast": self.warp.gridinput.evalmode = self.warp.gridinput.eval_fast - elif o["evalMode"].lower() == "exact": + elif self.getOption("evalMode") == "exact": self.warp.gridinput.evalmode = self.warp.gridinput.eval_exact - def _checkOptions(self, solverOptions): - """Check the solver options against the default ones and add - option iff it is NOT in solverOptions""" - - for key in self.solverOptionsDefault.keys(): - if key not in solverOptions.keys(): - solverOptions[key] = self.solverOptionsDefault[key] - else: - self.solverOptionsDefault[key] = solverOptions[key] - - # Print a couple of warnings about aExp and bExp which are not - # fully implemented. - if self.comm.rank == 0: - if self.solverOptions["aExp"] != 3.0 or self.solverOptions["bExp"] != 5.0: - Warning( - "The aExp and bExp options are currently not implemented " - "and will always use their default values of aExp=3.0 and " - "bExp=5.0" - ) - - return solverOptions - - def _printCurrentOptions(self): - """Prints a nicely formatted dictionary of all the current options to - the stdout on the root processor - """ - if self.comm.rank == 0: - print("+---------------------------------------+") - print("| All IDWarp Options: |") - print("+---------------------------------------+") - pprint(self.solverOptions) - def _processFortranStringArray(self, strArray): """Getting arrays of strings out of Fortran can be kinda nasty. This takes the array and returns a nice python list of strings""" diff --git a/idwarp/__init__.py b/idwarp/__init__.py index 48273a7..d6282e7 100644 --- a/idwarp/__init__.py +++ b/idwarp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "2.2.2" +__version__ = "2.3.0" from .UnstructuredMesh import USMesh from .MultiUnstructuredMesh import MultiUSMesh diff --git a/reg_tests/solve_script.py b/reg_tests/solve_script.py index 248c590..3f9e5a4 100644 --- a/reg_tests/solve_script.py +++ b/reg_tests/solve_script.py @@ -43,7 +43,7 @@ def printHeader(testName): # NOTE: we no longer run test1 in idwarp. test1 has been moved to pyofm and will be tested from there. def test1(): - # Test the Ahmed body openfoam mesh + # Test the Ahmed body OpenFOAM mesh sys.stdout.flush() # change directory to the correct test case os.chdir("../input_files/ahmedBodyMesh/") @@ -55,7 +55,7 @@ def test1(): meshOptions.update( { "gridFile": file_name, - "fileType": "openfoam", + "fileType": "OpenFOAM", "symmetryPlanes": [[[0, 0, 0], [0, 1, 0]]], } ) @@ -113,7 +113,7 @@ def test2(): meshOptions = copy.deepcopy(defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name, "fileType": "CGNS"}) # Create warping object mesh = USMesh(options=meshOptions) @@ -168,7 +168,7 @@ def test3(): meshOptions = copy.deepcopy(defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name, "fileType": "CGNS"}) # Create warping object mesh = USMesh(options=meshOptions) @@ -226,7 +226,7 @@ def test4(): meshOptions.update( { "gridFile": file_name, - "fileType": "cgns", + "fileType": "CGNS", "symmetryPlanes": [[[0, 0, 0], [0, 0, 1]]], } ) @@ -287,7 +287,7 @@ def test5(): meshOptions.update( { "gridFile": file_name, - "fileType": "cgns", + "fileType": "CGNS", } ) # Create warping object diff --git a/reg_tests/solve_script_cs.py b/reg_tests/solve_script_cs.py index 49f2388..1ebbadc 100644 --- a/reg_tests/solve_script_cs.py +++ b/reg_tests/solve_script_cs.py @@ -52,7 +52,7 @@ def printHeader(testName): # NOTE: we no longer run test1 in idwarp. test1 has been moved to pyofm and will be tested from there. def test1(): - # Test the Ahmed body openfoam mesh + # Test the Ahmed body OpenFOAM mesh sys.stdout.flush() # change directory to the correct test case os.chdir("../input_files/ahmedBodyMesh/") @@ -64,7 +64,7 @@ def test1(): meshOptions.update( { "gridFile": file_name, - "fileType": "openfoam", + "fileType": "OpenFOAM", "symmetryPlanes": [[[0, 0, 0], [0, 1, 0]]], } ) @@ -141,7 +141,7 @@ def test2(): meshOptions = copy.deepcopy(defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name, "fileType": "CGNS"}) # Create warping object mesh = USMesh(options=meshOptions) @@ -215,7 +215,7 @@ def test3(): meshOptions = copy.deepcopy(defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name, "fileType": "CGNS"}) # Create warping object mesh = USMesh(options=meshOptions) diff --git a/setup.py b/setup.py index 2ef3e7d..5e9715f 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ "numpy>=1.16", "petsc4py>=3.11", "mpi4py>=3.0", + "mdolab-baseclasses>=1.3", ], extras_require={"testing": ["parameterized", "testflo"]}, classifiers=["Operating System :: Linux", "Programming Language :: Python, Fortran"], diff --git a/tests/test_usmesh.py b/tests/test_usmesh.py index bf43ad3..d2fc584 100644 --- a/tests/test_usmesh.py +++ b/tests/test_usmesh.py @@ -123,6 +123,7 @@ def setUp(self): # --- Reference options to be used by every test --- self.defOpts = { "gridFile": None, + "fileType": "CGNS", "aExp": 3.0, "bExp": 5.0, "LdefFact": 1.0, @@ -132,7 +133,6 @@ def setUp(self): "symmTol": 1e-6, "useRotations": True, "bucketSize": 8, - "fileType": None, } # --- Setting the ref file for parallel tests --- self.ref_app = "" @@ -156,7 +156,7 @@ def test_comesh(self, train=False): meshOptions = copy.deepcopy(self.defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name}) # --- Call shared computation -- eval_warp(handler=handler, test_name=test_name, meshOptions=meshOptions, iscomplex=self.iscomplex) @@ -176,7 +176,7 @@ def test_omesh(self, train=False): meshOptions = copy.deepcopy(self.defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name}) # --- Call shared computation --- eval_warp(handler=handler, test_name=test_name, meshOptions=meshOptions, iscomplex=self.iscomplex) @@ -199,7 +199,6 @@ def test_sym_mesh(self, train=False): meshOptions.update( { "gridFile": file_name, - "fileType": "cgns", "symmetryPlanes": [[[0, 0, 0], [0, 0, 1]]], } ) @@ -222,7 +221,7 @@ def test_inflate_cube(self, train=False): meshOptions = copy.deepcopy(self.defOpts) - meshOptions.update({"gridFile": file_name, "fileType": "cgns"}) + meshOptions.update({"gridFile": file_name}) # --- Call shared computation -- eval_warp(handler=handler, test_name=test_name, meshOptions=meshOptions, iscomplex=self.iscomplex)