Source code for dtOOPythonApp.builder.multipleBoundedVolume_gridChannel

from dtOOPythonApp.tools.dtBundleTools import dtBundleBuilder

from dtOOPythonSWIG import (
    analyticGeometry,
    analyticSurface,
    rectangularTrimmedSurface_curveRotateConstructOCC,
    dtPoint3,
    analyticCurve,
    map2dTo3d,
    map3dTo3d,
    dtVector3,
    dtLinearAlgebra,
    labeledVectorHandlingAnalyticGeometry,
    vectorHandlingAnalyticGeometry,
    infinityMap3dTo3d,
    multipleBoundedVolume,
    multipleBoundedSurface,
    partRotatingMap2dTo3d,
    #bSplineCurve_curveConnectConstructOCC,
)

from typing import List, Tuple
import logging

import numpy as np

[docs] class multipleBoundedVolume_gridChannel(dtBundleBuilder): """ Create the grid channel as a multiple bounded volume. This class: - Creates multiple bounded surfaces on the hub and shroud. - Creates bounding faces from the meanplane and coupling faces. - Creates a multiple bounded volume of the grid channel. Attributes ---------- label_: str Label. channel_: analyticGeometry 360° rotated channel domain meanplanes_: List[analyticGeometry] List of meanplane faces couplings_: List[analyticGeometry] List of coupling faces nBlades_: int Number of blades nInOutSurf_: int Number of meanplane faces extending from the mesh blocks to the inlet and outlet each rotVector_: dtVector3 Rotation vector of the grid channel. orientation_: int Orientation of the blade in the channel. - 1 : Blade is oriented in u-direction of channel - -1 : Blade is oriented in negative u-direction of the channel boundSurf_: labeledVectorHandlingAnalyticGeometry Container for bounding surfaces. gridChannel_: analyticGeometry Volume of the grid channel. Examples -------- >>> import dtOOPythonSWIG as dtOO Build channel geometry >>> channel = dtOO.rotatingMap2dTo3d( ... dtOO.dtVector3(0,0,1), ... dtOO.analyticSurface( ... dtOO.bSplineSurface_bSplineCurveFillConstructOCC( ... dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+1.00, +0.00, 0.50) ... << dtOO.dtPoint3(+0.50, +0.00, 0.50), ... 1 ... ).result(), ... dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.50, +0.00, 0.50) ... << dtOO.dtPoint3(+0.50, +0.00, 0.00), ... 1 ... ).result(), ... dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.50, +0.00, 0.00) ... << dtOO.dtPoint3(+1.00, +0.00, 0.00), ... 1 ... ).result(), ... dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+1.00, +0.00, 0.00) ... << dtOO.dtPoint3(+1.00, +0.00, 0.50), ... 1 ... ).result(), ... ).result() ... ) ... ) Define meanplane curves >>> c_mp0 = dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.50, +0.00, 0.50) ... << dtOO.dtPoint3(+0.50, +0.00, 0.00), ... 1 ... ).result() >>> c_mp1 = dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.55, +0.10, 0.50) ... << dtOO.dtPoint3(+0.60, +0.10, 0.00), ... 1 ... ).result() >>> c_mp2 = dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.90, +0.10, 0.50) ... << dtOO.dtPoint3(+0.95, +0.10, 0.00), ... 1 ... ).result() >>> c_mp3 = dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+1.00, +0.00, 0.50) ... << dtOO.dtPoint3(+1.00, +0.00, 0.00), ... 1 ... ).result() Define coupling curve >>> c_coup = dtOO.bSplineCurve_pointConstructOCC( ... dtOO.vectorDtPoint3() ... << dtOO.dtPoint3(+0.85, +0.15, 0.50) ... << dtOO.dtPoint3(+0.85, +0.25, 0.00), ... 1 ... ).result() Create meanplane faces in the channel from the curves >>> mp0 = dtOO.analyticSurface( ... dtOO.bSplineSurface_exchangeSurfaceConstructOCC( ... dtOO.bSplineSurface_skinConstructOCC( ... c_mp1, c_mp0 ... ).result() ... ).result() ... ) >>> mp1 = dtOO.analyticSurface( ... dtOO.bSplineSurface_exchangeSurfaceConstructOCC( ... dtOO.bSplineSurface_skinConstructOCC( ... c_mp1, c_mp2 ... ).result() ... ).result() ... ) >>> mp2 = dtOO.analyticSurface( ... dtOO.bSplineSurface_exchangeSurfaceConstructOCC( ... dtOO.bSplineSurface_skinConstructOCC( ... c_mp2, c_mp3 ... ).result() ... ).result() ... ) Create coupling faces from meanplane curves and coupling curve >>> coup0 = dtOO.analyticSurface( ... dtOO.bSplineSurface_skinConstructOCC( ... c_mp2, c_coup ... ).result() ... ) >>> coup1 = dtOO.analyticSurface( ... dtOO.bSplineSurface_skinConstructOCC( ... c_coup, c_mp1 ... ).result() ... ) Make a list containing the meanplane and coupling faces >>> meanplaneFaces = [ ... dtOO.map2dTo3d.MustDownCast(mp0), ... dtOO.map2dTo3d.MustDownCast(mp1), ... dtOO.map2dTo3d.MustDownCast(mp2), ... ] >>> couplingFaces = [ ... dtOO.map2dTo3d.MustDownCast(coup0), ... dtOO.map2dTo3d.MustDownCast(coup1), ... ] Create the grid channel >>> from dtOOPythonApp.builder import multipleBoundedVolume_gridChannel >>> gridChannel = multipleBoundedVolume_gridChannel( ... label = "test", ... channel = channel, ... meanplanes = meanplaneFaces, ... couplings = couplingFaces, ... nBlades = 12, ... nInOutSurfSuction = 1 ... ) >>> gridChannel.build() Return the multiple bounded volume and a list with the bounding faces >>> gc, gcFaces = gridChannel.getGridChannel() Check the class of the returned volume >>> gc.virtualClassName() 'multipleBoundedVolume' The main method of this class is :meth:`build`. The grid channel is built as a multiple bounded volume. It forms the part of the bladed channel mesh that is not occupied by the mesh blocks. The construction of the multiple bounded volume requires a set of bounding surfaces. The bounding surfaces ``boundSurfs_`` are based on the meanplane and coupling faces provided to the class through the lists ``meanplanes_`` and ``couplings_``. The following figure shows the surfaces in ``meanplanes_`` and ``couplings_``. .. _meanplanesAndCouplings: .. figure:: bladeFigs/gidChannel_meanplanesAndCouplings.png :width: 100% :align: center Meanplane faces (yellow and green) and coupling faces (cyan) provided to this class. The blade (grey) is shown for reference. The grid channel is formed on the blade side from the coupling faces of the mesh blocks (cyan) and the FE-Meanplane faces (:numref:`meanplanesAndCouplings` yellow). The bounding faces on the opposing side of the grid channel are formed through a rotational translation of the faces in ``meanplanes_`` (:numref:`meanplanesAndCouplings` yellow and green). These rotated faces form the periodic pressure faces of the bladed channel. The inlet and outlet faces (:numref:`bounds` red) of the grid channel are created from the rotation of the interface curves of the FE-Meanplane faces. The FE-Meanplane face from which the inlet interface is created is the last face in the meanplane list ``meanplanes_[-1]``. The face from which the outlet is created is the first face in the meanplane list ``meanplanes_[0]``. The rotation angle is defined through the number of blades ``nBlades_`` in the full 360° channel. The rotation vector is provided through ``rotVector_``. The bounding surfaces on the hub and shroud are created as multiple bounded surfaces. The required bounding curves (:numref:`bounds`, magenta) are generated from the edges of the other bounding faces of the grid channel. The multiple bounded surfaces need bounding faces on the hub and shroud of the channel geometry ``channel_`` in which the bounding curves are located. The input ``orientation_`` encodes the orientation of the blade within the channel. The following values are supported: - ``1``: Blade is oriented in the positive u-direction of the channel - ``-1``: Blade is oriented in the negative u-direction of the channel The method :meth:`calcRotParams` is used to ensure that the bounding faces of the multiple bounded surfaces extend over the correct hub and shroud regions. The bounding surfaces are appended to the list ``boundSurf_`` The boundary faces of the grid channel and the bounding curves of the hub boundary face, are shown in the following figure. .. _bounds: .. figure:: bladeFigs/gidChannel_bound.png :width: 100% :align: center Bounding faces of the grid channel with periodic faces (yellow / green), inlet and outlet (red), coupling faces (cyan) and bounding curves of the hub boundary (magenta). The blade (grey) is shown for reference. The following face labels are used for the bounding faces: - ``"inlet"``: Inlet boundary (:numref:`bounds` red) - ``"outlet"``: Outlet boundary (:numref:`bounds` red) - ``"suction_tri_" + str(i)``: Suction boundary built from meanplane faces (:numref:`bounds` and :numref:`meanplanesAndCouplings` yellow) - ``"coupling_" + str(i)``: Boundary connecting to the blade mesh block (:numref:`bounds` cyan) - ``"pressure_tri_" + str(i)``: Pressure boundary built from rotated meanplane faces (:numref:`bounds` yellow) - ``"pressure_quad_" + str(i)``: Pressure boundary built from rotated meanplane faces (:numref:`bounds` green) - ``"hub"``: Multiple bounded surface on the hub - ``"shroud"``: Multiple bounded surface on the shroud The strings ``"pressure"`` and ``"suction"`` correspond to the periodic faces associated with the pressure and suction sides of a turbine channel created with this class. The markers ``"tri"`` and ``"quad"`` in the naming of the pressure and suction boundaries are used to distinguish between faces meshed unstructured with prism elements and faces meshed transfinite with hexahedral elements. The grid channel is created from the faces in ``boundSurf_`` and stored in ``gridChannel_``. The method :meth:`getGridChannel` returns the grid channel ``gridChannel_`` together with a list of the bounding faces ``boundSurf_`` If debug mode is enabled, the bounding faces can be plotted using the following naming convention: :: "debug_gridChannelFace_" + label_ + "_" + face.getLabel() """ def __init__( self, label: str, channel: analyticGeometry, meanplanes: List[analyticGeometry], couplings: List[analyticGeometry], nBlades: int, nInOutSurfSuction: int = 2, rotVector: dtVector3 = dtVector3(0, 0, 1), orientation: int = 1, ) -> None: """ Parameters ---------- label: str Label. channel: analyticGeometry 360° rotated channel domain meanplanes: List[analyticGeometry] List of meanplane faces couplings: List[analyticGeometry] List of coupling faces nBlades: int Number of blades nInOutSurfSuction: int Number of meanplane faces extending from the mesh blocks to the inlet and outlet each rotVector: dtVector3 Rotation vector of the grid channel orientation: int Orientation of the blade in the channel. - 1 : Blade is oriented in u-direction of channel - -1 : Blade is oriented in negative u-direction of the channel """ super( multipleBoundedVolume_gridChannel, self ).__init__() logging.debug("Initializing %s ..." % (label)) self.label_ = label self.channel_ = map3dTo3d.MustDownCast( channel ) self.meanplanes_ = meanplanes self.couplings_ = couplings self.nBlades_ = nBlades self.nInOutSurf_ = nInOutSurfSuction self.rotVector_ = dtLinearAlgebra.normalize(rotVector) self.orientation_ = orientation
[docs] def build(self) -> None: """Build part. This method: - Creates multiple bounded surfaces on the hub and shroud. - Creates bounding faces from the meanplane and coupling faces. - Creates a multiple bounded volume representing the grid channel. Parameters ---------- None Returns ------- None This method creates the grid channel from the faces in ``meanplanes_`` and ``couplings_``. The following figure illustrates the operations performed. .. _mbvActivity: .. figure:: bladeFigs/multipleBoundedVolume_gridChannel.png :width: 100% :align: center Activity diagram of class `multipleBoundedVolume_gridChannel`. Colors correspond to the creation of geometries shown in :numref:`meanplanesAndCouplings` and :numref:`bounds`. **Prepare Containers** Three vector-handling containers are created. The container ``boundSurf_`` stores the bounding surfaces of the multiple bounded volume. The containers ``hubCurves`` and ``shroudCurves`` store the bounding curves of the multiple bounded surfaces that define the hub and shroud boundaries of the grid channel. **Create Bounding faces for the Multiple Bounded Surfaces on Hub and Shroud** To create the multiple bounded surfaces, bounding faces on the hub and shroud are required. To ensure that these surfaces extend over the full grid channel domain, the hub and shroud points ``p0h`` and ``p0s`` at the inlet or outlet, depending on ``orientation_``, are extracted from ``meanplanes_``. By passing these points to the method :meth:`calcRotParams`, their u-coordinate within the channel, including a tolerance, is calculated. The hub and shroud bounding faces ``m2d_hub`` and ``m2d_shr`` are then created by rotating a segment of the channel ``channel_`` at this u-coordinate on the hub or shroud around ``rotVector_``. **Iterate over meanplane faces** :: for i, face in enumerate(meanplanes_): The periodic faces as well as the inlet and outlet boundaries of the grid channel are created by iterating over ``meanplanes_``. A volume ``vol`` is created by rotating the current meanplane face ``face``. The bounding surfaces of the grid channel are extracted from ``vol`` as segments of constant parameter coordinates. Initially, the string ``lab`` is set to ``"quad"``. **Interface Meanplane** :: i == 0 or i == len(meanplanes_) - 1 If the iteration processes the first or last meanplane face, an inlet or outlet interface boundary is created. By definition of ``meanplanes_``, the first entry ``i == 0`` contains the outlet meanplane surface, while the last entry ``i == len(meanplanes_) - 1`` contains the inlet surface. The inlet and outlet boundaries are added to ``boundSurf_`` with the labels ``"inlet"`` and ``"outlet"``, respectively. The corresponding bounding curves on the hub and shroud are appended to ``hubCurves`` and ``shroudCurves``. **Periodic FE-Meanplane faces** :: i < nInOutSurf_ or i >= len(meanplanes_) - nInOutSurf_ The first and last ``n`` faces in ``meanplanes_`` are part of the boundary surfaces (compare :numref:`meanplanesAndCouplings` and :numref:`bounds`). The number of these faces is specified by ``nInOutSurf_``. If the iteration is processing one of these faces, the value of ``lab`` is changed to ``"tri"``. These faces are added to ``boundSurf_`` with the label :: "suction_" + lab + "_" + str(i) The corresponding bounding curves on the hub and shroud are added to the respective containers. **Create Periodic Pressure Surfaces** In every iteration, the rotated meanplane face is added to ``boundSurf_`` with the following label: :: "pressure_" + lab + "_" + str(i) The associated bounding curves are appended to ``hubCurves`` and ``shroudCurves``. **Iterate over coupling faces** :: for i, face in enumerate(couplings_): All coupling faces are part of the grid channel boundary surfaces. The last two faces in ``couplings_`` correspond to the faces downstream of the trailing edge and are oriented orthogonally to the flow direction. These faces are oriented differently from the remaining coupling faces. The condition ``i >= len(couplings_) - 2`` identifies these faces and ensures that the correct bounding curves are added to ``hubCurves`` and ``shroudCurves``. **Create the Multiple Bounded Surfaces on the Hub and Shroud** The multiple bounded surfaces on the hub and shroud, ``mbs_hub`` and ``mbs_shroud``, are created from ``m2d_hub`` and ``m2d_shr`` together with the lists of bounding curves ``hubCurves`` and ``shroudCurves``. **Create the Grid Channel** The grid channel volume is created using the ``dtOO`` class ``multipleBoundedVolume`` from ``boundSurf_``. The resulting object is stored in ``gridChannel_``. """ # # prepare container objects for geometires # # vector handler for boundary surfaces self.boundSurf_ = labeledVectorHandlingAnalyticGeometry() # vector handlers for bounding curves of multiple bounded surfaces of hub and shroud hubCurves = vectorHandlingAnalyticGeometry() shroudCurves = vectorHandlingAnalyticGeometry() # get the hub and shroud points from the meanplane at the inlet or outlet if self.orientation_ > 0: # at the inlet p0h = self.meanplanes_[-1].getPointPercent(1,0) p0s = self.meanplanes_[-1].getPointPercent(1,1) else: # at the outlet p0h = self.meanplanes_[0].getPointPercent(1,0) p0s = self.meanplanes_[0].getPointPercent(1,1) # get the u coordinates of these points in the channel uHub = self.calcRotParams(p0h) uShr = self.calcRotParams(p0s) # # generate bounding faces for multiple bounded surfaces # the face is a circular segment and not the whole rotating face # (rotation of 2*pi would be detected as degenerated face # (starting and ending curve would be at the same point)) # angle = 2*np.pi * 0.95 # curve segment at hub at calculated u coordinate m1d_hub = self.channel_.segmentConstWPercent(0).segmentConstUPercent(uHub) # create the bounding face m2d_hub = analyticSurface( rectangularTrimmedSurface_curveRotateConstructOCC( analyticCurve.MustDownCast(m1d_hub).ptrDtCurve(), dtPoint3(0, 0, 0), self.rotVector_, angle ).result() ) # do the same with the shroud m1d_shr = self.channel_.segmentConstWPercent(1).segmentConstUPercent(uShr) m2d_shr = analyticSurface( rectangularTrimmedSurface_curveRotateConstructOCC( analyticCurve.MustDownCast(m1d_shr).ptrDtCurve(), dtPoint3(0, 0, 0), self.rotVector_, angle ).result() ) ## Debug statement to plot the hub and shroud faces #self.appendAnalyticGeometry( # m2d_hub, # "TEST_m2d_hub_"+self.label_ # ) #self.appendAnalyticGeometry( # m2d_shr, # "TEST_m2d_shr_"+self.label_ # ) # iterate over meanplane faces for i, face in enumerate(self.meanplanes_): # create a volume by rotating the surface vol = partRotatingMap2dTo3d( self.rotVector_, map2dTo3d.MustDownCast( face ), 0.00, (1/self.nBlades_), ) # Faces are found by cutting segments from the volume # label "quad" for faces which are meshed with hexagonal faces # (faces with rotational periodicity to mesh blocks) lab = "quad" # # outlet or inlet boundary of region (first or last meanplane face) # if i == 0 or i == len(self.meanplanes_)-1: if i == 0: let = "outlet" if i == len(self.meanplanes_)-1: let = "inlet" inOut = vol.segmentConstVPercent(1) self.boundSurf_.push_back(inOut << let) # boundary curves for multiple bounded surfaces at hub and shroud hub = vol.segmentConstVPercent(1).segmentConstVPercent(0) hubCurves.push_back(hub) shr = vol.segmentConstVPercent(1).segmentConstVPercent(1) shroudCurves.push_back(shr) # # Periodic meanplane faces # # special treatment for suction boundaries # only the first two and last two faces are taken from the meanplane # the other faces are the coupling faces # self.nInOutSurf_ specifies how many extention faces are defined between # the mesh blocks and inlet or outlet if i < self.nInOutSurf_ or i >= len(self.meanplanes_)-self.nInOutSurf_: # label "tri" for faces which are meshed with prisms # (faces with rotational periodicity to suction and pressure) lab = "tri" # suction boundary (second periodic) per1 = vol.segmentConstUPercent(0) self.boundSurf_.push_back(per1 << "suction_"+lab+"_"+str(i)) # boundary curves for multiple bounded surfaces at hub and shroud hub = vol.segmentConstWPercent(0).segmentConstUPercent(0) hubCurves.push_back(hub) shr = vol.segmentConstWPercent(1).segmentConstUPercent(0) shroudCurves.push_back(shr) # pressure boundary (first periodic) # here always the meanpalne is used # lab changes from "quad" to "tri" depending on the periodicity per0 = vol.segmentConstUPercent(1) self.boundSurf_.push_back(per0 << "pressure_"+lab+"_"+str(i)) # boundary curves for multiple bounded surfaces at hub and shroud # hub boundary hub = vol.segmentConstWPercent(0).segmentConstUPercent(1) hubCurves.push_back(hub) # shroud boundary shr = vol.segmentConstWPercent(1).segmentConstUPercent(1) shroudCurves.push_back(shr) # # Iterate over coupling faces # for i, face in enumerate(self.couplings_): self.boundSurf_.push_back(face << "coupling_"+str(i)) # boundary curves for multiple bounded surfaces at hub and shroud if i >= len(self.couplings_)-2: # trailing edge faces orthogonal to the flow direction (oriented differently) hubCurves.push_back(face.segmentConstUPercent(0)) shroudCurves.push_back(face.segmentConstUPercent(1)) else: # coupling faces surrounding blade hubCurves.push_back(face.segmentConstVPercent(0)) shroudCurves.push_back(face.segmentConstVPercent(1)) # create multiple bounded surfaces for hub and shroud # push them into the vector handler for the multiple bounded volume mbs_hub = multipleBoundedSurface(m2d_hub, hubCurves) mbs_shroud = multipleBoundedSurface(m2d_shr, shroudCurves) self.boundSurf_.push_back(mbs_hub.clone() << "hub") self.boundSurf_.push_back(mbs_shroud.clone() << "shroud") # append boundaries if debug is enabeled if self.debug(): for face in self.boundSurf_: self.appendAnalyticGeometry( face, "debug_gridChannelFace_"+self.label_+"_"+face.getLabel() ) for i, curve in enumerate(hubCurves): self.appendAnalyticGeometry( curve, "debug_"+self.label_+"_hubCurve_"+str(i) ) for i, curve in enumerate(shroudCurves): self.appendAnalyticGeometry( curve, "debug_"+self.label_+"_shroudCurve_"+str(i) ) # creat grid channel as multi bounded volume self.gridChannel_ = multipleBoundedVolume(infinityMap3dTo3d(), self.boundSurf_)
[docs] def calcRotParams(self, p0) -> float: """Calculate the u-parameter of a point within the channel. This method: - Reparametrizes a point in channel coordinates. - Offsets the u-parameter by ``0.01``. - Corrects the value if the parameter becomes negative. - Returns the resulting u-parameter. Parameters ---------- p0 : dtPoint3 Cartesian point within the channel. Returns ------- uvwP0 : float u-parameter of the point in the channel reduced by the tolerance. """ # reparametrize the point in uwv-parameters of the channel # get the u coordinate in percent and adding 0.01 tolerance uvwP0 = self.channel_.percent_u( self.channel_.reparamInVolume(p0).x() ) - 0.01 # correcting if parameter is negative if uvwP0 < 0.0: uvwP0 = 1 + uvwP0 return uvwP0
# # return method for grid channel and its faces #
[docs] def getGridChannel(self) -> Tuple[analyticGeometry, List[analyticGeometry]]: """Return the grid channel volume ``gridChannel_`` and its bounding surfaces ``boundSurf_``. Parameters ---------- None Returns ------- gridChannel_: analyticGeometry Multiple bounded volume of the grid channel boundSurf_: labeledVectorHandlingAnalyticGeometry List of bounding faces """ return self.gridChannel_, self.boundSurf_