|
| 1 | +******************************* |
| 2 | +Sample generation using ADflow |
| 3 | +******************************* |
| 4 | + |
| 5 | +This section explains how to use ``WingFFD`` module for generating samples using ADflow. There are typically three |
| 6 | +main steps involved in the process: setting up options and initializing the module, adding design variables |
| 7 | +and generating samples. The ``WingFFD`` module is demonstrated using a CRM wing example. This script is |
| 8 | +available in ``examples`` directory of the repository on github. |
| 9 | + |
| 10 | +Setting up options |
| 11 | +------------------ |
| 12 | + |
| 13 | +First step involves creating options dictionary which is used for initializating the module. There are five |
| 14 | +mandatory options: ``solverOptions``, ``ffdFile``, ``gridFile``, ``liftIndex`` and ``aeroProblem``, rest all are optional, |
| 15 | +please refer :ref:`options<options>` section for more details. Following snippet of the code shows an example:: |
| 16 | + |
| 17 | + from blackbox import WingFFD |
| 18 | + from baseclasses import AeroProblem |
| 19 | + import numpy as np |
| 20 | + |
| 21 | + solverOptions = { |
| 22 | + # Common Parameters |
| 23 | + "monitorvariables": ["cl", "cd", "cmy", "yplus"], |
| 24 | + "writeTecplotSurfaceSolution": True, |
| 25 | + "writeSurfaceSolution": False, |
| 26 | + "writeVolumeSolution": False, |
| 27 | + # Physics Parameters |
| 28 | + "equationType": "RANS", |
| 29 | + "smoother": "DADI", |
| 30 | + "MGCycle": "sg", |
| 31 | + "nsubiterturb": 10, |
| 32 | + "nCycles": 7000, |
| 33 | + # ANK Solver Parameters |
| 34 | + "useANKSolver": True, |
| 35 | + "ANKSubspaceSize": 400, |
| 36 | + "ANKASMOverlap": 3, |
| 37 | + "ANKPCILUFill": 4, |
| 38 | + "ANKJacobianLag": 5, |
| 39 | + "ANKOuterPreconIts": 3, |
| 40 | + "ANKInnerPreconIts": 3, |
| 41 | + # NK Solver Parameters |
| 42 | + "useNKSolver": True, |
| 43 | + "NKSwitchTol": 1e-6, |
| 44 | + "NKSubspaceSize": 400, |
| 45 | + "NKASMOverlap": 3, |
| 46 | + "NKPCILUFill": 4, |
| 47 | + "NKJacobianLag": 5, |
| 48 | + "NKOuterPreconIts": 3, |
| 49 | + "NKInnerPreconIts": 3, |
| 50 | + # Termination Criteria |
| 51 | + "L2Convergence": 1e-14 |
| 52 | + } |
| 53 | + |
| 54 | + # Creating aeroproblem for adflow |
| 55 | + # Chord ref is 1.0 since all the dimensions are scaled according to it |
| 56 | + ap = AeroProblem( |
| 57 | + name="crm", alpha=2.0, mach=0.85, reynolds=5e6, reynoldsLength=1.0, T=298.15, |
| 58 | + areaRef=3.407014, chordRef=1.0, evalFuncs=["cl", "cd", "cmy"], xRef=1.2077, yRef=0.0, zRef=0.007669 |
| 59 | + ) |
| 60 | + |
| 61 | + # Options for blackbox |
| 62 | + options = { |
| 63 | + "solver": "adflow", |
| 64 | + "solverOptions": solverOptions, |
| 65 | + "gridFile": "crm_volMesh.cgns", |
| 66 | + "ffdFile": "crm_ffd.xyz", |
| 67 | + "liftIndex": 3, # Very important |
| 68 | + "aeroProblem": ap, |
| 69 | + "noOfProcessors": 8, |
| 70 | + "sliceLocation": [0.883, 1.003, 2.093, 2.612, 3.112, 3.548], |
| 71 | + "writeDeformedFFD": True |
| 72 | + } |
| 73 | + |
| 74 | + # Initialize the class |
| 75 | + wing = WingFFD(options=options) |
| 76 | + |
| 77 | +Firstly, required packages and modules are imported. Then, ``solverOptions`` dictionary is created, refer |
| 78 | +`ADflow <https://mdolab-adflow.readthedocs-hosted.com/en/latest/options.html>`_. Then, `AeroProblem <https://mdolab-baseclasses.readthedocs-hosted.com/en/latest/pyAero_problem.html>`_ |
| 79 | +object is created which contains details about the flow conditions and the desired output variables are |
| 80 | +defined using ``evalFuncs`` argument. Then, ``options`` dictionary is created, refer :ref:`options<options>` |
| 81 | +section for more details. |
| 82 | + |
| 83 | +Adding design variables |
| 84 | +----------------------- |
| 85 | + |
| 86 | +Next step is to add design variables based on which samples will be generated. The ``addDV`` method needs three arguments: |
| 87 | + |
| 88 | +- ``name (str)``: name of the design variable to add. The available design variables are: |
| 89 | + |
| 90 | + - ``shape``: FFD control points which parameterize the airfoil shape |
| 91 | + - ``twist``: Twist of the airfoil sections along the wing span, number of sections will be one less than the sections defined in the FFD file since root is fixed |
| 92 | + - ``alpha``: Angle of attack for the analysis |
| 93 | + - ``mach``: Mach number for the analysis |
| 94 | + - ``altitude``: Altitude for the analysis |
| 95 | + |
| 96 | +- ``lowerBound (numpy array or float)``: lower bound for the variable |
| 97 | +- ``upperBound (numpy array or float)``: upper bound for the variable |
| 98 | + |
| 99 | + .. note:: |
| 100 | + When ``shape`` variable is to be added, the lower and upper bound should be a 1D numpy array of the same size |
| 101 | + as the number of FFD points. The number of FFD points can be accessed via ``nffd`` attribute of the class. |
| 102 | + |
| 103 | + When ``twist`` variable is to be added, the lower and upper bound should be a 1D numpy array of the same size |
| 104 | + as the number of section defined in the FFD file minus one. The twist is defined in degrees. The number of twist |
| 105 | + sections can be accessed via ``nTwist`` attribute of the class. |
| 106 | + |
| 107 | + For other cases, lower and upper bound should be float. |
| 108 | + |
| 109 | +Following code snippet adds ``alpha``, ``shape``, and ``twist`` as design variables:: |
| 110 | + |
| 111 | + # Add alpha as a design variable |
| 112 | + wing.addDV("alpha", lowerBound=1.5, upperBound=3.5) |
| 113 | + |
| 114 | + # Add the wing shape as a design variable |
| 115 | + lowerBound = np.array([-0.01]*wing.nffd) |
| 116 | + upperBound = np.array([0.01]*wing.nffd) |
| 117 | + wing.addDV("shape", lowerBound=lowerBound, upperBound=upperBound) |
| 118 | + |
| 119 | + # Add the wing twist as a design variable |
| 120 | + lowerBound = np.array([-2.0]*wing.nTwist) |
| 121 | + upperBound = np.array([2.0]*wing.nTwist) |
| 122 | + wing.addDV("twist", lowerBound=lowerBound, upperBound=upperBound) |
| 123 | + |
| 124 | +Here, the upper and lower bound for ``shape`` variable is set to 0.01 and -0.01, respectively. |
| 125 | + |
| 126 | +Generating samples and accessing data |
| 127 | +--------------------------------------- |
| 128 | + |
| 129 | +After adding design variables, generating samples is very easy. You just need to use ``generateSamples`` |
| 130 | +method from the initialized object. This method has two arguments: |
| 131 | + |
| 132 | +- ``numSamples (int)``: number of samples to generate |
| 133 | +- ``doe (numpy array)``: 2D numpy array in which each row represents a specific sample |
| 134 | + |
| 135 | +.. note:: |
| 136 | + You can either provide ``numSamples`` or ``doe`` i.e. both of them are mutually exclusive. |
| 137 | + If both are provided, then an error will be raised. |
| 138 | + |
| 139 | +Typically, ``numSamples (int)`` should be used for generating samples. This option will internally generate doe based on the |
| 140 | +options provided while initializating the module. In some cases, you might want to generate samples based on your own doe. In that |
| 141 | +case, you use ``doe (numpy array)`` argument. Following snippet of the code will generate 5 samples using internally generated doe:: |
| 142 | + |
| 143 | + wing.generateSamples(numSamples=5) |
| 144 | + |
| 145 | +You can see the following output upon successful completion of sample generation process: |
| 146 | + |
| 147 | +- A folder with the name specificed in the ``directory`` option (or the default name - *output*) is created. This folder contains all the generated |
| 148 | + files/folders. |
| 149 | + |
| 150 | +- Within the main output folder, there will be subfolders equal to the number of samples you requested. Each of the folder corresponds to the specific |
| 151 | + analysis performed. It will contain log.txt which contains the output from mesh generation and solver. There will be other files depending on the |
| 152 | + options provided to solver and blackbox. |
| 153 | + |
| 154 | +- ``data.mat`` file which contains: |
| 155 | + |
| 156 | + - **Input variable**: a 2D numpy array ``x`` in which each row represents a specific sample based on which analysis is performed. The number |
| 157 | + of rows will be usually equal to the number of samples argument in the ``generateSamples`` method. But, many times few of the analysis |
| 158 | + fail. It depends a lot on the solver options, so set those options after some tuning. |
| 159 | + |
| 160 | + .. note:: |
| 161 | + The order of values in each row is based on how you add design variables. In this tutorial, first ``alpha`` is added as |
| 162 | + design variable and then shape coefficients are added. Thus, first value in each row will be alpha, next ``nffd`` |
| 163 | + values will be FFD coefficients, and then ``nTwist`` values will be twist values. |
| 164 | + |
| 165 | + - **Outputs**: There are two kinds of outputs - mandatory and user specificed. The ``evalFuncs`` argument in the aero problem |
| 166 | + decides the user desired outputs. Along with these outputs, `volume` of the wing is the mandatory output. Following snippet |
| 167 | + shows how to access the data.mat file. In this tutorial, ``evalFuncs`` argument contains ``cl``, ``cd``, ``cmy``. So, data.mat |
| 168 | + will contain these variables, along with ``volume``:: |
| 169 | + |
| 170 | + from scipy.io import loadmat |
| 171 | + data = loadmat("data.mat") # mention the location of mat file |
| 172 | + |
| 173 | + x = data["x"] |
| 174 | + cl = data["cl"] |
| 175 | + cd = data["cd"] |
| 176 | + cmy = data["cmy"] |
| 177 | + volume = data["volume"] |
| 178 | + |
| 179 | +- ``description.txt``: contains various informations about the sample generation such as design variables, bounds, number of failed analysis, etc. |
0 commit comments