From 3bbb23a04b7fa60c07cad33f88206d7645080f2e Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Thu, 17 Aug 2023 23:55:27 +0100 Subject: [PATCH 1/8] Quick_Start_Examples --- .../Automated_Meshing/Convergence_Plots.py | 85 +++ .../Automated_Meshing/Download_Data.py | 21 + .../Download_Plot_Sectional_Forces.py | 69 +++ .../Automated_Meshing/Download_Vis_Figures.py | 59 +++ .../Quick_Starts/Automated_Meshing/Files.py | 26 + .../Quick_Starts/Automated_Meshing/README.md | 11 + .../Automated_Meshing/Submit_Case.py | 35 ++ .../Automated_Meshing/localFiles/om6Case.json | 54 ++ .../localFiles/om6SurfaceMesh.json | 28 + .../localFiles/om6VolumeMesh.json | 48 ++ .../Automated_Meshing/localFiles/om6wing.csm | 454 ++++++++++++++++ .../Quick_Starts/ONERAM6/Convergence_Plots.py | 85 +++ .../Quick_Starts/ONERAM6/Download_Data.py | 21 + .../ONERAM6/Download_Plot_Sectional_Forces.py | 69 +++ .../ONERAM6/Download_Vis_Figures.py | 59 +++ examples/Quick_Starts/ONERAM6/Files.py | 21 + examples/Quick_Starts/ONERAM6/README.md | 16 + examples/Quick_Starts/ONERAM6/Submit_Case.py | 29 + .../ONERAM6/localFiles/Flow360.json | 55 ++ .../Quick_Starts/S809/Convergence_Plots.py | 90 ++++ examples/Quick_Starts/S809/Download_Data.py | 21 + examples/Quick_Starts/S809/Files.py | 28 + examples/Quick_Starts/S809/README.md | 11 + examples/Quick_Starts/S809/Submit_Case.py | 45 ++ .../Quick_Starts/S809/localFiles/s809.csm | 500 ++++++++++++++++++ .../S809/localFiles/s809Case.json | 61 +++ .../S809/localFiles/s809SurfaceMesh.json | 25 + .../S809/localFiles/s809VolumeMesh.json | 51 ++ 28 files changed, 2077 insertions(+) create mode 100644 examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py create mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Data.py create mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py create mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py create mode 100644 examples/Quick_Starts/Automated_Meshing/Files.py create mode 100644 examples/Quick_Starts/Automated_Meshing/README.md create mode 100644 examples/Quick_Starts/Automated_Meshing/Submit_Case.py create mode 100644 examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json create mode 100644 examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json create mode 100644 examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json create mode 100644 examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm create mode 100644 examples/Quick_Starts/ONERAM6/Convergence_Plots.py create mode 100644 examples/Quick_Starts/ONERAM6/Download_Data.py create mode 100644 examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py create mode 100644 examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py create mode 100644 examples/Quick_Starts/ONERAM6/Files.py create mode 100644 examples/Quick_Starts/ONERAM6/README.md create mode 100644 examples/Quick_Starts/ONERAM6/Submit_Case.py create mode 100644 examples/Quick_Starts/ONERAM6/localFiles/Flow360.json create mode 100644 examples/Quick_Starts/S809/Convergence_Plots.py create mode 100644 examples/Quick_Starts/S809/Download_Data.py create mode 100644 examples/Quick_Starts/S809/Files.py create mode 100644 examples/Quick_Starts/S809/README.md create mode 100644 examples/Quick_Starts/S809/Submit_Case.py create mode 100644 examples/Quick_Starts/S809/localFiles/s809.csm create mode 100644 examples/Quick_Starts/S809/localFiles/s809Case.json create mode 100644 examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json create mode 100644 examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json diff --git a/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py b/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py new file mode 100644 index 0000000..35794ed --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py @@ -0,0 +1,85 @@ +import matplotlib.pyplot as plt +from collections import defaultdict +import os, csv +import numpy as np +import pandas as pd + + + + +def plotLoads(data, plotName): + axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) + axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) + axes[0].set_ylabel('CL'); + axes[1].set_ylabel('CD'); + axes[0].set_ylim(0.27, 0.28); + axes[1].set_ylim(0.017, 0.018); + + for ax in axes: + ax.legend() + ax.set_xlabel('Pseudo step'); + ax.grid(which='major', color='gray') + +def plotResiduals(res, plotName): + x = res['pseudo_step'] + keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] + labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] + for ax, key, label in zip(axes, keys, labels): + for j, key in enumerate(keys): + ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) + ax.set_ylabel('Residuals') + ax.legend() + ax.set_title("ONERAM6 - Automated Meshing") + ax.grid(which='major', color='gray') + ax.set_xlabel('Pseudo step'); + ax.set_yticks(10.0**np.arange(-14,-3)); + ax.set_ylim([1e-9,1e-2]) + ax.set_xlim([0,5000]) + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +naming = ["ONERAM6"] +figures = [] +axes = [] + +def main(): + + #Plot loads convergence histories + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + + csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotLoads(data, naming[j]) + + + figures[0].savefig(f'figures/CL.png', dpi=500); + figures[1].savefig(f'figures/CD.png', dpi=500); + + for i in range(0,2): + axes[i].cla(); + + #Plot residual convergence histories + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') + res = pd.read_csv(csvPath, skipinitialspace=True) + plotResiduals(res, naming[j]) + + figures[0].savefig(f'figures/Residuals.png', dpi=500); + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Data.py b/examples/Quick_Starts/Automated_Meshing/Download_Data.py new file mode 100644 index 0000000..abb056c --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Download_Data.py @@ -0,0 +1,21 @@ +import os +import flow360 as fl +from flow360 import MyCases + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + #Download the files + case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py new file mode 100644 index 0000000..e2ae51f --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py @@ -0,0 +1,69 @@ +import os, csv +import numpy as np +import flow360 as fl +import pandas as pd +from flow360 import MyCases +import matplotlib.pyplot as plt +from collections import defaultdict + + + +def plotSectionalForces(data, plotName): + axes[0].plot(data['Y'], data['fluid/wing_CFx_per_span'], label=plotName) + axes[1].plot(data['Y'], data['fluid/wing_CFz_per_span'], label=plotName) + axes[0].set_ylabel('CFx per span'); + axes[1].set_ylabel('CFz per span'); + for ax in axes: + ax.legend() + ax.set_xlabel('Y'); + ax.grid(which='major', color='gray') + + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +naming = ["ONERAM6"] +figures = [] +axes = [] + + +def main(): + + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + forces = f"results/postprocess/forceDistribution.csv" + #print(case.results) + case._download_file(forces, to_folder=caseFolder) + + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotSectionalForces(data, naming[j]) + + figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); + figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); + + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py b/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py new file mode 100644 index 0000000..07333f1 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py @@ -0,0 +1,59 @@ +import os +import numpy as np +import flow360 as fl +#from flow360.component.case import CaseDownloadable +#from flow360.component.resource_base import Flow360Resource + +from flow360 import MyCases +from PIL import Image + +def fetchPNG(case, field, res = 'H'): + # download the legend i.e. color bar + casename = case.name + os.makedirs('visualize', exist_ok=True) + + legend = f'visualize/{field}Legend.png' + case._download_file(legend, to_file=legend) + + # list all available theta and phi + viewAngles = [(0, 0),(180, 0)] + for theta in [60, 120]: + for phi in range(0,360,90): + viewAngles.append((theta, phi)) + # download the contour + axis files + for theta, phi in viewAngles: + fname = f'{theta:03d}_{phi:03d}.png' + contour = f'visualize/{field}_{res}_{fname}' + axis = f'visualize/Ax_{fname}' + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + imgContour = Image.open(contour).convert('RGBA') + imgAxis = Image.open(axis) + imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) + imgAxis = imgAxis.convert('RGBA') + imgLegend = Image.open(legend).convert('RGBA') + background = Image.new('RGBA', imgContour.size, (67,100,200)) + imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) + imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) + background.paste(imgContour, (0,0), imgContour) + background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') + +def main(): + os.makedirs('visualization_figures', exist_ok=True) + with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + my_cases = MyCases() + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + fetchPNG(case, 'Cp') + fetchPNG(case, 'Cf') + fetchPNG(case, 'Fv') + +if __name__ == "__main__": + main() diff --git a/examples/Quick_Starts/Automated_Meshing/Files.py b/examples/Quick_Starts/Automated_Meshing/Files.py new file mode 100644 index 0000000..bfe15f8 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Files.py @@ -0,0 +1,26 @@ +import os +from flow360.examples import base_test_case +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--localFiles', default = 0, type = int, required = False) +args = parser.parse_args() +local = args.localFiles + +base_test_case.here = os.path.dirname(os.path.abspath(__file__)) + +class ONERAM6(base_test_case.BaseTestCase): + name = "localFiles" + if local==1: + class url: + geometry = "local://om6wing.csm" + surface_json = "local://om6SurfaceMesh.json" + volume_json = "local://om6VolumeMesh.json" + case_json = "local://om6Case.json" + else: + class url: + geometry = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6wing.csm" + surface_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6SurfaceMesh.json" + volume_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6VolumeMesh.json" + case_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6Case.json" + diff --git a/examples/Quick_Starts/Automated_Meshing/README.md b/examples/Quick_Starts/Automated_Meshing/README.md new file mode 100644 index 0000000..c829d53 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/README.md @@ -0,0 +1,11 @@ +To run the demo case follow these steps: + +1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. + +2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. + +3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. + +Files.py contains reference to the location of the meshes and JSON files. The default option is to use files from Flexcompute storage servers. To run with files stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 + + diff --git a/examples/Quick_Starts/Automated_Meshing/Submit_Case.py b/examples/Quick_Starts/Automated_Meshing/Submit_Case.py new file mode 100644 index 0000000..d847b14 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/Submit_Case.py @@ -0,0 +1,35 @@ +import os +import flow360 as fl +from Files import ONERAM6 + +print("Obtaining mesh and solver files") + +ONERAM6.get_files() + +print("Files accessed") + +caseNameList=[] + +params = fl.SurfaceMeshingParams(ONERAM6.surface_json) +surface_mesh = fl.SurfaceMesh.create( + ONERAM6.geometry, params=params, name="ONERAM6_Automated_Meshing_Surface" +) +surface_mesh = surface_mesh.submit() + + +params = fl.VolumeMeshingParams(ONERAM6.volume_json) +volume_mesh = surface_mesh.create_volume_mesh("ONERAM6_Automated_Meshing_Volume", params=params) +volume_mesh = volume_mesh.submit() + +params = fl.Flow360Params(ONERAM6.case_json) + +#submit case +case = fl.Case.create("ONERAM6_Automated_Meshing", params, volume_mesh.id) +caseNameList.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open('caseNameList.dat', 'w') as f: + for line in caseNameList: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json b/examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json new file mode 100644 index 0000000..2028d61 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json @@ -0,0 +1,54 @@ +{ + "geometry" : { + "refArea" : 1.15315084119231, + "momentCenter" : [0.0, 0.0, 0.0], + "momentLength" : [1.47602, 0.801672958512342, 1.47602] + }, + "volumeOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Mach"] + }, + "surfaceOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Cp", "Cf"] + }, + "navierStokesSolver" : { + "absoluteTolerance" : 1e-10, + "kappaMUSCL" : -1.0 + }, + "turbulenceModelSolver" : { + "modelType" : "SpalartAllmaras", + "absoluteTolerance" : 1e-8 + }, + "freestream" : + { + "Reynolds" : 14.6e+6, + "Mach" : 0.84, + "Temperature" : 297.78, + "alphaAngle" : 3.06, + "betaAngle" : 0.0 + }, + "boundaries": { + "fluid/symmetric": { + "type": "SlipWall" + }, + "fluid/wing": { + "type": "NoSlipWall" + }, + "fluid/farfield": { + "type": "Freestream" + } + }, + "timeStepping" : { + "physicalSteps" : 1, + "timeStepSize" : "inf", + "maxPseudoSteps" : 5000, + "CFL" : { + "initial" : 1, + "final": 200, + "rampSteps" : 2250 + } + } +} diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json b/examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json new file mode 100644 index 0000000..fb14ade --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json @@ -0,0 +1,28 @@ +{ + "maxEdgeLength": 0.15, + "curvatureResolutionAngle": 10, + "growthRate": 1.07, + "edges": { + "wingLeadingEdge": { + "type": "aniso", + "method": "height", + "value": 3e-4 + }, + "wingTrailingEdge": { + "type": "aniso", + "method": "height", + "value": 3e-4 + }, + "rootAirfoilEdge": { + "type": "projectAnisoSpacing" + }, + "tipAirfoilEdge": { + "type": "projectAnisoSpacing" + } + }, + "faces": { + "wing": { + "maxEdgeLength": 0.15 + } + } +} diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json b/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json new file mode 100644 index 0000000..2840c95 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json @@ -0,0 +1,48 @@ +{ + "refinement": [ + { + "type": "cylinder", + "radius": 1.1, + "length": 2.0, + "spacing": 0.075, + "axis": [0,1,0], + "center": [0.7,-1.0,0] + }, + { + "type": "cylinder", + "radius": 2.2, + "length": 2.0, + "spacing": 0.1, + "axis": [0,1,0], + "center": [0.7,-1.0,0] + }, + { + "type": "cylinder", + "radius": 3.3, + "length": 2.0, + "spacing": 0.175, + "axis": [0,1,0], + "center": [0.7,-1.0,0] + }, + { + "type": "cylinder", + "radius": 4.5, + "length": 2.0, + "spacing": 0.225, + "axis": [0,1,0], + "center": [0.7,-1.0,0] + }, + { + "type": "cylinder", + "radius": 6.5, + "length": 14.5, + "spacing": 0.3, + "axis": [-1,0,0], + "center": [2,-1.0,0] + } + ], + "volume": { + "firstLayerThickness": 1.35e-06, + "growthRate": 1.04 + } +} diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm b/examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm new file mode 100644 index 0000000..d1ad7a4 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm @@ -0,0 +1,454 @@ +# OM6 WING TUTORIAL STEP 1 | 3D Sketches of Airfoils +#============================================================================================ +mark +skbeg 0.805900000 0.000000000 0.0000000000 +spline 0.804129438 0.000231777 0.0000000000 +spline 0.802038127 0.00050538 0.0000000000 +spline 0.799568688 0.000828465 0.0000000000 +spline 0.796652459 0.001209978 0.0000000000 +spline 0.79320772 0.001660638 0.0000000000 +spline 0.789138892 0.002192934 0.0000000000 +spline 0.784330973 0.002821859 0.0000000000 +spline 0.778649297 0.003565302 0.0000000000 +spline 0.771932201 0.004444055 0.0000000000 +spline 0.763988848 0.005483182 0.0000000000 +spline 0.754592054 0.006710326 0.0000000000 +spline 0.743469828 0.008150711 0.0000000000 +spline 0.730298924 0.009828112 0.0000000000 +spline 0.714691059 0.01168966 0.0000000000 +spline 0.696181873 0.01377404 0.0000000000 +spline 0.677546241 0.015782584 0.0000000000 +spline 0.658809389 0.017717067 0.0000000000 +spline 0.639969622 0.019585546 0.0000000000 +spline 0.621026379 0.021397048 0.0000000000 +spline 0.601979738 0.023158745 0.0000000000 +spline 0.582826477 0.024875071 0.0000000000 +spline 0.563568046 0.026546507 0.0000000000 +spline 0.544202188 0.02816959 0.0000000000 +spline 0.524728985 0.029736904 0.0000000000 +spline 0.505146501 0.031237732 0.0000000000 +spline 0.485454577 0.032658372 0.0000000000 +spline 0.465652485 0.033983514 0.0000000000 +spline 0.445738213 0.035196957 0.0000000000 +spline 0.425710711 0.036282343 0.0000000000 +spline 0.405570303 0.037224763 0.0000000000 +spline 0.385316666 0.03801116 0.0000000000 +spline 0.36494706 0.038630897 0.0000000000 +spline 0.344459632 0.039072691 0.0000000000 +spline 0.323856315 0.039344199 0.0000000000 +spline 0.303134933 0.039432365 0.0000000000 +spline 0.28229307 0.039342668 0.0000000000 +spline 0.261331288 0.039078252 0.0000000000 +spline 0.240247897 0.038642421 0.0000000000 +spline 0.219041847 0.038037432 0.0000000000 +spline 0.197712253 0.037261431 0.0000000000 +spline 0.176258067 0.036306359 0.0000000000 +spline 0.154678563 0.035154486 0.0000000000 +spline 0.132971566 0.033774383 0.0000000000 +spline 0.111130628 0.032116888 0.0000000000 +spline 0.09266206 0.030456815 0.0000000000 +spline 0.077072569 0.028830428 0.0000000000 +spline 0.063937366 0.027269399 0.0000000000 +spline 0.052891459 0.025799921 0.0000000000 +spline 0.043619176 0.024441174 0.0000000000 +spline 0.035850623 0.023202828 0.0000000000 +spline 0.029355794 0.022026617 0.0000000000 +spline 0.023935875 0.020811965 0.0000000000 +spline 0.01942759 0.019503102 0.0000000000 +spline 0.015683539 0.018096082 0.0000000000 +spline 0.012585821 0.01661927 0.0000000000 +spline 0.010031763 0.015113607 0.0000000000 +spline 0.007931104 0.013618421 0.0000000000 +spline 0.006214456 0.012165141 0.0000000000 +spline 0.004815333 0.010775528 0.0000000000 +spline 0.003683285 0.009462797 0.0000000000 +spline 0.002774553 0.008233316 0.0000000000 +spline 0.00205029 0.007088535 0.0000000000 +spline 0.001479955 0.006026843 0.0000000000 +spline 0.001037032 0.005044773 0.0000000000 +spline 0.000697668 0.004137732 0.0000000000 +spline 0.00044389 0.003300886 0.0000000000 +spline 0.000260467 0.002529075 0.0000000000 +spline 0.000134988 0.001817627 0.0000000000 +spline 5.60906E-05 0.001161785 0.0000000000 +spline 1.32974E-05 0.000557199 0.0000000000 +spline 0.000000000 0.000000000 0.0000000000 +skend +skbeg 0.000000000 0.000000000 0.000000000 +spline 1.32974E-05 -0.000557199 0.000000000 +spline 5.60906E-05 -0.001161785 0.000000000 +spline 0.000134988 -0.001817627 0.000000000 +spline 0.000260467 -0.002529075 0.000000000 +spline 0.00044389 -0.003300886 0.000000000 +spline 0.000697668 -0.004137732 0.000000000 +spline 0.001037032 -0.005044773 0.000000000 +spline 0.001479955 -0.006026843 0.000000000 +spline 0.00205029 -0.007088535 0.000000000 +spline 0.002774553 -0.008233316 0.000000000 +spline 0.003683285 -0.009462797 0.000000000 +spline 0.004815333 -0.010775528 0.000000000 +spline 0.006214456 -0.012165141 0.000000000 +spline 0.007931104 -0.013618421 0.000000000 +spline 0.010031763 -0.015113607 0.000000000 +spline 0.012585821 -0.01661927 0.000000000 +spline 0.015683539 -0.018096082 0.000000000 +spline 0.01942759 -0.019503102 0.000000000 +spline 0.023935875 -0.020811965 0.000000000 +spline 0.029355794 -0.022026617 0.000000000 +spline 0.035850623 -0.023202828 0.000000000 +spline 0.043619176 -0.024441174 0.000000000 +spline 0.052891459 -0.025799921 0.000000000 +spline 0.063937366 -0.027269399 0.000000000 +spline 0.077072569 -0.028830428 0.000000000 +spline 0.09266206 -0.030456815 0.000000000 +spline 0.111130628 -0.032116888 0.000000000 +spline 0.132971566 -0.033774383 0.000000000 +spline 0.154678563 -0.035154486 0.000000000 +spline 0.176258067 -0.036306359 0.000000000 +spline 0.197712253 -0.037261431 0.000000000 +spline 0.219041847 -0.038037432 0.000000000 +spline 0.240247897 -0.038642421 0.000000000 +spline 0.261331288 -0.039078252 0.000000000 +spline 0.28229307 -0.039342668 0.000000000 +spline 0.303134933 -0.039432365 0.000000000 +spline 0.323856315 -0.039344199 0.000000000 +spline 0.344459632 -0.039072691 0.000000000 +spline 0.36494706 -0.038630897 0.000000000 +spline 0.385316666 -0.03801116 0.000000000 +spline 0.405570303 -0.037224763 0.000000000 +spline 0.425710711 -0.036282343 0.000000000 +spline 0.445738213 -0.035196957 0.000000000 +spline 0.465652485 -0.033983514 0.000000000 +spline 0.485454577 -0.032658372 0.000000000 +spline 0.505146501 -0.031237732 0.000000000 +spline 0.524728985 -0.029736904 0.000000000 +spline 0.544202188 -0.02816959 0.000000000 +spline 0.563568046 -0.026546507 0.000000000 +spline 0.582826477 -0.024875071 0.000000000 +spline 0.601979738 -0.023158745 0.000000000 +spline 0.621026379 -0.021397048 0.000000000 +spline 0.639969622 -0.019585546 0.000000000 +spline 0.658809389 -0.017717067 0.000000000 +spline 0.677546241 -0.015782584 0.000000000 +spline 0.696181873 -0.01377404 0.000000000 +spline 0.714691059 -0.01168966 0.000000000 +spline 0.730298924 -0.009828112 0.000000000 +spline 0.743469828 -0.008150711 0.000000000 +spline 0.754592054 -0.006710326 0.000000000 +spline 0.763988848 -0.005483182 0.000000000 +spline 0.771932201 -0.004444055 0.000000000 +spline 0.778649297 -0.003565302 0.000000000 +spline 0.784330973 -0.002821859 0.000000000 +spline 0.789138892 -0.002192934 0.000000000 +spline 0.79320772 -0.001660638 0.000000000 +spline 0.796652459 -0.001209978 0.000000000 +spline 0.799568688 -0.000828465 0.000000000 +spline 0.802038127 -0.00050538 0.000000000 +spline 0.804129438 -0.000231777 0.000000000 +skend +group +store rootAirfoil + +mark +skbeg 0.45333164 0.000000000 0.000000000 +spline 0.45233567 0.000130378 0.000000000 +spline 0.451159275 0.000284284 0.000000000 +spline 0.449770176 0.000466025 0.000000000 +spline 0.44812975 0.000680632 0.000000000 +spline 0.446192029 0.000934135 0.000000000 +spline 0.443903249 0.001233561 0.000000000 +spline 0.441198717 0.001587341 0.000000000 +spline 0.438002684 0.002005539 0.000000000 +spline 0.43422421 0.002499852 0.000000000 +spline 0.429755947 0.003084378 0.000000000 +spline 0.4244701 0.003774666 0.000000000 +spline 0.41821367 0.004584906 0.000000000 +spline 0.410804826 0.00552847 0.000000000 +spline 0.402025152 0.006575621 0.000000000 +spline 0.391613439 0.007748118 0.000000000 +spline 0.381130598 0.008877956 0.000000000 +spline 0.370590818 0.009966133 0.000000000 +spline 0.359993149 0.011017183 0.000000000 +spline 0.349337271 0.012036182 0.000000000 +spline 0.338623231 0.013027165 0.000000000 +spline 0.327849215 0.013992625 0.000000000 +spline 0.31701604 0.014932835 0.000000000 +spline 0.306122435 0.015845845 0.000000000 +spline 0.295168447 0.016727484 0.000000000 +spline 0.284152987 0.017571724 0.000000000 +spline 0.273075964 0.018370857 0.000000000 +spline 0.261936971 0.01911627 0.000000000 +spline 0.250734874 0.019798851 0.000000000 +spline 0.239469084 0.020409398 0.000000000 +spline 0.228139782 0.020939524 0.000000000 +spline 0.216746788 0.021381885 0.000000000 +spline 0.205288559 0.021730497 0.000000000 +spline 0.193764052 0.021979014 0.000000000 +spline 0.182174357 0.022131741 0.000000000 +spline 0.170518248 0.022181336 0.000000000 +spline 0.158794367 0.02213088 0.000000000 +spline 0.14700303 0.021982142 0.000000000 +spline 0.135143285 0.02173698 0.000000000 +spline 0.123214542 0.021396664 0.000000000 +spline 0.111216305 0.020960151 0.000000000 +spline 0.099147982 0.020422908 0.000000000 +spline 0.087009166 0.019774961 0.000000000 +spline 0.074798633 0.01899863 0.000000000 +spline 0.062512756 0.018066263 0.000000000 +spline 0.052123891 0.017132445 0.000000000 +spline 0.043354553 0.016217577 0.000000000 +spline 0.035965791 0.015339473 0.000000000 +spline 0.029752292 0.014512868 0.000000000 +spline 0.024536484 0.013748551 0.000000000 +spline 0.020166549 0.013051962 0.000000000 +spline 0.016513104 0.012390324 0.000000000 +spline 0.013464312 0.011707063 0.000000000 +spline 0.01092833 0.010970807 0.000000000 +spline 0.008822242 0.010179335 0.000000000 +spline 0.007079726 0.009348605 0.000000000 +spline 0.005643027 0.008501646 0.000000000 +spline 0.004461373 0.007660579 0.000000000 +spline 0.003495731 0.006843086 0.000000000 +spline 0.002708702 0.006061407 0.000000000 +spline 0.002071907 0.005322975 0.000000000 +spline 0.00156073 0.004631372 0.000000000 +spline 0.001153321 0.003987414 0.000000000 +spline 0.000832498 0.003390195 0.000000000 +spline 0.000583347 0.002837765 0.000000000 +spline 0.000392449 0.002327541 0.000000000 +spline 0.000249695 0.001856801 0.000000000 +spline 0.000146517 0.001422645 0.000000000 +spline 7.5933E-05 0.001022444 0.000000000 +spline 3.15519E-05 0.000653523 0.000000000 +spline 7.47997E-06 0.000313433 0.000000000 +spline 0.000000000 0.000000000 0.000000000 +skend +skbeg 0.00000000 0.000000000 0.000000000 +spline 7.47997E-06 -0.000313433 0.000000000 +spline 3.15519E-05 -0.000653523 0.000000000 +spline 7.5933E-05 -0.001022444 0.000000000 +spline 0.000146517 -0.001422645 0.000000000 +spline 0.000249695 -0.001856801 0.000000000 +spline 0.000392449 -0.002327541 0.000000000 +spline 0.000583347 -0.002837765 0.000000000 +spline 0.000832498 -0.003390195 0.000000000 +spline 0.001153321 -0.003987414 0.000000000 +spline 0.00156073 -0.004631372 0.000000000 +spline 0.002071907 -0.005322975 0.000000000 +spline 0.002708702 -0.006061407 0.000000000 +spline 0.003495731 -0.006843086 0.000000000 +spline 0.004461373 -0.007660579 0.000000000 +spline 0.005643027 -0.008501646 0.000000000 +spline 0.007079726 -0.009348605 0.000000000 +spline 0.008822242 -0.010179335 0.000000000 +spline 0.01092833 -0.010970807 0.000000000 +spline 0.013464312 -0.011707063 0.000000000 +spline 0.016513104 -0.012390324 0.000000000 +spline 0.020166549 -0.013051962 0.000000000 +spline 0.024536484 -0.013748551 0.000000000 +spline 0.029752292 -0.014512868 0.000000000 +spline 0.035965791 -0.015339473 0.000000000 +spline 0.043354553 -0.016217577 0.000000000 +spline 0.052123891 -0.017132445 0.000000000 +spline 0.062512756 -0.018066263 0.000000000 +spline 0.074798633 -0.01899863 0.000000000 +spline 0.087009166 -0.019774961 0.000000000 +spline 0.099147982 -0.020422908 0.000000000 +spline 0.111216305 -0.020960151 0.000000000 +spline 0.123214542 -0.021396664 0.000000000 +spline 0.135143285 -0.02173698 0.000000000 +spline 0.14700303 -0.021982142 0.000000000 +spline 0.158794367 -0.02213088 0.000000000 +spline 0.170518248 -0.022181336 0.000000000 +spline 0.182174357 -0.022131741 0.000000000 +spline 0.193764052 -0.021979014 0.000000000 +spline 0.205288559 -0.021730497 0.000000000 +spline 0.216746788 -0.021381885 0.000000000 +spline 0.228139782 -0.020939524 0.000000000 +spline 0.239469084 -0.020409398 0.000000000 +spline 0.250734874 -0.019798851 0.000000000 +spline 0.261936971 -0.01911627 0.000000000 +spline 0.273075964 -0.018370857 0.000000000 +spline 0.284152987 -0.017571724 0.000000000 +spline 0.295168447 -0.016727484 0.000000000 +spline 0.306122435 -0.015845845 0.000000000 +spline 0.31701604 -0.014932835 0.000000000 +spline 0.327849215 -0.013992625 0.000000000 +spline 0.338623231 -0.013027165 0.000000000 +spline 0.349337271 -0.012036182 0.000000000 +spline 0.359993149 -0.011017183 0.000000000 +spline 0.370590818 -0.009966133 0.000000000 +spline 0.381130598 -0.008877956 0.000000000 +spline 0.391613439 -0.007748118 0.000000000 +spline 0.402025152 -0.006575621 0.000000000 +spline 0.410804826 -0.00552847 0.000000000 +spline 0.41821367 -0.004584906 0.000000000 +spline 0.4244701 -0.003774666 0.000000000 +spline 0.429755947 -0.003084378 0.000000000 +spline 0.43422421 -0.002499852 0.000000000 +spline 0.438002684 -0.002005539 0.000000000 +spline 0.441198717 -0.001587341 0.000000000 +spline 0.443903249 -0.001233561 0.000000000 +spline 0.446192029 -0.000934135 0.000000000 +spline 0.44812975 -0.000680632 0.000000000 +spline 0.449770176 -0.000466025 0.000000000 +spline 0.451159275 -0.000284284 0.000000000 +spline 0.45233567 -0.000130378 0.000000000 +skend +group +store tipAirfoil + +# OM6 WING TUTORIAL STEP 2 | Wing Planform +#============================================================================================ + +# SWEPT WING OM6 PARAMETERS +#-------------------------------------------------------------------------------------------- +DESPMTR span 1.1963 +CONPMTR LEsweep 30 + +# TIP FACE +#-------------------------------------------------------------------------------------------- +restore tipAirfoil +join +rotatex 90 0 0 +translate span/tand(90-LEsweep) -span 0 + +# COLLECTING NODE COORDINATES AT TIP FACE TO ADD ATTRIBUTE AFTER BLEND +#-------------------------------------------------------------------------------------------- +dimension tipLeadingPoint 1 3 +dimension tipTrailingPointUp 1 3 +dimension tipTrailingPointLow 1 3 + +set xmin @xmax +set zmin @zmax +set zmax @zmin + +set numNodes @nnode +patbeg inode numNodes + evaluate node @ibody inode + IFTHEN @edata[1] LT xmin + set xmin @edata[1] + set tipLeadingPoint "xmin;@edata[2];@edata[3];" + set leadingNodeID inode + ENDIF +patend + +patbeg inode numNodes + IFTHEN inode NE leadingNodeID + evaluate node @ibody inode + IFTHEN @edata[3] GT zmax + set zmax @edata[3] + set tipTrailingPointUp "@edata[1];@edata[2];zmax;" + ENDIF + + IFTHEN @edata[3] LT zmin + set zmin @edata[3] + set tipTrailingPointLow "@edata[1];@edata[2];zmin;" + ENDIF + ENDIF +patend + +# TIP AIRFOIL --> TIP FACE +combine +store tipFace + +# ROOT FACE +#-------------------------------------------------------------------------------------------- +restore rootAirfoil +join +rotatex 90 0 0 + +# COLLECTING NODE COORDINATES AT ROOT FACE TO ADD ATTRIBUTE AFTER BLEND +#-------------------------------------------------------------------------------------------- +# defining arrays with one row and three columns +dimension rootLeadingPoint 1 3 +dimension rootTrailingPointUp 1 3 +dimension rootTrailingPointLow 1 3 + +# initializing xmin, zmin, zmax with their opposite extremes +set xmin @xmax +set zmin @zmax +set zmax @zmin + +set numNodes @nnode +patbeg inode numNodes + evaluate node @ibody inode + IFTHEN @edata[1] LT xmin + set xmin @edata[1] + set rootLeadingPoint "xmin;@edata[2];@edata[3];" + set leadingNodeID inode + ENDIF +patend + +patbeg inode numNodes + IFTHEN inode NE leadingNodeID + evaluate node @ibody inode + IFTHEN @edata[3] GT zmax + set zmax @edata[3] + set rootTrailingPointUp "@edata[1];@edata[2];zmax;" + ENDIF + + IFTHEN @edata[3] LT zmin + set zmin @edata[3] + set rootTrailingPointLow "@edata[1];@edata[2];zmin;" + ENDIF + ENDIF +patend + +# ROOT AIRFOIL --> ROOT FACE +combine +store rootFace + +# OM6 WING TUTORIAL STEP 3 | Wing Solid Model +#============================================================================================ +mark +restore tipFace +restore rootFace +blend "-1;1.1" + +# OM6 WING TUTORIAL STEP 4 | Adding Attributes | Method 1: Using Edge Attribute UDPRIM +#============================================================================================= + +#ADDING EDGE ATTRIBUTES +#-------------------------------------------------------------------------------------------- + +# METHOD 1: USING COORDINATES OF NODES +udparg edgeAttr attrname $edgeName attrstr $wingLeadingEdge +udprim edgeAttr xyzs "rootLeadingPoint[1];rootLeadingPoint[2];rootLeadingPoint[3];tipLeadingPoint[1];tipLeadingPoint[2];tipLeadingPoint[3];" +udparg edgeAttr attrname $edgeName attrstr $wingTrailingEdge +udprim edgeAttr xyzs "rootTrailingPointUp[1];rootTrailingPointUp[2];rootTrailingPointUp[3];tipTrailingPointUp[1];tipTrailingPointUp[2];tipTrailingPointUp[3];" +udparg edgeAttr attrname $edgeName attrstr $wingTrailingEdge +udprim edgeAttr xyzs "rootTrailingPointLow[1];rootTrailingPointLow[2];rootTrailingPointLow[3];tipTrailingPointLow[1];tipTrailingPointLow[2];tipTrailingPointLow[3];" + + +# OM6 WING TUTORIAL STEP 4 | Adding Attributes | Method 2: Using Bounding Box +#============================================================================================= + +# METHOD 2: USING BOUNDING BOX +select edge @xmin @xmax -0.01 0.01 @zmin @zmax +attribute edgeName $rootAirfoilEdge + +# OM6 WING TUTORIAL STEP 4 | Adding Attributes | Method 3: Using Bounding Box +#============================================================================================= + +# METHOD 3: USING EDGE ID +select edge 2 +attribute edgeName $tipAirfoilEdge + +select edge 5 +attribute edgeName $tipAirfoilEdge + + +# OM6 WING TUTORIAL STEP 5 | Adding Face and Group Attributes +#============================================================================================= + +store wingModel + +#ADDING FACE AND GROUP ATTRIBUTE +#-------------------------------------------------------------------------------------------- +restore wingModel +attribute faceName $wing +attribute groupName $wing +scale 1/0.8059 0 0 0 + +END diff --git a/examples/Quick_Starts/ONERAM6/Convergence_Plots.py b/examples/Quick_Starts/ONERAM6/Convergence_Plots.py new file mode 100644 index 0000000..5e02433 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Convergence_Plots.py @@ -0,0 +1,85 @@ +import matplotlib.pyplot as plt +from collections import defaultdict +import os, csv +import numpy as np +import pandas as pd + + + + +def plotLoads(data, plotName): + axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) + axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) + axes[0].set_ylabel('CL'); + axes[1].set_ylabel('CD'); + axes[0].set_ylim(0.255, 0.265); + axes[1].set_ylim(0.0203, 0.0212); + + for ax in axes: + ax.legend() + ax.set_xlabel('Pseudo step'); + ax.grid(which='major', color='gray') + +def plotResiduals(res, plotName): + x = res['pseudo_step'] + keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] + labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] + for ax, key, label in zip(axes, keys, labels): + for j, key in enumerate(keys): + ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) + ax.set_ylabel('Residuals') + ax.legend() + ax.set_title("ONERAM6 - Quickstart") + ax.grid(which='major', color='gray') + ax.set_xlabel('Pseudo step'); + ax.set_yticks(10.0**np.arange(-14,-3)); + ax.set_ylim([1e-9,1e-2]) + ax.set_xlim([0,500]) + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +naming = ["ONERAM6"] +figures = [] +axes = [] + +def main(): + + #Plot loads convergence histories + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + + csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotLoads(data, naming[j]) + + + figures[0].savefig(f'figures/CL.png', dpi=500); + figures[1].savefig(f'figures/CD.png', dpi=500); + + for i in range(0,2): + axes[i].cla(); + + #Plot residual convergence histories + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') + res = pd.read_csv(csvPath, skipinitialspace=True) + plotResiduals(res, naming[j]) + + figures[0].savefig(f'figures/Residuals.png', dpi=500); + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/ONERAM6/Download_Data.py b/examples/Quick_Starts/ONERAM6/Download_Data.py new file mode 100644 index 0000000..abb056c --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Download_Data.py @@ -0,0 +1,21 @@ +import os +import flow360 as fl +from flow360 import MyCases + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + #Download the files + case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py new file mode 100644 index 0000000..a26a629 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py @@ -0,0 +1,69 @@ +import os, csv +import numpy as np +import flow360 as fl +import pandas as pd +from flow360 import MyCases +import matplotlib.pyplot as plt +from collections import defaultdict + + + +def plotSectionalForces(data, plotName): + axes[0].plot(data['Y'], data['wing_CFx_per_span'], label=plotName) + axes[1].plot(data['Y'], data['wing_CFz_per_span'], label=plotName) + axes[0].set_ylabel('CFx per span'); + axes[1].set_ylabel('CFz per span'); + for ax in axes: + ax.legend() + ax.set_xlabel('Y'); + ax.grid(which='major', color='gray') + + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +naming = ["ONERAM6"] +figures = [] +axes = [] + + +def main(): + + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + forces = f"results/postprocess/forceDistribution.csv" + #print(case.results) + case._download_file(forces, to_folder=caseFolder) + + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotSectionalForces(data, naming[j]) + + figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); + figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); + + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py b/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py new file mode 100644 index 0000000..07333f1 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py @@ -0,0 +1,59 @@ +import os +import numpy as np +import flow360 as fl +#from flow360.component.case import CaseDownloadable +#from flow360.component.resource_base import Flow360Resource + +from flow360 import MyCases +from PIL import Image + +def fetchPNG(case, field, res = 'H'): + # download the legend i.e. color bar + casename = case.name + os.makedirs('visualize', exist_ok=True) + + legend = f'visualize/{field}Legend.png' + case._download_file(legend, to_file=legend) + + # list all available theta and phi + viewAngles = [(0, 0),(180, 0)] + for theta in [60, 120]: + for phi in range(0,360,90): + viewAngles.append((theta, phi)) + # download the contour + axis files + for theta, phi in viewAngles: + fname = f'{theta:03d}_{phi:03d}.png' + contour = f'visualize/{field}_{res}_{fname}' + axis = f'visualize/Ax_{fname}' + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + imgContour = Image.open(contour).convert('RGBA') + imgAxis = Image.open(axis) + imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) + imgAxis = imgAxis.convert('RGBA') + imgLegend = Image.open(legend).convert('RGBA') + background = Image.new('RGBA', imgContour.size, (67,100,200)) + imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) + imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) + background.paste(imgContour, (0,0), imgContour) + background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') + +def main(): + os.makedirs('visualization_figures', exist_ok=True) + with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + my_cases = MyCases() + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + fetchPNG(case, 'Cp') + fetchPNG(case, 'Cf') + fetchPNG(case, 'Fv') + +if __name__ == "__main__": + main() diff --git a/examples/Quick_Starts/ONERAM6/Files.py b/examples/Quick_Starts/ONERAM6/Files.py new file mode 100644 index 0000000..a69b6ad --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Files.py @@ -0,0 +1,21 @@ +import os +from flow360.examples import base_test_case +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--localFiles', default = 0, type = int, required = False) +args = parser.parse_args() +local = args.localFiles + +base_test_case.here = os.path.dirname(os.path.abspath(__file__)) + +class ONERAM6(base_test_case.BaseTestCase): + name = "localFiles" + if local==1: + class url: + mesh = "local://wing_tetra.1.lb8.ugrid" + case_json = "local://Flow360.json" + else: + class url: + mesh = "https://simcloud-public-1.s3.amazonaws.com/om6/wing_tetra.1.lb8.ugrid" + case_json = "local://Flow360.json" diff --git a/examples/Quick_Starts/ONERAM6/README.md b/examples/Quick_Starts/ONERAM6/README.md new file mode 100644 index 0000000..c475c38 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/README.md @@ -0,0 +1,16 @@ +To run the demo case follow these steps: + +1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. + +2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. + +3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. + +4. Run Download_Plot_Sectional_Forces.py -> this script will download and plot the sectional loads. + +5. Download_Vis_Figures.py -> this script will download Cp, Cf and streamline visualizations. + +Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh from Flexcompute storage servers and the Flow360.json file located in the "localFiles directory". +To run with a mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 + + diff --git a/examples/Quick_Starts/ONERAM6/Submit_Case.py b/examples/Quick_Starts/ONERAM6/Submit_Case.py new file mode 100644 index 0000000..25349ee --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/Submit_Case.py @@ -0,0 +1,29 @@ +import os +import flow360 as fl +from Files import ONERAM6 + +print("Obtaining mesh and solver files") + +ONERAM6.get_files() + +print("Files accessed") + +caseNameList=[] + +# submit mesh +volume_mesh = fl.VolumeMesh.from_file(ONERAM6.mesh_filename, name="ONERAM6_Mesh") +volume_mesh = volume_mesh.submit() + + +params = fl.Flow360Params(ONERAM6.case_json) + +#submit case +case = fl.Case.create("ONERAM6_Quickstart", params, volume_mesh.id) +caseNameList.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open('caseNameList.dat', 'w') as f: + for line in caseNameList: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/ONERAM6/localFiles/Flow360.json b/examples/Quick_Starts/ONERAM6/localFiles/Flow360.json new file mode 100644 index 0000000..04b1b2d --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/localFiles/Flow360.json @@ -0,0 +1,55 @@ +{ + "geometry" : { + "refArea" : 1.15315084119231, + "momentCenter" : [0.0, 0.0, 0.0], + "momentLength" : [1.47602, 0.801672958512342, 1.47602] + }, + "volumeOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Mach"] + }, + "surfaceOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Cp", "Cf"] + }, + "navierStokesSolver" : { + "absoluteTolerance" : 1e-10, + "kappaMUSCL" : -1.0 + }, + "turbulenceModelSolver" : { + "modelType" : "SpalartAllmaras", + "absoluteTolerance" : 1e-8 + }, + "freestream" : + { + "Reynolds" : 14.6e6, + "Mach" : 0.84, + "Temperature" : 288.15, + "alphaAngle" : 3.06, + "betaAngle" : 0.0 + }, + "boundaries" : { + "1" : { + "type" : "NoSlipWall", + "name" : "wing" + }, + "2" : { + "type" : "SlipWall", + "name" : "symmetry" + }, + "3" : { + "type" : "Freestream", + "name" : "freestream" + } + }, + "timeStepping" : { + "maxPseudoSteps" : 500, + "CFL" : { + "initial" : 5, + "final": 200, + "rampSteps" : 40 + } + } +} diff --git a/examples/Quick_Starts/S809/Convergence_Plots.py b/examples/Quick_Starts/S809/Convergence_Plots.py new file mode 100644 index 0000000..f95dd45 --- /dev/null +++ b/examples/Quick_Starts/S809/Convergence_Plots.py @@ -0,0 +1,90 @@ +import matplotlib.pyplot as plt +from collections import defaultdict +import os, csv +import numpy as np +import pandas as pd + + + + +def plotLoads(data, plotName): + axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) + axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) + axes[0].set_ylabel('CL'); + axes[1].set_ylabel('CD'); + axes[0].set_ylim(0.7, 0.8); + axes[1].set_ylim(0.01, 0.016); + + for ax in axes: + ax.legend() + ax.set_xlabel('Pseudo step'); + ax.grid(which='major', color='gray') + +def plotResiduals(res, plotName): + x = res['pseudo_step'] + keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] + labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] + for ax, key, label in zip(axes, keys, labels): + for j, key in enumerate(keys): + if plotName =="Automated Meshing": + ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) + else: + ax.semilogy(x, res[key], "--", label = plotName + ' ' + labels[j]) + ax.set_ylabel('Residuals') + ax.legend() + ax.set_title("ONERAM6") + ax.grid(which='major', color='gray') + ax.set_xlabel('Pseudo step'); + ax.set_yticks(10.0**np.arange(-14,-3)); + ax.set_ylim([1e-13,1e-2]) + ax.set_xlim([0,5000]) + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +naming = ["Automated Meshing", "Structured Mesh"] +figures = [] +axes = [] + +def main(): + + #Plot loads convergence histories + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + + csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotLoads(data, naming[j]) + + + figures[0].savefig(f'figures/CL.png', dpi=500); + figures[1].savefig(f'figures/CD.png', dpi=500); + + for i in range(0,2): + axes[i].cla(); + + #Plot residual convergence histories + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + for j, resolution in enumerate(caseNameList): + if "Structured" in resolution: + figures[0].gca().set_prop_cycle(None) + csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') + res = pd.read_csv(csvPath, skipinitialspace=True) + plotResiduals(res, naming[j]) + + figures[0].savefig(f'figures/Residuals.png', dpi=500); + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/S809/Download_Data.py b/examples/Quick_Starts/S809/Download_Data.py new file mode 100644 index 0000000..abb056c --- /dev/null +++ b/examples/Quick_Starts/S809/Download_Data.py @@ -0,0 +1,21 @@ +import os +import flow360 as fl +from flow360 import MyCases + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + #Download the files + case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/S809/Files.py b/examples/Quick_Starts/S809/Files.py new file mode 100644 index 0000000..2db9334 --- /dev/null +++ b/examples/Quick_Starts/S809/Files.py @@ -0,0 +1,28 @@ +import os +from flow360.examples import base_test_case +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--localFiles', default = 0, type = int, required = False) +args = parser.parse_args() +local = args.localFiles + +base_test_case.here = os.path.dirname(os.path.abspath(__file__)) + +class S809(base_test_case.BaseTestCase): + name = "localFiles" + if local==1: + class url: + geometry = "local://s809.csm" + surface_json = "local://s809SurfaceMesh.json" + volume_json = "local://s809VolumeMesh.json" + case_json = "local://s809Case.json" + mesh = "local://s809structured.cgns" + else: + class url: + geometry = "https://simcloud-public-1.s3.amazonaws.com/s809/s809.csm" + surface_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809SurfaceMesh.json" + volume_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809VolumeMesh.json" + case_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809Case.json" + mesh = "https://simcloud-public-1.s3.amazonaws.com/s809/s809structured.cgns" + diff --git a/examples/Quick_Starts/S809/README.md b/examples/Quick_Starts/S809/README.md new file mode 100644 index 0000000..6d1b6bb --- /dev/null +++ b/examples/Quick_Starts/S809/README.md @@ -0,0 +1,11 @@ +To run the demo case follow these steps: + +1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. + +2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. + +3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. + +Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh/files from Flexcompute storage servers. To run with a files/mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 + + diff --git a/examples/Quick_Starts/S809/Submit_Case.py b/examples/Quick_Starts/S809/Submit_Case.py new file mode 100644 index 0000000..26c6611 --- /dev/null +++ b/examples/Quick_Starts/S809/Submit_Case.py @@ -0,0 +1,45 @@ +import os +import flow360 as fl +from Files import S809 + +print("Obtaining mesh and solver files") + +S809.get_files() + +print("Files accessed") + +caseNameList=[] + +params = fl.SurfaceMeshingParams(S809.surface_json) +surface_mesh = fl.SurfaceMesh.create( + S809.geometry, params=params, name="NREL_S809_Surface" +) +surface_mesh = surface_mesh.submit() + +params = fl.VolumeMeshingParams(S809.volume_json) +volume_mesh = surface_mesh.create_volume_mesh("NREL_S809_Volume", params=params) +volume_mesh = volume_mesh.submit() + +params = fl.Flow360Params(S809.case_json) + +#submit case +case = fl.Case.create("NREL_S809_Automated_Meshing", params, volume_mesh.id) +caseNameList.append(case.name) +case = case.submit() +# submit mesh + +volume_mesh2 = fl.VolumeMesh.from_file(S809.mesh_filename, name="S809_Structured_Mesh") +volume_mesh2 = volume_mesh2.submit() + + + +#submit case +case = fl.Case.create("NREL_S809_Structured", params, volume_mesh2.id) +caseNameList.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open('caseNameList.dat', 'w') as f: + for line in caseNameList: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/S809/localFiles/s809.csm b/examples/Quick_Starts/S809/localFiles/s809.csm new file mode 100644 index 0000000..0917b8e --- /dev/null +++ b/examples/Quick_Starts/S809/localFiles/s809.csm @@ -0,0 +1,500 @@ +# s809 quasi model + +# MODEL PARAMETERS +#============================================================================================ +CFGPMTR span 0.01 + +#============================================================================================ +# START OF AIRFOIL COORDINATE DATA DEFINED AS SPLINE CONTROL POINTS +#============================================================================================ +mark +skbeg 1.0 0.0 0.0 +spline 0.9990087753797489 0.0 0.0001321828466218432 +spline 0.9979782091593512 0.0 0.0002741365260412735 +spline 0.9969068557635791 0.0 0.0004264835758199288 +spline 0.9957932246948061 0.0 0.0005898723955456967 +spline 0.9946357794236589 0.0 0.000764976189374184 +spline 0.9934329362271468 0.0 0.0009524914611459873 +spline 0.9921830629310853 0.0 0.001153135994925303 +spline 0.9908844775440524 0.0 0.001367646240830077 +spline 0.989535446754503 0.0 0.001596774018720059 +spline 0.9881341842683602 0.0 0.001841282440792564 +spline 0.9866788489530366 0.0 0.002101940944253337 +spline 0.985167542766593 0.0 0.002379519309983862 +spline 0.9835983076146946 0.0 0.002674780687421874 +spline 0.9819691254713504 0.0 0.002988472647273746 +spline 0.9802779118004638 0.0 0.003321317959763864 +spline 0.9785225143647212 0.0 0.003674002637238989 +spline 0.9767007086101434 0.0 0.004047162676085185 +spline 0.9748101954585755 0.0 0.004441368111425334 +spline 0.9728485932799635 0.0 0.004857105799393651 +spline 0.9708134354305149 0.0 0.005294758282188284 +spline 0.9687021638729153 0.0 0.005754580342793698 +spline 0.9665121231330085 0.0 0.006236672005476944 +spline 0.9642405538624044 0.0 0.006740947713494844 +spline 0.9618845864734133 0.0 0.007267102876028185 +spline 0.9594413058819921 0.0 0.007814893341668305 +spline 0.9569078532694137 0.0 0.008384572274999218 +spline 0.9542813168484929 0.0 0.008976386692140221 +spline 0.9515587130207037 0.0 0.009590480501001138 +spline 0.9487369855100286 0.0 0.01022687599315634 +spline 0.945813004638998 0.0 0.01088545314097124 +spline 0.942783566780977 0.0 0.01156592651642563 +spline 0.9396453939897179 0.0 0.01226781965352034 +spline 0.9363951343059824 0.0 0.01299043656646606 +spline 0.933029362394118 0.0 0.01373283033384398 +spline 0.9295445809001825 0.0 0.01449376850663881 +spline 0.9259372228246188 0.0 0.01527169514489701 +spline 0.9222036938715868 0.0 0.01606487250470437 +spline 0.9183406961706944 0.0 0.01687287443984651 +spline 0.9143451253433935 0.0 0.01769612476124379 +spline 0.9102139219133466 0.0 0.01853517829516109 +spline 0.9059440820295047 0.0 0.01939073585381203 +spline 0.9015326705041669 0.0 0.0202636679370996 +spline 0.8969768349970166 0.0 0.0211550406643361 +spline 0.8922738213174143 0.0 0.02206614403565564 +spline 0.8874209879814227 0.0 0.02299852295736582 +spline 0.8824157789065917 0.0 0.02395371951544411 +spline 0.8772555452780451 0.0 0.02493236848365839 +spline 0.871937712953194 0.0 0.02593488331140327 +spline 0.8664598353434976 0.0 0.02696171349694147 +spline 0.8608196105029857 0.0 0.02801335031976217 +spline 0.8550148967822955 0.0 0.02909033341023709 +spline 0.8490437289616155 0.0 0.03019325775840857 +spline 0.842904334811851 0.0 0.0313227811278828 +spline 0.8365951520255968 0.0 0.03247963182927922 +spline 0.8301148454519939 0.0 0.03366461679509788 +spline 0.8234623244327893 0.0 0.03487862990790976 +spline 0.8166367605901403 0.0 0.03612266041712947 +spline 0.8096376055437406 0.0 0.03739780142291998 +spline 0.8024646084903562 0.0 0.0387052583052012 +spline 0.7951178337336249 0.0 0.04004635693099488 +spline 0.7875976779783103 0.0 0.04142255150634959 +spline 0.7799048855450517 0.0 0.04283543222787684 +spline 0.7720405718469885 0.0 0.04428672144903029 +spline 0.764006100624526 0.0 0.04577762971872359 +spline 0.7558030102259701 0.0 0.04730830501029148 +spline 0.7474331767310014 0.0 0.04887871183598985 +spline 0.7388988423677872 0.0 0.05048872230075181 +spline 0.730202623188583 0.0 0.05213811155355769 +spline 0.7213475153241476 0.0 0.05382655355598901 +spline 0.7123368998493218 0.0 0.05555361719955787 +spline 0.7031745452467058 0.0 0.05731876300571855 +spline 0.69386460963093 0.0 0.05912134003410383 +spline 0.6844116396172271 0.0 0.06096058363704927 +spline 0.6748205680217986 0.0 0.06283561368188392 +spline 0.665096709423409 0.0 0.06474543346595307 +spline 0.6552457429145084 0.0 0.06668887483255358 +spline 0.6452735768378489 0.0 0.06866395997956463 +spline 0.6351863692266301 0.0 0.07066806835347454 +spline 0.6249906011976696 0.0 0.07269837521620778 +spline 0.614693065790501 0.0 0.07475186971075072 +spline 0.6043008534849249 0.0 0.07682536643411747 +spline 0.5938213360342397 0.0 0.07891551896463068 +spline 0.5832621489109076 0.0 0.08101883522205784 +spline 0.5726311722031908 0.0 0.08313169460316408 +spline 0.5619365100316617 0.0 0.0852503667620717 +spline 0.5511864687035037 0.0 0.08737103184894508 +spline 0.5403886582479288 0.0 0.08948532211318229 +spline 0.5295443536347108 0.0 0.09155082146513059 +spline 0.5186526637149406 0.0 0.093508819466527 +spline 0.5077145060100517 0.0 0.09530002448757624 +spline 0.4967358941104777 0.0 0.09688163728672797 +spline 0.4857285404238327 0.0 0.0982374692530671 +spline 0.4747053574728284 0.0 0.09935422841845784 +spline 0.4636804756705865 0.0 0.1002250959841126 +spline 0.4526703173015253 0.0 0.1008775775024013 +spline 0.4416901488525046 0.0 0.1013517069461934 +spline 0.4307531547434411 0.0 0.1016869462753451 +spline 0.4198705757551138 0.0 0.1019073957619457 +spline 0.409052474911212 0.0 0.1020175140191878 +spline 0.3983085704537707 0.0 0.102020722673854 +spline 0.3876482505857621 0.0 0.1019207302796176 +spline 0.3770805449562268 0.0 0.1017214996324819 +spline 0.366614099252437 0.0 0.1014272145731023 +spline 0.3562571530589818 0.0 0.1010422466698695 +spline 0.3460175209568611 0.0 0.1005711221509409 +spline 0.3359025767618802 0.0 0.1000184894177816 +spline 0.3259192409693631 0.0 0.09938908744743591 +spline 0.3160739728043233 0.0 0.09868769430576518 +spline 0.3063728173758031 0.0 0.0979184348849529 +spline 0.2968214287315485 0.0 0.09708468321780293 +spline 0.2874250128269072 0.0 0.09618987254191749 +spline 0.2781883198873672 0.0 0.09523752704071008 +spline 0.2691156429584259 0.0 0.09423124213006169 +spline 0.2602108189257212 0.0 0.09317466500805605 +spline 0.2514772318607602 0.0 0.09207147562350841 +spline 0.2429178186658557 0.0 0.09092536822426382 +spline 0.2345350767235326 0.0 0.08974003360267972 +spline 0.2263310734521497 0.0 0.08851914216425252 +spline 0.2183074576081409 0.0 0.08726632792547645 +spline 0.2104654721446554 0.0 0.08598517352768564 +spline 0.2028059751031133 0.0 0.08467915779876325 +spline 0.1953296475197865 0.0 0.08335057001177201 +spline 0.1880370338930814 0.0 0.08200053040199076 +spline 0.1809283348004926 0.0 0.08063031939065975 +spline 0.174003406637984 0.0 0.07924142472132756 +spline 0.167261775159455 0.0 0.0778355129107922 +spline 0.1607026499657334 0.0 0.07641440127488959 +spline 0.1543249387956431 0.0 0.07498003051488465 +spline 0.1481272670795852 0.0 0.07353443933141793 +spline 0.1421079903377829 0.0 0.07207973849062355 +spline 0.136265213681155 0.0 0.07061808770178886 +spline 0.1305968079196271 0.0 0.06915167310371734 +spline 0.1251004343249101 0.0 0.06768268854963494 +spline 0.1197735521444585 0.0 0.06621331405476247 +spline 0.1146134431840524 0.0 0.06474570036269205 +spline 0.1096172281589022 0.0 0.06328195319826324 +spline 0.1047818861828762 0.0 0.06182412023784516 +spline 0.1001042636133988 0.0 0.06037417668314067 +spline 0.0955811055906026 0.0 0.05893401958041332 +spline 0.09120912376568728 0.0 0.05750526531275962 +spline 0.08698543086186246 0.0 0.05608802103941818 +spline 0.08290724235031724 0.0 0.0546819321888603 +spline 0.07897164242539444 0.0 0.0532869240883429 +spline 0.07517559945229199 0.0 0.05190315972873338 +spline 0.07151598157745588 0.0 0.05053099744208442 +spline 0.06798957192358278 0.0 0.04917095308891824 +spline 0.06459308255058191 0.0 0.04782366605042816 +spline 0.0613231678130878 0.0 0.04648986890403303 +spline 0.05817643755043745 0.0 0.04517035971433999 +spline 0.05514962419690089 0.0 0.04386562107260177 +spline 0.0522398084218954 0.0 0.04257535245580712 +spline 0.0494440534245906 0.0 0.04129936971763946 +spline 0.04675935170961605 0.0 0.04003771916909245 +spline 0.04418263883782127 0.0 0.03879063313594722 +spline 0.04171080546394079 0.0 0.03755849017575403 +spline 0.0393407113403479 0.0 0.03634178121271874 +spline 0.03706919591366027 0.0 0.03514107852653007 +spline 0.03489308754950842 0.0 0.03395700803963984 +spline 0.0328092142437711 0.0 0.03279022610505327 +spline 0.03081442130669026 0.0 0.03164140393916078 +spline 0.02890557025739307 0.0 0.03051120484320511 +spline 0.02707954726234012 0.0 0.02940026938918174 +spline 0.02533328261072452 0.0 0.02830921044108411 +spline 0.02366379767113633 0.0 0.02723852351758408 +spline 0.02206889850930264 0.0 0.02618754886327262 +spline 0.02054690332277005 0.0 0.02515499167745248 +spline 0.01909601888035668 0.0 0.02413989782199087 +spline 0.01771434552204848 0.0 0.02314161889171025 +spline 0.01639989602578823 0.0 0.02215975790118321 +spline 0.01515061108223743 0.0 0.02119412197037715 +spline 0.0139643743690182 0.0 0.02024467901875141 +spline 0.01283928155090316 0.0 0.01931121406891053 +spline 0.01177387408157978 0.0 0.01839308238717782 +spline 0.01076660853473847 0.0 0.01748986939901837 +spline 0.00981580222714559 0.0 0.01660142415482439 +spline 0.008919651040157602 0.0 0.01572780450871212 +spline 0.00807624660018714 0.0 0.01486922804946303 +spline 0.007283593843212163 0.0 0.01402602888380527 +spline 0.006539627889841374 0.0 0.01319861868401332 +spline 0.005842231522928772 0.0 0.01238745307662587 +spline 0.005189252804380801 0.0 0.01159300300372984 +spline 0.00457877921444681 0.0 0.01081553086942471 +spline 0.004010313687738792 0.0 0.01005424080374014 +spline 0.003483699806842021 0.0 0.00930824224955492 +spline 0.002998598306054439 0.0 0.008576955474867004 +spline 0.002554508246922546 0.0 0.007860066871602116 +spline 0.002150774440356647 0.0 0.007157490155457711 +spline 0.001786588077112968 0.0 0.006469334283710064 +spline 0.0014609820866705 0.0 0.005795871881533633 +spline 0.001172825302467035 0.0 0.005137507579772312 +spline 0.0009208181859395529 0.0 0.004494745285221833 +spline 0.0007034921053932834 0.0 0.00386815318214632 +spline 0.0005192141509123521 0.0 0.003258326565700027 +spline 0.0003661999809912765 0.0 0.002665853757095631 +spline 0.0002425331721573686 0.0 0.002091281683153914 +spline 0.0001461916778060298 0.0 0.001535086295722448 +spline 7.507954461230265e-05 0.0 0.0009976486125247068 +spline 2.706178238058099e-05 0.0 0.0004792375441569564 +spline 0.0 0.0 -2e-05 +spline -1.070830957182994e-05 0.0 -0.000519598311385809 +skend +skbeg -1.070830957182994e-05 0.0 -0.000519598311385809 +spline 3.968998753512799e-05 0.0 -0.001037509830861938 +spline 0.0001544840568354694 0.0 -0.001567077700830471 +spline 0.0003330005974600307 0.0 -0.002102362798194595 +spline 0.0005713982330428165 0.0 -0.002639405904449688 +spline 0.000863943689447444 0.0 -0.003176750825022636 +spline 0.001204041187529012 0.0 -0.003715392396788188 +spline 0.001584856681617041 0.0 -0.004258373275115059 +spline 0.002003405523994527 0.0 -0.004807438615428181 +spline 0.002460239577347682 0.0 -0.005362228585298055 +spline 0.002955678937385457 0.0 -0.005922700898486172 +spline 0.003489668175242838 0.0 -0.006489331456856403 +spline 0.004061778904578593 0.0 -0.007063212671762736 +spline 0.004671377979317589 0.0 -0.007645944172044386 +spline 0.005318786434080808 0.0 -0.008238403728316477 +spline 0.006004727102922545 0.0 -0.008841199225460172 +spline 0.0067298150747689 0.0 -0.009455204787190708 +spline 0.007494532383332695 0.0 -0.01008161458290012 +spline 0.008299574437578642 0.0 -0.0107215277922225 +spline 0.009146509207501577 0.0 -0.01137505520968715 +spline 0.01003706772614734 0.0 -0.01204217940044264 +spline 0.01097296958479492 0.0 -0.01272293881869857 +spline 0.0119559222904556 0.0 -0.01341745846879269 +spline 0.01298760891938634 0.0 -0.01412597577692548 +spline 0.01406967688231738 0.0 -0.01484887207075198 +spline 0.01520372580958288 0.0 -0.01558670854751259 +spline 0.01639128900162343 0.0 -0.01634026376459482 +spline 0.0176338259386493 0.0 -0.01711058453748232 +spline 0.01893276947827469 0.0 -0.01789890885157339 +spline 0.02028998838784297 0.0 -0.01870590471118367 +spline 0.021707570020899 0.0 -0.01953199144915441 +spline 0.02318761572011189 0.0 -0.02037767688026184 +spline 0.02473223242752406 0.0 -0.02124357914813543 +spline 0.02634352350690856 0.0 -0.02213045071712301 +spline 0.02802357758366764 0.0 -0.02303920636682479 +spline 0.02977446392883618 0.0 -0.02397093952821648 +spline 0.03159849412509542 0.0 -0.02492643072524681 +spline 0.0334983142711 0.0 -0.02590593498173195 +spline 0.03547664189528116 0.0 -0.02690969438981583 +spline 0.03753622984868726 0.0 -0.02793797255124282 +spline 0.03967986724462401 0.0 -0.0289910692587028 +spline 0.04191037114087689 0.0 -0.03006933277308035 +spline 0.04423057877584989 0.0 -0.03117317503379109 +spline 0.04664333885113084 0.0 -0.03230308927043829 +spline 0.04915150201408446 0.0 -0.03345967034493388 +spline 0.05175790968774128 0.0 -0.03464363769800025 +spline 0.05446538326601325 0.0 -0.03585586210193234 +spline 0.05727670419236906 0.0 -0.03709739228004851 +spline 0.06019461042531121 0.0 -0.03836949304733217 +spline 0.06322176993077168 0.0 -0.03967367773027207 +spline 0.06636086616124724 0.0 -0.0410115109689275 +spline 0.06961512477893321 0.0 -0.04238334465505924 +spline 0.07298802080985184 0.0 -0.04378895083978962 +spline 0.07648307254935904 0.0 -0.04522797839743766 +spline 0.08010383602697499 0.0 -0.04669994202852096 +spline 0.08385389906508349 0.0 -0.04820420968064849 +spline 0.0877368751514413 0.0 -0.04973998934481379 +spline 0.09175639560326551 0.0 -0.05130631460027155 +spline 0.09591610193599359 0.0 -0.05290202965894086 +spline 0.1002196365063859 0.0 -0.05452577440089731 +spline 0.1046705744454653 0.0 -0.05617612670893558 +spline 0.1092723383444706 0.0 -0.05785179941951395 +spline 0.1140282739289971 0.0 -0.05955140744787053 +spline 0.118941646031923 0.0 -0.06127343076817566 +spline 0.1240156333302364 0.0 -0.06301621682741695 +spline 0.1292532980134544 0.0 -0.06477797531587161 +spline 0.1346575774499731 0.0 -0.06655678189702584 +spline 0.1402312646816999 0.0 -0.06835057926331453 +spline 0.145976992038676 0.0 -0.07015718054064093 +spline 0.1518972043156699 0.0 -0.07197427078031515 +spline 0.1579941531465652 0.0 -0.0737994164437385 +spline 0.1642698678733839 0.0 -0.07563006887560098 +spline 0.1707261383222463 0.0 -0.07746357292336253 +spline 0.1773644951395592 0.0 -0.07929717613067996 +spline 0.1841861901734267 0.0 -0.08112803925358168 +spline 0.1911921770958491 0.0 -0.08295324809596143 +spline 0.1983830924429242 0.0 -0.08476982663221334 +spline 0.2057592372495541 0.0 -0.08657475136158772 +spline 0.2133205881761383 0.0 -0.08836486367457544 +spline 0.2210671665631215 0.0 -0.09013508253700149 +spline 0.228999015720709 0.0 -0.09187838020338955 +spline 0.2371157858805631 0.0 -0.09358714724072138 +spline 0.2454167104818773 0.0 -0.0952532386592112 +spline 0.253900566453729 0.0 -0.09686797090938096 +spline 0.2625656378594985 0.0 -0.09842212527853032 +spline 0.271409674155087 0.0 -0.0999059567496887 +spline 0.2804298533150048 0.0 -0.1013092110802701 +spline 0.2896227369931855 0.0 -0.1026211487668667 +spline 0.2989842308841478 0.0 -0.1038305791058019 +spline 0.3085095401897754 0.0 -0.1049259036994803 +spline 0.3181931282896007 0.0 -0.105895171592076 +spline 0.3280286697423238 0.0 -0.1067262260763325 +spline 0.3380089354060737 0.0 -0.1074080118622318 +spline 0.3481258546850401 0.0 -0.1079304569375507 +spline 0.3583706075243047 0.0 -0.1082835313684722 +spline 0.3687336192634914 0.0 -0.1084572831240863 +spline 0.3792045587801731 0.0 -0.1084419108205368 +spline 0.3897723402659258 0.0 -0.1082278430527005 +spline 0.4004251345866191 0.0 -0.1078058241365513 +spline 0.4111503886251916 0.0 -0.1071670056284606 +spline 0.4219348501840513 0.0 -0.1063030431992751 +spline 0.4327646048911357 0.0 -0.1052061977433087 +spline 0.443625192867815 0.0 -0.1038699762176675 +spline 0.4545027714111136 0.0 -0.1022964297709617 +spline 0.4653844817568922 0.0 -0.1004941785157118 +spline 0.4762579333989129 0.0 -0.09847261810728107 +spline 0.487112462668105 0.0 -0.0962476912693429 +spline 0.4979419091897664 0.0 -0.09385246112821385 +spline 0.5087423665477697 0.0 -0.09132347137228447 +spline 0.5195111236195169 0.0 -0.08869676363149047 +spline 0.5302441844780578 0.0 -0.08599923861877558 +spline 0.5409366610565618 0.0 -0.08325192565542588 +spline 0.5515839887679104 0.0 -0.08047553197225492 +spline 0.562180905404066 0.0 -0.07768683601008168 +spline 0.5727202659770608 0.0 -0.07489449597745233 +spline 0.5831948336774904 0.0 -0.0721059815371694 +spline 0.5935975605653802 0.0 -0.06932857477744805 +spline 0.6039215953241852 0.0 -0.06656932633102096 +spline 0.6141602911177806 0.0 -0.06383501482372582 +spline 0.6243072128140815 0.0 -0.0611321101746916 +spline 0.6343561437346243 0.0 -0.05846674096651958 +spline 0.6443010934245192 0.0 -0.05584466568542402 +spline 0.654136304929079 0.0 -0.05327124837348633 +spline 0.6638562595587845 0.0 -0.05075142887031545 +spline 0.6734555608926294 0.0 -0.04828922475404752 +spline 0.68292890428342 0.0 -0.04588754953176419 +spline 0.6922712725108067 0.0 -0.04354892022017716 +spline 0.7014779591140057 0.0 -0.04127551700651144 +spline 0.7105445749562107 0.0 -0.03906918592140247 +spline 0.7194670535507448 0.0 -0.03693144395959395 +spline 0.728241655246941 0.0 -0.03486348644439229 +spline 0.7368649696308267 0.0 -0.03286619659011231 +spline 0.7453339170193678 0.0 -0.03094015684006354 +spline 0.7536457485798926 0.0 -0.02908566187588805 +spline 0.7617980450829666 0.0 -0.02730273307059344 +spline 0.7697887143667543 0.0 -0.02559113414133143 +spline 0.7776159889025231 0.0 -0.0239503942764878 +spline 0.7852784328772136 0.0 -0.02237988510919161 +spline 0.7927749277899079 0.0 -0.02087880339244491 +spline 0.8001046562246066 0.0 -0.0194461465682929 +spline 0.8072670937708214 0.0 -0.01808073092426795 +spline 0.8142620001428573 0.0 -0.01678121029499591 +spline 0.8210894094564424 0.0 -0.0155460945599613 +spline 0.827749619749582 0.0 -0.01437376778086368 +spline 0.834243181905752 0.0 -0.01326250582754034 +spline 0.8405708878986454 0.0 -0.01221049340110895 +spline 0.8467337584631576 0.0 -0.01121584034464276 +spline 0.85273303061958 0.0 -0.01027659710009958 +spline 0.8585701448006564 0.0 -0.009390769301301755 +spline 0.8642467317791884 0.0 -0.008556331430968605 +spline 0.8697645994865493 0.0 -0.007771239504196711 +spline 0.8751257198057465 0.0 -0.007033442755898926 +spline 0.8803322154188231 0.0 -0.006340894323048289 +spline 0.8853863467852825 0.0 -0.00569156092448408 +spline 0.8902905149556262 0.0 -0.005083552914446419 +spline 0.8950472829366074 0.0 -0.00451546184692306 +spline 0.8996593119542231 0.0 -0.003985928633443964 +spline 0.9041293286400948 0.0 -0.003493525982262833 +spline 0.9084601198891991 0.0 -0.003036774954622432 +spline 0.9126545258367009 0.0 -0.002614160204129024 +spline 0.916715429351497 0.0 -0.002224144055931803 +spline 0.9206457500475196 0.0 -0.001865178668875309 +spline 0.9244484363630523 0.0 -0.001535717317537781 +spline 0.9281264607221862 0.0 -0.001234253989142401 +spline 0.9316828134372723 0.0 -0.0009593507534290472 +spline 0.9351204907253561 0.0 -0.0007095851580201726 +spline 0.9384424881765357 0.0 -0.0004835514176657564 +spline 0.9416517951144666 0.0 -0.000279867781856104 +spline 0.9447513886398117 0.0 -9.718282734864164e-05 +spline 0.9477442309699601 0.0 6.581944159388359e-05 +spline 0.9506332610484662 0.0 0.0002104150724955854 +spline 0.9534213925108137 0.0 0.0003378368501247392 +spline 0.9561115089547733 0.0 0.0004492715658608403 +spline 0.9587064601295556 0.0 0.0005458580111912734 +spline 0.9612090584428907 0.0 0.0006286855844429949 +spline 0.9636220774576785 0.0 0.0006987340543540364 +spline 0.9659482569956704 0.0 0.0007565156074196434 +spline 0.9681902870752733 0.0 0.0008024051444539233 +spline 0.9703508003831635 0.0 0.0008368525695455863 +spline 0.9724323725670436 0.0 0.0008603668483639414 +spline 0.9744375225228721 0.0 0.0008735011788687537 +spline 0.9763687126541365 0.0 0.0008768402005455661 +spline 0.978228349095837 0.0 0.0008709890168437007 +spline 0.9800187819161187 0.0 0.0008565638293025258 +spline 0.9817423066024864 0.0 0.0008341839823327035 +spline 0.9834011592783564 0.0 0.0008044653654583872 +spline 0.984997524228127 0.0 0.000768014772539381 +spline 0.9865335291756596 0.0 0.0007254254046528674 +spline 0.9880112468246192 0.0 0.0006772731699272991 +spline 0.9894326951380275 0.0 0.0006241137598995575 +spline 0.9907998376773224 0.0 0.0005664803912198834 +spline 0.9921145839806025 0.0 0.000504882126302716 +spline 0.9933787900062063 0.0 0.0004398026937263032 +spline 0.9945942586228987 0.0 0.0003716997389508177 +spline 0.9957627401747287 0.0 0.0003010044410611266 +spline 0.9968859330904163 0.0 0.0002281214408622816 +spline 0.9979654845460086 0.0 0.0001534290300594675 +spline 0.9990029912109952 0.0 7.727955486242507e-05 +skend +group +store s809airfoil + +#============================================================================================ +# START OF THE CSM SCRIPT +# restoring sketches | s809 +#============================================================================================ +restore s809airfoil +join + +# collecting leading edge point +dimension wleading 1 3 1 +dimension wutrailing 1 3 1 +dimension wltrailing 1 3 1 + +set xmin @xmax +set zmin @zmax +set zmax @zmin +set nNodes @nnode +patbeg inode nNodes + evaluate node @ibody inode + IFTHEN @edata[1] LT xmin + set xmin @edata[1] + set wleading "xmin;@edata[2];@edata[3];" + set leadingNode inode + ENDIF + IFTHEN inode NE leadingNode + IFTHEN @edata[3] GT zmax + set zmax @edata[3] + set wutrailing "@edata[1];@edata[2];zmax;" + ENDIF + IFTHEN @edata[3] LT zmin + set zmin @edata[3] + set wltrailing "@edata[1];@edata[2];zmin;" + ENDIF + ENDIF +patend + +combine +store airfoilFace + +#RESTORING THE AIRFOIL FACE FOR EXTRUSION +#============================================================================================ +restore airfoilFace + +extrude 0 span 0 + +#ADDING EDGE ATTRIBUTES OF QUASI MODEL +#-------------------------------------------------------------------------------------------- +dimension ySymm 2 1 +set ySymm "0;span;" +patbeg i 2 + udparg edgeAttr attrname $edgeName attrstr $symmetry + udprim edgeAttr xyzs "wleading[1];ySymm[i];wleading[3];wutrailing[1];ySymm[i];wutrailing[3];" + udparg edgeAttr attrname $edgeName attrstr $symmetry + udprim edgeAttr xyzs "wleading[1];ySymm[i];wleading[3];wltrailing[1];ySymm[i];wltrailing[3];" +patend + +udparg edgeAttr attrname $edgeName attrstr $wingleadingEdge +udprim edgeAttr xyzs "wleading[1];wleading[2];wleading[3];wleading[1];span;wleading[3];" +udparg edgeAttr attrname $edgeName attrstr $wingtrailingEdge +udprim edgeAttr xyzs "wutrailing[1];wutrailing[2];wutrailing[3];wutrailing[1];span;wutrailing[3];" +udparg edgeAttr attrname $edgeName attrstr $wingtrailingEdge +udprim edgeAttr xyzs "wltrailing[1];wltrailing[2];wltrailing[3];wltrailing[1];span;wltrailing[3];" + +store quasiBody + +#============================================================================================ +restore quasiBody +attribute faceName $wing +attribute groupName $wing +set nWingFaces @nface +set sumWingArea @area + +#finding min area face as trailing edge face | wing +#-------------------------------------------------- +set minArea sumWingArea +patbeg i nWingFaces + select face i + IFTHEN @area LT minArea + set minArea @area + set trailingFace i + ENDIF +patend +select face trailingFace +attribute faceName $wingTrailing +attribute groupName $wing + +END diff --git a/examples/Quick_Starts/S809/localFiles/s809Case.json b/examples/Quick_Starts/S809/localFiles/s809Case.json new file mode 100644 index 0000000..b8fe459 --- /dev/null +++ b/examples/Quick_Starts/S809/localFiles/s809Case.json @@ -0,0 +1,61 @@ +{ + "geometry": { + "refArea": 0.01, + "momentCenter": [0.25,0,0], + "momentLength": [1,1,1] + }, + "volumeOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Mach"] + }, + "surfaceOutput" : { + "outputFormat" : "tecplot", + "animationFrequency" : -1, + "outputFields": ["primitiveVars", "Cp", "Cf"] + }, + "navierStokesSolver": { + "absoluteTolerance": 1e-11, + "linearIterations": 35, + "kappaMUSCL": 0.33, + "orderOfAccuracy": 2 + }, + "turbulenceModelSolver": { + "modelType": "SpalartAllmaras", + "absoluteTolerance": 1e-10, + "linearIterations": 25, + "kappaMUSCL": -1, + "orderOfAccuracy": 2 + }, + "freestream": { + "Reynolds": 2e+06, + "Mach": 0.15, + "Temperature": 293.15, + "alphaAngle": 5.13, + "betaAngle": 0 + }, + "boundaries": { + "fluid/symmetric-1": { + "type": "SlipWall" + }, + "fluid/symmetric-2": { + "type": "SlipWall" + }, + "fluid/wing": { + "type": "NoSlipWall" + }, + "fluid/farfield": { + "type": "Freestream" + } + }, + "timeStepping": { + "physicalSteps": 1, + "timeStepSize": "inf", + "maxPseudoSteps": 5000, + "CFL": { + "initial": 200, + "final": 200, + "rampSteps": 100 + } + } +} diff --git a/examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json b/examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json new file mode 100644 index 0000000..fd98185 --- /dev/null +++ b/examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json @@ -0,0 +1,25 @@ +{ + "maxEdgeLength": 0.58, + "curvatureResolutionAngle": 10, + "growthRate": 1.08, + "edges": { + "wingleadingEdge": { + "type": "aniso", + "method": "height", + "value": 4e-4 + }, + "wingtrailingEdge": { + "type": "aniso", + "method": "height", + "value": 4e-4 + }, + "symmetry": { + "type": "projectAnisoSpacing" + } + }, + "faces": { + "wing": { + "maxEdgeLength": 0.40 + } + } +} diff --git a/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json b/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json new file mode 100644 index 0000000..9c8c62e --- /dev/null +++ b/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json @@ -0,0 +1,51 @@ +{ + "farfield": { + "type": "quasi-3d" + }, + "refinement": [ + { + "type": "cylinder", + "radius": 1.1, + "length": 1.0, + "spacing": 0.08, + "axis": [0,1,0], + "center": [0.7,0.5,0] + }, + { + "type": "cylinder", + "radius": 2.2, + "length": 1.0, + "spacing": 0.13, + "axis": [0,1,0], + "center": [0.7,0.5,0] + }, + { + "type": "cylinder", + "radius": 3.3, + "length": 1.0, + "spacing": 0.2, + "axis": [0,1,0], + "center": [0.7,0.5,0] + }, + { + "type": "cylinder", + "radius": 4.5, + "length": 1.0, + "spacing": 0.25, + "axis": [0,1,0], + "center": [0.7,0.5,0] + }, + { + "type": "cylinder", + "radius": 6.5, + "length": 14.5, + "spacing": 0.29, + "axis": [-1,0,0], + "center": [2,0.5,0] + } + ], + "volume": { + "firstLayerThickness": 4.29e-06, + "growthRate": 1.04 + } +} From bcf0dca258967beabffae2a85b0921d5e2e741df Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Tue, 22 Aug 2023 02:16:35 +0100 Subject: [PATCH 2/8] Added XV15 Quick-Start + File paths --- .../Quick_Starts/Automated_Meshing/Files.py | 6 +- .../localFiles/om6VolumeMesh.json | 1 + examples/Quick_Starts/S809/Files.py | 6 +- .../S809/localFiles/s809VolumeMesh.json | 1 + .../Quick_Starts/XV15/Convergence_Plots.py | 96 +++++++++++++++++++ examples/Quick_Starts/XV15/Download_Data.py | 21 ++++ .../XV15/Download_Plot_Sectional_Forces.py | 70 ++++++++++++++ .../Quick_Starts/XV15/Download_Vis_Figures.py | 59 ++++++++++++ examples/Quick_Starts/XV15/Files.py | 31 ++++++ examples/Quick_Starts/XV15/README.md | 11 +++ examples/Quick_Starts/XV15/Submit_Case.py | 38 ++++++++ .../XV15_quick_start_flow360_1st.json | 71 ++++++++++++++ .../XV15_quick_start_flow360_2nd.json | 71 ++++++++++++++ 13 files changed, 476 insertions(+), 6 deletions(-) create mode 100644 examples/Quick_Starts/XV15/Convergence_Plots.py create mode 100644 examples/Quick_Starts/XV15/Download_Data.py create mode 100644 examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py create mode 100644 examples/Quick_Starts/XV15/Download_Vis_Figures.py create mode 100644 examples/Quick_Starts/XV15/Files.py create mode 100644 examples/Quick_Starts/XV15/README.md create mode 100644 examples/Quick_Starts/XV15/Submit_Case.py create mode 100644 examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json create mode 100644 examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json diff --git a/examples/Quick_Starts/Automated_Meshing/Files.py b/examples/Quick_Starts/Automated_Meshing/Files.py index bfe15f8..432587b 100644 --- a/examples/Quick_Starts/Automated_Meshing/Files.py +++ b/examples/Quick_Starts/Automated_Meshing/Files.py @@ -20,7 +20,7 @@ class url: else: class url: geometry = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6wing.csm" - surface_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6SurfaceMesh.json" - volume_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6VolumeMesh.json" - case_json = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6Case.json" + surface_json = "local://om6SurfaceMesh.json" + volume_json = "local://om6VolumeMesh.json" + case_json = "local://om6Case.json" diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json b/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json index 2840c95..dbf33a1 100644 --- a/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json +++ b/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json @@ -1,4 +1,5 @@ { + "refinementFactor": 1.45, "refinement": [ { "type": "cylinder", diff --git a/examples/Quick_Starts/S809/Files.py b/examples/Quick_Starts/S809/Files.py index 2db9334..b1c6072 100644 --- a/examples/Quick_Starts/S809/Files.py +++ b/examples/Quick_Starts/S809/Files.py @@ -21,8 +21,8 @@ class url: else: class url: geometry = "https://simcloud-public-1.s3.amazonaws.com/s809/s809.csm" - surface_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809SurfaceMesh.json" - volume_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809VolumeMesh.json" - case_json = "https://simcloud-public-1.s3.amazonaws.com/s809/s809Case.json" + surface_json = "local://s809SurfaceMesh.json" + volume_json = "local://s809VolumeMesh.json" + case_json = "local://s809Case.json" mesh = "https://simcloud-public-1.s3.amazonaws.com/s809/s809structured.cgns" diff --git a/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json b/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json index 9c8c62e..4a22281 100644 --- a/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json +++ b/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json @@ -1,4 +1,5 @@ { + "refinementFactor": 1.0, "farfield": { "type": "quasi-3d" }, diff --git a/examples/Quick_Starts/XV15/Convergence_Plots.py b/examples/Quick_Starts/XV15/Convergence_Plots.py new file mode 100644 index 0000000..a2953b5 --- /dev/null +++ b/examples/Quick_Starts/XV15/Convergence_Plots.py @@ -0,0 +1,96 @@ +import matplotlib.pyplot as plt +from collections import defaultdict +import os, csv +import numpy as np +import pandas as pd + +def addAccumPseudoStep(history): + pseudoSteps = history['pseudo_step'] + accum = list() + accum.append(0) + for i in range(1,len(pseudoSteps)): + if pseudoSteps[i] > pseudoSteps[i-1]: + accum.append(accum[-1]+abs(pseudoSteps[i] - pseudoSteps[i-1])) + else: + accum.append(accum[-1]+1) + history['accum_pseudo_step'] = accum + + +def plotLoads(data, plotName): + axes[0].plot(data['accum_pseudo_step'], data['CFz'], label=plotName) + axes[1].plot(data['accum_pseudo_step'], data['CMz'], label=plotName) + axes[0].set_ylabel('CFz'); + axes[1].set_ylabel('CMz'); + #axes[0].set_ylim(0.255, 0.265); + #axes[1].set_ylim(0.0203, 0.0212); + + for ax in axes: + ax.legend() + ax.set_xlabel('Pseudo step'); + ax.grid(which='major', color='gray') + +def plotResiduals(res, plotName): + x = res['accum_pseudo_step'] + keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] + labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] + for ax, key, label in zip(axes, keys, labels): + for j, key in enumerate(keys): + ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) + ax.set_ylabel('Residuals') + ax.legend() + ax.set_title("XV-15 - Quickstart") + ax.grid(which='major', color='gray') + ax.set_xlabel('Pseudo step'); + ax.set_yticks(10.0**np.arange(-14,-3)); + ax.set_ylim([1e-9,1e-2]) + ax.set_xlim([0,7000]) + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +naming = ["XV15"] +figures = [] +axes = [] + +def main(): + + #Plot loads convergence histories + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + + csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + addAccumPseudoStep(data) + plotLoads(data, naming[j]) + + + figures[0].savefig(f'figures/CFz.png', dpi=500); + figures[1].savefig(f'figures/CMz.png', dpi=500); + + for i in range(0,2): + axes[i].cla(); + + #Plot residual convergence histories + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') + res = pd.read_csv(csvPath, skipinitialspace=True) + addAccumPseudoStep(res) + plotResiduals(res, naming[j]) + + figures[0].savefig(f'figures/Residuals.png', dpi=500); + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/XV15/Download_Data.py b/examples/Quick_Starts/XV15/Download_Data.py new file mode 100644 index 0000000..abb056c --- /dev/null +++ b/examples/Quick_Starts/XV15/Download_Data.py @@ -0,0 +1,21 @@ +import os +import flow360 as fl +from flow360 import MyCases + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + #Download the files + case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py new file mode 100644 index 0000000..3d5fd41 --- /dev/null +++ b/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py @@ -0,0 +1,70 @@ +import os, csv +import numpy as np +import flow360 as fl +import pandas as pd +from flow360 import MyCases +import matplotlib.pyplot as plt +from collections import defaultdict + + + +def plotSectionalForces(data, plotName): + axes[0].plot(data['Y'], data['innerRotating/blade_CFx_per_span'], label=plotName) + axes[1].plot(data['Y'], data['innerRotating/blade_CFz_per_span'], label=plotName) + axes[0].set_ylabel('CFx per span'); + axes[1].set_ylabel('CFz per span'); + axes[1].set_ylim(-0.002,0.01) + for ax in axes: + ax.legend() + ax.set_xlabel('Y'); + ax.grid(which='major', color='gray') + + + +#read in caseNameList + +with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + +my_cases = MyCases() + +naming = ["XV15"] +figures = [] +axes = [] + + +def main(): + + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + forces = f"results/postprocess/forceDistribution.csv" + #print(case.results) + case._download_file(forces, to_folder=caseFolder) + + for i in [0,1]: + fig, ax = plt.subplots(figsize=(8,6)) + figures.append(fig) + axes.append(ax) + dir_path= os.path.join(os.getcwd(),f'figures') + if not os.path.isdir(dir_path): # if directory already exists: + os.makedirs(dir_path) # make that dir + + for j, resolution in enumerate(caseNameList): + csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') + data = pd.read_csv(csvPath, skipinitialspace=True) + plotSectionalForces(data, naming[j]) + + figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); + figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); + + + + +if __name__ == '__main__': + main() + diff --git a/examples/Quick_Starts/XV15/Download_Vis_Figures.py b/examples/Quick_Starts/XV15/Download_Vis_Figures.py new file mode 100644 index 0000000..07333f1 --- /dev/null +++ b/examples/Quick_Starts/XV15/Download_Vis_Figures.py @@ -0,0 +1,59 @@ +import os +import numpy as np +import flow360 as fl +#from flow360.component.case import CaseDownloadable +#from flow360.component.resource_base import Flow360Resource + +from flow360 import MyCases +from PIL import Image + +def fetchPNG(case, field, res = 'H'): + # download the legend i.e. color bar + casename = case.name + os.makedirs('visualize', exist_ok=True) + + legend = f'visualize/{field}Legend.png' + case._download_file(legend, to_file=legend) + + # list all available theta and phi + viewAngles = [(0, 0),(180, 0)] + for theta in [60, 120]: + for phi in range(0,360,90): + viewAngles.append((theta, phi)) + # download the contour + axis files + for theta, phi in viewAngles: + fname = f'{theta:03d}_{phi:03d}.png' + contour = f'visualize/{field}_{res}_{fname}' + axis = f'visualize/Ax_{fname}' + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + imgContour = Image.open(contour).convert('RGBA') + imgAxis = Image.open(axis) + imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) + imgAxis = imgAxis.convert('RGBA') + imgLegend = Image.open(legend).convert('RGBA') + background = Image.new('RGBA', imgContour.size, (67,100,200)) + imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) + imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) + background.paste(imgContour, (0,0), imgContour) + background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') + +def main(): + os.makedirs('visualization_figures', exist_ok=True) + with open('caseNameList.dat', 'r') as file: + caseNameList = file.read().splitlines() + my_cases = MyCases() + for i in range(0, len(caseNameList)): + caseFolder = os.path.join(os.getcwd(), caseNameList[i]) + os.makedirs(caseFolder, exist_ok = True) + #Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == caseNameList[i]: + break + fetchPNG(case, 'Cp') + fetchPNG(case, 'Cf') + fetchPNG(case, 'Fv') + +if __name__ == "__main__": + main() diff --git a/examples/Quick_Starts/XV15/Files.py b/examples/Quick_Starts/XV15/Files.py new file mode 100644 index 0000000..3afb576 --- /dev/null +++ b/examples/Quick_Starts/XV15/Files.py @@ -0,0 +1,31 @@ +import os +from flow360.examples import base_test_case +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--localFiles', default = 0, type = int, required = False) +args = parser.parse_args() +local = args.localFiles + +base_test_case.here = os.path.dirname(os.path.abspath(__file__)) + +class XV15_1st(base_test_case.BaseTestCase): + name = "localFiles" + if local==1: + class url: + case_json = "local://XV15_quick_start_flow360_1st.json" + mesh = "local://XV15_Hover_ascent_coarse.cgns" + else: + class url: + case_json = "local://XV15_quick_start_flow360_1st.json" + mesh = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_Hover_ascent_coarse.cgns" + +class XV15_2nd(base_test_case.BaseTestCase): + name = "localFiles" + if local==1: + class url: + case_json = "local://XV15_quick_start_flow360_2nd.json" + else: + class url: + case_json = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_quick_start_flow360_2nd.json" + diff --git a/examples/Quick_Starts/XV15/README.md b/examples/Quick_Starts/XV15/README.md new file mode 100644 index 0000000..6d1b6bb --- /dev/null +++ b/examples/Quick_Starts/XV15/README.md @@ -0,0 +1,11 @@ +To run the demo case follow these steps: + +1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. + +2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. + +3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. + +Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh/files from Flexcompute storage servers. To run with a files/mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 + + diff --git a/examples/Quick_Starts/XV15/Submit_Case.py b/examples/Quick_Starts/XV15/Submit_Case.py new file mode 100644 index 0000000..fdbe44a --- /dev/null +++ b/examples/Quick_Starts/XV15/Submit_Case.py @@ -0,0 +1,38 @@ +import os +import flow360 as fl +from Files import XV15_1st, XV15_2nd + +print("Obtaining mesh and solver files") + +XV15_1st.get_files() +XV15_2nd.get_files() + +print("Files accessed") + +caseNameList=[] + +# submit mesh + +volume_mesh = fl.VolumeMesh.from_file(XV15_1st.mesh_filename, name="XV15_Mesh") +volume_mesh = volume_mesh.submit() + +#submit case 1st order + +params = fl.Flow360Params(XV15_1st.case_json) +case = fl.Case.create("XV15_1st_order", params, volume_mesh.id) +case = case.submit() + +#submit case 2nd order +print(params) +case_fork_1 = case.fork(params=params) +case_fork_1.name = "XV15_2nd_order" +params2 = fl.Flow360Params(XV15_2nd.case_json) +case_fork_1.params = params2 +caseNameList.append(case_fork_1.name) +case_fork_1 = case_fork_1.submit() + +# dump case name for use in download and post-processing scripts + +with open('caseNameList.dat', 'w') as f: + for line in caseNameList: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json b/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json new file mode 100644 index 0000000..57260d8 --- /dev/null +++ b/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json @@ -0,0 +1,71 @@ +{ + "geometry" : { + "refArea" : 45.604, + "momentCenter" : [0, 0, 0], + "momentLength" : [3.81, 3.81, 3.81] + }, + "volumeOutput" : { + "outputFormat" : "tecplot", + "outputFields": ["primitiveVars", "T","Cp", "Mach", "qcriterion", "VelocityRelative"] + }, + "surfaceOutput" : { + "outputFormat" : "tecplot", + "outputFields": ["primitiveVars", "Cp", "Cf","CfTangent", "CfNormal","nodeForcesPerUnitArea"] + }, + "navierStokesSolver" : { + "absoluteTolerance" : 1e-9, + "relativeTolerance" : 1e-2, + "linearIterations" : 35, + "kappaMUSCL" : -1.0, + "orderOfAccuracy" : 1, + "updateJacobianFrequency" : 4, + "equationEvalFrequency" : 1 + }, + "turbulenceModelSolver" : { + "modelType" : "SpalartAllmaras", + "absoluteTolerance" : 1e-8, + "relativeTolerance" : 1e-2, + "linearIterations" : 25, + "kappaMUSCL" : -1.0, + "DDES" : true, + "orderOfAccuracy" : 1, + "updateJacobianFrequency" : 4, + "equationEvalFrequency" : 1, + "rotationCorrection" : true + }, + "freestream" : + { + "muRef" : 4.29279e-08, + "Mach" : 1.46972e-02, + "MachRef" : 0.70, + "Temperature" : 288.15, + "alphaAngle" : -90.0, + "betaAngle" : 0.0 + }, + "boundaries" : { + "farField/farField" : { "type" : "Freestream" }, + "farField/rotationInterface" : { "type" : "SlidingInterface" }, + "innerRotating/rotationInterface" : { "type" : "SlidingInterface" }, + "innerRotating/blade" : { "type" : "NoSlipWall" } + }, + "slidingInterfaces" : [ + { + "stationaryPatches" : ["farField/rotationInterface"], + "rotatingPatches" : ["innerRotating/rotationInterface"], + "axisOfRotation" : [0,0,-1], + "centerOfRotation" : [0,0,0], + "omega" : 1.84691e-01, + "volumeName" : ["innerRotating"] + } + ], + "timeStepping" : { + "timeStepSize" : 5.67000e-01, + "physicalSteps" : 60, + "maxPseudoSteps" : 12, + "CFL" : { + "initial" : 1, + "final" : 1000, + "rampSteps" : 10 + } + } +} diff --git a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json b/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json new file mode 100644 index 0000000..99ac463 --- /dev/null +++ b/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json @@ -0,0 +1,71 @@ +{ + "geometry" : { + "refArea" : 45.604, + "momentCenter" : [0, 0, 0], + "momentLength" : [3.81, 3.81, 3.81] + }, + "volumeOutput" : { + "outputFormat" : "tecplot", + "outputFields": ["primitiveVars", "T","Cp", "Mach", "qcriterion", "VelocityRelative"] + }, + "surfaceOutput" : { + "outputFormat" : "tecplot", + "outputFields": ["primitiveVars", "Cp", "Cf","CfTangent", "CfNormal","nodeForcesPerUnitArea"] + }, + "navierStokesSolver" : { + "absoluteTolerance" : 1e-9, + "relativeTolerance" : 1e-2, + "linearIterations" : 35, + "kappaMUSCL" : -1.0, + "orderOfAccuracy" : 2, + "updateJacobianFrequency" : 4, + "equationEvalFrequency" : 1 + }, + "turbulenceModelSolver" : { + "modelType" : "SpalartAllmaras", + "absoluteTolerance" : 1e-8, + "relativeTolerance" : 1e-2, + "linearIterations" : 25, + "kappaMUSCL" : -1.0, + "DDES" : true, + "orderOfAccuracy" : 2, + "updateJacobianFrequency" : 4, + "equationEvalFrequency" : 1, + "rotationCorrection" : true + }, + "freestream" : + { + "muRef" : 4.29279e-08, + "Mach" : 1.46972e-02, + "MachRef" : 0.70, + "Temperature" : 288.15, + "alphaAngle" : -90.0, + "betaAngle" : 0.0 + }, + "boundaries" : { + "farField/farField" : { "type" : "Freestream" }, + "farField/rotationInterface" : { "type" : "SlidingInterface" }, + "innerRotating/rotationInterface" : { "type" : "SlidingInterface" }, + "innerRotating/blade" : { "type" : "NoSlipWall" } + }, + "slidingInterfaces" : [ + { + "stationaryPatches" : ["farField/rotationInterface"], + "rotatingPatches" : ["innerRotating/rotationInterface"], + "axisOfRotation" : [0,0,-1], + "centerOfRotation" : [0,0,0], + "omega" : 1.84691e-01, + "volumeName" : ["innerRotating"] + } + ], + "timeStepping" : { + "timeStepSize" : 2.83500e-01, + "physicalSteps" : 600, + "maxPseudoSteps" : 35, + "CFL" : { + "initial" : 1, + "final" : 1e7, + "rampSteps" : 33 + } + } +} From c9e99d96bfafaeb060007f07b49c0e8764ddcfa6 Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Mon, 4 Sep 2023 20:49:16 +0100 Subject: [PATCH 3/8] Added no limit in cases search --- examples/Quick_Starts/Automated_Meshing/Download_Data.py | 3 ++- .../Automated_Meshing/Download_Plot_Sectional_Forces.py | 3 ++- .../Quick_Starts/Automated_Meshing/Download_Vis_Figures.py | 3 ++- examples/Quick_Starts/ONERAM6/Download_Data.py | 3 ++- .../Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py | 3 ++- examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py | 3 ++- examples/Quick_Starts/S809/Download_Data.py | 3 ++- examples/Quick_Starts/XV15/Download_Data.py | 3 ++- examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py | 3 ++- examples/Quick_Starts/XV15/Download_Vis_Figures.py | 3 ++- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Data.py b/examples/Quick_Starts/Automated_Meshing/Download_Data.py index abb056c..3f7c98f 100644 --- a/examples/Quick_Starts/Automated_Meshing/Download_Data.py +++ b/examples/Quick_Starts/Automated_Meshing/Download_Data.py @@ -8,7 +8,7 @@ with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) @@ -17,5 +17,6 @@ for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) #Download the files case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py index e2ae51f..3cf2d30 100644 --- a/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py +++ b/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py @@ -25,7 +25,7 @@ def plotSectionalForces(data, plotName): with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) naming = ["ONERAM6"] figures = [] @@ -41,6 +41,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) forces = f"results/postprocess/forceDistribution.csv" #print(case.results) case._download_file(forces, to_folder=caseFolder) diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py b/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py index 07333f1..aa27a96 100644 --- a/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py +++ b/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py @@ -43,7 +43,7 @@ def main(): os.makedirs('visualization_figures', exist_ok=True) with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() - my_cases = MyCases() + my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) os.makedirs(caseFolder, exist_ok = True) @@ -51,6 +51,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) fetchPNG(case, 'Cp') fetchPNG(case, 'Cf') fetchPNG(case, 'Fv') diff --git a/examples/Quick_Starts/ONERAM6/Download_Data.py b/examples/Quick_Starts/ONERAM6/Download_Data.py index abb056c..3f7c98f 100644 --- a/examples/Quick_Starts/ONERAM6/Download_Data.py +++ b/examples/Quick_Starts/ONERAM6/Download_Data.py @@ -8,7 +8,7 @@ with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) @@ -17,5 +17,6 @@ for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) #Download the files case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py index a26a629..28e85a1 100644 --- a/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py +++ b/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py @@ -25,7 +25,7 @@ def plotSectionalForces(data, plotName): with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) naming = ["ONERAM6"] figures = [] @@ -41,6 +41,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) forces = f"results/postprocess/forceDistribution.csv" #print(case.results) case._download_file(forces, to_folder=caseFolder) diff --git a/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py b/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py index 07333f1..aa27a96 100644 --- a/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py +++ b/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py @@ -43,7 +43,7 @@ def main(): os.makedirs('visualization_figures', exist_ok=True) with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() - my_cases = MyCases() + my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) os.makedirs(caseFolder, exist_ok = True) @@ -51,6 +51,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) fetchPNG(case, 'Cp') fetchPNG(case, 'Cf') fetchPNG(case, 'Fv') diff --git a/examples/Quick_Starts/S809/Download_Data.py b/examples/Quick_Starts/S809/Download_Data.py index abb056c..3f7c98f 100644 --- a/examples/Quick_Starts/S809/Download_Data.py +++ b/examples/Quick_Starts/S809/Download_Data.py @@ -8,7 +8,7 @@ with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) @@ -17,5 +17,6 @@ for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) #Download the files case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/XV15/Download_Data.py b/examples/Quick_Starts/XV15/Download_Data.py index abb056c..3f7c98f 100644 --- a/examples/Quick_Starts/XV15/Download_Data.py +++ b/examples/Quick_Starts/XV15/Download_Data.py @@ -8,7 +8,7 @@ with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) @@ -17,5 +17,6 @@ for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) #Download the files case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py index 3d5fd41..2471eda 100644 --- a/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py +++ b/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py @@ -26,7 +26,7 @@ def plotSectionalForces(data, plotName): with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() -my_cases = MyCases() +my_cases = MyCases(limit=None) naming = ["XV15"] figures = [] @@ -42,6 +42,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) forces = f"results/postprocess/forceDistribution.csv" #print(case.results) case._download_file(forces, to_folder=caseFolder) diff --git a/examples/Quick_Starts/XV15/Download_Vis_Figures.py b/examples/Quick_Starts/XV15/Download_Vis_Figures.py index 07333f1..aa27a96 100644 --- a/examples/Quick_Starts/XV15/Download_Vis_Figures.py +++ b/examples/Quick_Starts/XV15/Download_Vis_Figures.py @@ -43,7 +43,7 @@ def main(): os.makedirs('visualization_figures', exist_ok=True) with open('caseNameList.dat', 'r') as file: caseNameList = file.read().splitlines() - my_cases = MyCases() + my_cases = MyCases(limit=None) for i in range(0, len(caseNameList)): caseFolder = os.path.join(os.getcwd(), caseNameList[i]) os.makedirs(caseFolder, exist_ok = True) @@ -51,6 +51,7 @@ def main(): for case in my_cases: if case.name == caseNameList[i]: break + print(case.name) fetchPNG(case, 'Cp') fetchPNG(case, 'Cf') fetchPNG(case, 'Fv') From b9e157dd49ec34dc6dba1e85d2e1e986dd7f0878 Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Wed, 6 Dec 2023 00:10:19 +0000 Subject: [PATCH 4/8] Script Revisions according to Flow360Scripts Guidelines --- .../Automated_Meshing/Convergence_Plots.py | 85 ---------------- .../Automated_Meshing/Download_Data.py | 22 ----- .../Download_Plot_Sectional_Forces.py | 70 -------------- .../Automated_Meshing/Download_Vis_Figures.py | 60 ------------ .../Quick_Starts/Automated_Meshing/Files.py | 26 ----- .../Quick_Starts/Automated_Meshing/README.md | 12 ++- .../Automated_Meshing/Submit_Case.py | 35 ------- .../Automated_Meshing/convergence_plots.py | 82 ++++++++++++++++ .../Automated_Meshing/download_data.py | 29 ++++++ .../download_plot_sectional_forces.py | 58 +++++++++++ .../Automated_Meshing/download_vis_figures.py | 70 ++++++++++++++ .../{localFiles => }/om6Case.json | 0 .../{localFiles => }/om6SurfaceMesh.json | 0 .../{localFiles => }/om6VolumeMesh.json | 0 .../{localFiles => }/om6wing.csm | 0 .../Automated_Meshing/submit_case.py | 33 +++++++ .../Quick_Starts/ONERAM6/Convergence_Plots.py | 85 ---------------- .../Quick_Starts/ONERAM6/Download_Data.py | 22 ----- .../ONERAM6/Download_Plot_Sectional_Forces.py | 70 -------------- .../ONERAM6/Download_Vis_Figures.py | 60 ------------ examples/Quick_Starts/ONERAM6/Files.py | 21 ---- .../ONERAM6/{localFiles => }/Flow360.json | 0 examples/Quick_Starts/ONERAM6/README.md | 14 +-- examples/Quick_Starts/ONERAM6/Submit_Case.py | 29 ------ .../Quick_Starts/ONERAM6/convergence_plots.py | 82 ++++++++++++++++ .../Quick_Starts/ONERAM6/download_data.py | 29 ++++++ .../ONERAM6/download_plot_sectional_forces.py | 57 +++++++++++ .../ONERAM6/download_vis_figures.py | 70 ++++++++++++++ .../ONERAM6/submit_case_from_download.py | 33 +++++++ .../ONERAM6/submit_case_from_id.py | 20 ++++ .../Quick_Starts/S809/Convergence_Plots.py | 90 ----------------- examples/Quick_Starts/S809/Download_Data.py | 22 ----- examples/Quick_Starts/S809/Files.py | 28 ------ examples/Quick_Starts/S809/README.md | 12 +-- examples/Quick_Starts/S809/Submit_Case.py | 45 --------- .../Quick_Starts/S809/convergence_plots.py | 87 +++++++++++++++++ examples/Quick_Starts/S809/download_data.py | 29 ++++++ .../S809/{localFiles => }/s809.csm | 0 .../S809/{localFiles => }/s809Case.json | 0 .../{localFiles => }/s809SurfaceMesh.json | 0 .../S809/{localFiles => }/s809VolumeMesh.json | 0 .../S809/submit_case_from_download.py | 47 +++++++++ .../Quick_Starts/S809/submit_case_from_id.py | 37 +++++++ .../Quick_Starts/XV15/Convergence_Plots.py | 96 ------------------- examples/Quick_Starts/XV15/Download_Data.py | 22 ----- .../XV15/Download_Plot_Sectional_Forces.py | 71 -------------- .../Quick_Starts/XV15/Download_Vis_Figures.py | 60 ------------ examples/Quick_Starts/XV15/Files.py | 31 ------ ...ck_start_flow360_2nd.json => Flow360.json} | 4 +- ...tart_flow360_1st.json => Flow360_New.json} | 35 +++---- examples/Quick_Starts/XV15/README.md | 12 ++- examples/Quick_Starts/XV15/Submit_Case.py | 38 -------- .../Quick_Starts/XV15/convergence_plots.py | 92 ++++++++++++++++++ examples/Quick_Starts/XV15/download_data.py | 28 ++++++ .../XV15/download_plot_sectional_forces.py | 63 ++++++++++++ .../Quick_Starts/XV15/download_vis_figures.py | 70 ++++++++++++++ .../XV15/submit_case_from_downloads.py | 32 +++++++ .../Quick_Starts/XV15/submit_case_from_id.py | 21 ++++ 58 files changed, 1111 insertions(+), 1135 deletions(-) delete mode 100644 examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py delete mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Data.py delete mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py delete mode 100644 examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py delete mode 100644 examples/Quick_Starts/Automated_Meshing/Files.py delete mode 100644 examples/Quick_Starts/Automated_Meshing/Submit_Case.py create mode 100644 examples/Quick_Starts/Automated_Meshing/convergence_plots.py create mode 100644 examples/Quick_Starts/Automated_Meshing/download_data.py create mode 100644 examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py create mode 100644 examples/Quick_Starts/Automated_Meshing/download_vis_figures.py rename examples/Quick_Starts/Automated_Meshing/{localFiles => }/om6Case.json (100%) rename examples/Quick_Starts/Automated_Meshing/{localFiles => }/om6SurfaceMesh.json (100%) rename examples/Quick_Starts/Automated_Meshing/{localFiles => }/om6VolumeMesh.json (100%) rename examples/Quick_Starts/Automated_Meshing/{localFiles => }/om6wing.csm (100%) create mode 100644 examples/Quick_Starts/Automated_Meshing/submit_case.py delete mode 100644 examples/Quick_Starts/ONERAM6/Convergence_Plots.py delete mode 100644 examples/Quick_Starts/ONERAM6/Download_Data.py delete mode 100644 examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py delete mode 100644 examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py delete mode 100644 examples/Quick_Starts/ONERAM6/Files.py rename examples/Quick_Starts/ONERAM6/{localFiles => }/Flow360.json (100%) delete mode 100644 examples/Quick_Starts/ONERAM6/Submit_Case.py create mode 100644 examples/Quick_Starts/ONERAM6/convergence_plots.py create mode 100644 examples/Quick_Starts/ONERAM6/download_data.py create mode 100644 examples/Quick_Starts/ONERAM6/download_plot_sectional_forces.py create mode 100644 examples/Quick_Starts/ONERAM6/download_vis_figures.py create mode 100644 examples/Quick_Starts/ONERAM6/submit_case_from_download.py create mode 100644 examples/Quick_Starts/ONERAM6/submit_case_from_id.py delete mode 100644 examples/Quick_Starts/S809/Convergence_Plots.py delete mode 100644 examples/Quick_Starts/S809/Download_Data.py delete mode 100644 examples/Quick_Starts/S809/Files.py delete mode 100644 examples/Quick_Starts/S809/Submit_Case.py create mode 100644 examples/Quick_Starts/S809/convergence_plots.py create mode 100644 examples/Quick_Starts/S809/download_data.py rename examples/Quick_Starts/S809/{localFiles => }/s809.csm (100%) rename examples/Quick_Starts/S809/{localFiles => }/s809Case.json (100%) rename examples/Quick_Starts/S809/{localFiles => }/s809SurfaceMesh.json (100%) rename examples/Quick_Starts/S809/{localFiles => }/s809VolumeMesh.json (100%) create mode 100644 examples/Quick_Starts/S809/submit_case_from_download.py create mode 100644 examples/Quick_Starts/S809/submit_case_from_id.py delete mode 100644 examples/Quick_Starts/XV15/Convergence_Plots.py delete mode 100644 examples/Quick_Starts/XV15/Download_Data.py delete mode 100644 examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py delete mode 100644 examples/Quick_Starts/XV15/Download_Vis_Figures.py delete mode 100644 examples/Quick_Starts/XV15/Files.py rename examples/Quick_Starts/XV15/{localFiles/XV15_quick_start_flow360_2nd.json => Flow360.json} (96%) rename examples/Quick_Starts/XV15/{localFiles/XV15_quick_start_flow360_1st.json => Flow360_New.json} (67%) delete mode 100644 examples/Quick_Starts/XV15/Submit_Case.py create mode 100644 examples/Quick_Starts/XV15/convergence_plots.py create mode 100644 examples/Quick_Starts/XV15/download_data.py create mode 100644 examples/Quick_Starts/XV15/download_plot_sectional_forces.py create mode 100644 examples/Quick_Starts/XV15/download_vis_figures.py create mode 100644 examples/Quick_Starts/XV15/submit_case_from_downloads.py create mode 100644 examples/Quick_Starts/XV15/submit_case_from_id.py diff --git a/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py b/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py deleted file mode 100644 index 35794ed..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Convergence_Plots.py +++ /dev/null @@ -1,85 +0,0 @@ -import matplotlib.pyplot as plt -from collections import defaultdict -import os, csv -import numpy as np -import pandas as pd - - - - -def plotLoads(data, plotName): - axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) - axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) - axes[0].set_ylabel('CL'); - axes[1].set_ylabel('CD'); - axes[0].set_ylim(0.27, 0.28); - axes[1].set_ylim(0.017, 0.018); - - for ax in axes: - ax.legend() - ax.set_xlabel('Pseudo step'); - ax.grid(which='major', color='gray') - -def plotResiduals(res, plotName): - x = res['pseudo_step'] - keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] - labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] - for ax, key, label in zip(axes, keys, labels): - for j, key in enumerate(keys): - ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) - ax.set_ylabel('Residuals') - ax.legend() - ax.set_title("ONERAM6 - Automated Meshing") - ax.grid(which='major', color='gray') - ax.set_xlabel('Pseudo step'); - ax.set_yticks(10.0**np.arange(-14,-3)); - ax.set_ylim([1e-9,1e-2]) - ax.set_xlim([0,5000]) - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -naming = ["ONERAM6"] -figures = [] -axes = [] - -def main(): - - #Plot loads convergence histories - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - - csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotLoads(data, naming[j]) - - - figures[0].savefig(f'figures/CL.png', dpi=500); - figures[1].savefig(f'figures/CD.png', dpi=500); - - for i in range(0,2): - axes[i].cla(); - - #Plot residual convergence histories - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') - res = pd.read_csv(csvPath, skipinitialspace=True) - plotResiduals(res, naming[j]) - - figures[0].savefig(f'figures/Residuals.png', dpi=500); - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Data.py b/examples/Quick_Starts/Automated_Meshing/Download_Data.py deleted file mode 100644 index 3f7c98f..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Download_Data.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import flow360 as fl -from flow360 import MyCases - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - #Download the files - case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py deleted file mode 100644 index 3cf2d30..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Download_Plot_Sectional_Forces.py +++ /dev/null @@ -1,70 +0,0 @@ -import os, csv -import numpy as np -import flow360 as fl -import pandas as pd -from flow360 import MyCases -import matplotlib.pyplot as plt -from collections import defaultdict - - - -def plotSectionalForces(data, plotName): - axes[0].plot(data['Y'], data['fluid/wing_CFx_per_span'], label=plotName) - axes[1].plot(data['Y'], data['fluid/wing_CFz_per_span'], label=plotName) - axes[0].set_ylabel('CFx per span'); - axes[1].set_ylabel('CFz per span'); - for ax in axes: - ax.legend() - ax.set_xlabel('Y'); - ax.grid(which='major', color='gray') - - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -naming = ["ONERAM6"] -figures = [] -axes = [] - - -def main(): - - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - forces = f"results/postprocess/forceDistribution.csv" - #print(case.results) - case._download_file(forces, to_folder=caseFolder) - - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotSectionalForces(data, naming[j]) - - figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); - figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); - - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py b/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py deleted file mode 100644 index aa27a96..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Download_Vis_Figures.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import numpy as np -import flow360 as fl -#from flow360.component.case import CaseDownloadable -#from flow360.component.resource_base import Flow360Resource - -from flow360 import MyCases -from PIL import Image - -def fetchPNG(case, field, res = 'H'): - # download the legend i.e. color bar - casename = case.name - os.makedirs('visualize', exist_ok=True) - - legend = f'visualize/{field}Legend.png' - case._download_file(legend, to_file=legend) - - # list all available theta and phi - viewAngles = [(0, 0),(180, 0)] - for theta in [60, 120]: - for phi in range(0,360,90): - viewAngles.append((theta, phi)) - # download the contour + axis files - for theta, phi in viewAngles: - fname = f'{theta:03d}_{phi:03d}.png' - contour = f'visualize/{field}_{res}_{fname}' - axis = f'visualize/Ax_{fname}' - case._download_file(contour, to_file=contour) - case._download_file(axis, to_file=axis) - # load and overlap contour + axis + legend - imgContour = Image.open(contour).convert('RGBA') - imgAxis = Image.open(axis) - imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) - imgAxis = imgAxis.convert('RGBA') - imgLegend = Image.open(legend).convert('RGBA') - background = Image.new('RGBA', imgContour.size, (67,100,200)) - imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) - imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) - background.paste(imgContour, (0,0), imgContour) - background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') - -def main(): - os.makedirs('visualization_figures', exist_ok=True) - with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - my_cases = MyCases(limit=None) - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - fetchPNG(case, 'Cp') - fetchPNG(case, 'Cf') - fetchPNG(case, 'Fv') - -if __name__ == "__main__": - main() diff --git a/examples/Quick_Starts/Automated_Meshing/Files.py b/examples/Quick_Starts/Automated_Meshing/Files.py deleted file mode 100644 index 432587b..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Files.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -from flow360.examples import base_test_case -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument('--localFiles', default = 0, type = int, required = False) -args = parser.parse_args() -local = args.localFiles - -base_test_case.here = os.path.dirname(os.path.abspath(__file__)) - -class ONERAM6(base_test_case.BaseTestCase): - name = "localFiles" - if local==1: - class url: - geometry = "local://om6wing.csm" - surface_json = "local://om6SurfaceMesh.json" - volume_json = "local://om6VolumeMesh.json" - case_json = "local://om6Case.json" - else: - class url: - geometry = "https://simcloud-public-1.s3.amazonaws.com/om6QuickStartAutoMesh/om6wing.csm" - surface_json = "local://om6SurfaceMesh.json" - volume_json = "local://om6VolumeMesh.json" - case_json = "local://om6Case.json" - diff --git a/examples/Quick_Starts/Automated_Meshing/README.md b/examples/Quick_Starts/Automated_Meshing/README.md index c829d53..3207f91 100644 --- a/examples/Quick_Starts/Automated_Meshing/README.md +++ b/examples/Quick_Starts/Automated_Meshing/README.md @@ -1,11 +1,13 @@ -To run the demo case follow these steps: +The following example demonstrates the use of automated meshing using the ONERA M6 wing. -1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. +To run the demo case follow these steps: -2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. +1. Run 'python3 submit_cases.py` -> this script will upload the geometry file to the Flexcompute servers, generate the surface and volume mesh and then submit the CFD case. -3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. +2. Run `python download_data.py` -> this script will download the csv files containing the loads and residual histories. -Files.py contains reference to the location of the meshes and JSON files. The default option is to use files from Flexcompute storage servers. To run with files stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 +3. Run `python convergence_plots.py` -> this script will plot the load and residual convergence histories. +4. Run `python download_plot_sectional_forces.py` -> this script will download and plot the sectional loads. +5. Run `download_vis_figures.py` -> this script will download Cp, Cf and streamline visualizations to the folder vis_figures. diff --git a/examples/Quick_Starts/Automated_Meshing/Submit_Case.py b/examples/Quick_Starts/Automated_Meshing/Submit_Case.py deleted file mode 100644 index d847b14..0000000 --- a/examples/Quick_Starts/Automated_Meshing/Submit_Case.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import flow360 as fl -from Files import ONERAM6 - -print("Obtaining mesh and solver files") - -ONERAM6.get_files() - -print("Files accessed") - -caseNameList=[] - -params = fl.SurfaceMeshingParams(ONERAM6.surface_json) -surface_mesh = fl.SurfaceMesh.create( - ONERAM6.geometry, params=params, name="ONERAM6_Automated_Meshing_Surface" -) -surface_mesh = surface_mesh.submit() - - -params = fl.VolumeMeshingParams(ONERAM6.volume_json) -volume_mesh = surface_mesh.create_volume_mesh("ONERAM6_Automated_Meshing_Volume", params=params) -volume_mesh = volume_mesh.submit() - -params = fl.Flow360Params(ONERAM6.case_json) - -#submit case -case = fl.Case.create("ONERAM6_Automated_Meshing", params, volume_mesh.id) -caseNameList.append(case.name) -case = case.submit() - -# dump case name for use in download and post-processing scripts - -with open('caseNameList.dat', 'w') as f: - for line in caseNameList: - f.write(f"{line}\n") diff --git a/examples/Quick_Starts/Automated_Meshing/convergence_plots.py b/examples/Quick_Starts/Automated_Meshing/convergence_plots.py new file mode 100644 index 0000000..9d69a2a --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/convergence_plots.py @@ -0,0 +1,82 @@ +"""Plot the loads and residuals convergence plots for ONERA M6 quick-start""" + +import os + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["CL", "CD"] +residuals = ["0_cont", "1_momx", "2_momy", "3_momz", "4_energ", "5_nuHat"] +figures = [] +axes = [] + + +def plot_loads(data, plot_name): + """Plot the loads""" + x = data["pseudo_step"] + for ax, load in zip(axes, loads): + ax.plot(x, data[load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Pseudo step") + ax.grid(which="major", color="gray") + if load == "CL": + ax.set_ylim(0.27, 0.28) + elif load == "CD": + ax.set_ylim(0.017, 0.018) + + +def plot_residuals(data, plot_name): + """Plot the residuals""" + x = data["pseudo_step"] + for ax, res in zip(axes, residuals): + for res in residuals: + ax.semilogy(x, data[res], label=plot_name + " " + res) + ax.set_ylabel("Residuals") + ax.legend(fontsize="8") + ax.set_title("ONERAM6 - Automated Meshing Quick-Start") + ax.grid(which="major", color="gray") + ax.set_xlabel("Pseudo step") + ax.set_yticks(10.0 ** np.arange(-14, -2)) + ax.set_ylim([1e-7, 1e-2]) + ax.set_xlim([0, 5000]) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +# calculate and plot loads convergence histories +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "total_forces_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_loads(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) + +for ax in axes: + ax.cla() + + +# plot residual convergence histories +fig, ax = plt.subplots(figsize=(8, 6)) +figures.append(fig) +axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), case_name, "nonlinear_residual_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_residuals(data, case_name) + +figures[0].savefig(os.path.join(dir_path, "Residuals.png"), dpi=500) diff --git a/examples/Quick_Starts/Automated_Meshing/download_data.py b/examples/Quick_Starts/Automated_Meshing/download_data.py new file mode 100644 index 0000000..2429730 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/download_data.py @@ -0,0 +1,29 @@ +"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" + +import os + +from flow360 import MyCases + +# read in case_name_list +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +my_cases = MyCases(limit=None) + +case = None + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + # download the files + case.results.download( + nonlinear_residuals=True, + surface_forces=True, + total_forces=True, + destination=case_folder, + ) diff --git a/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py b/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py new file mode 100644 index 0000000..5b8242d --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py @@ -0,0 +1,58 @@ +"""Plot the sectional loads for the ONERA M6 wing quick-start case""" + +import os + +import matplotlib.pyplot as plt +import pandas as pd +from flow360 import MyCases + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["wing_CFx_per_span", "wing_CFz_per_span"] +figures = [] +axes = [] + + +def plot_sectional_forces(data, plot_name): + """Plots the sectional loads""" + for ax, load in zip(axes, loads): + ax.plot(data["Y"], data["fluid/" + load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Y") + ax.grid(which="major", color="gray") + + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == case_name: + break + print(case.name) + forces = "results/postprocess/forceDistribution.csv" + # print(case.results) + case._download_file(forces, to_folder=case_folder) + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "forceDistribution.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_sectional_forces(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) diff --git a/examples/Quick_Starts/Automated_Meshing/download_vis_figures.py b/examples/Quick_Starts/Automated_Meshing/download_vis_figures.py new file mode 100644 index 0000000..b91ed37 --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/download_vis_figures.py @@ -0,0 +1,70 @@ +"""Downloads visualizations of surface CP, CF and streamlines""" + +import os + +from flow360 import MyCases +from PIL import Image + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +fields = ["Cp", "Cf", "Fv"] + + +def fetch_png(case, field, res="H"): + """Download the required figures from Flexcompute servers""" + # download the legend i.e. color bar + case_name = case.name + os.makedirs("visualize", exist_ok=True) + + legend = f"visualize/{field}Legend.png" + case._download_file(legend, to_file=legend) + + # list all available theta and phi + view_angles = [(0, 0), (180, 0)] + for theta in [60, 120]: + for phi in range(0, 360, 90): + view_angles.append((theta, phi)) + # download the contour + axis files + for theta, phi in view_angles: + fname = f"{theta:03d}_{phi:03d}.png" + contour = f"visualize/{field}_{res}_{fname}" + axis = f"visualize/Ax_{fname}" + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + img_contour = Image.open(contour).convert("RGBA") + img_axis = Image.open(axis) + img_axis = img_axis.resize((img_axis.width * 3, img_axis.height * 3)) + img_axis = img_axis.convert("RGBA") + img_legend = Image.open(legend).convert("RGBA") + background = Image.new("RGBA", img_contour.size, (67, 100, 200)) + img_contour.paste(img_axis, (0, img_contour.height - img_axis.height), img_axis) + img_contour.paste( + img_legend, + (-int(0.1 * img_contour.height), int(0.1 * img_contour.height)), + img_legend, + ) + background.paste(img_contour, (0, 0), img_contour) + background.save( + f"vis_figures/{case_name}_{field}_{res}_{theta:03d}_{phi:03d}_final.png" + ) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "vis_figures") +os.makedirs(dir_path, exist_ok=True) + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + for field in fields: + fetch_png(case, field) diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json b/examples/Quick_Starts/Automated_Meshing/om6Case.json similarity index 100% rename from examples/Quick_Starts/Automated_Meshing/localFiles/om6Case.json rename to examples/Quick_Starts/Automated_Meshing/om6Case.json diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json b/examples/Quick_Starts/Automated_Meshing/om6SurfaceMesh.json similarity index 100% rename from examples/Quick_Starts/Automated_Meshing/localFiles/om6SurfaceMesh.json rename to examples/Quick_Starts/Automated_Meshing/om6SurfaceMesh.json diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json b/examples/Quick_Starts/Automated_Meshing/om6VolumeMesh.json similarity index 100% rename from examples/Quick_Starts/Automated_Meshing/localFiles/om6VolumeMesh.json rename to examples/Quick_Starts/Automated_Meshing/om6VolumeMesh.json diff --git a/examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm b/examples/Quick_Starts/Automated_Meshing/om6wing.csm similarity index 100% rename from examples/Quick_Starts/Automated_Meshing/localFiles/om6wing.csm rename to examples/Quick_Starts/Automated_Meshing/om6wing.csm diff --git a/examples/Quick_Starts/Automated_Meshing/submit_case.py b/examples/Quick_Starts/Automated_Meshing/submit_case.py new file mode 100644 index 0000000..001c32c --- /dev/null +++ b/examples/Quick_Starts/Automated_Meshing/submit_case.py @@ -0,0 +1,33 @@ +"""Submit surface mesh from geometry file, volume mesh from surface mesh and case from volume mesh""" + +import flow360 as fl + +case_name_list = [] + +# submit surface mesh +params = fl.SurfaceMeshingParams("om6SurfaceMesh.json") +surface_mesh = fl.SurfaceMesh.create( + "om6wing.csm", params=params, name="ONERAM6_Automated_Meshing_Surface" +) +surface_mesh = surface_mesh.submit() + +# submit volume mesh +params = fl.VolumeMeshingParams("om6VolumeMesh.json") +volume_mesh = surface_mesh.create_volume_mesh( + "ONERAM6_Automated_Meshing_Volume", params=params +) +volume_mesh = volume_mesh.submit() + + +params = fl.Flow360Params("om6Case.json") + +# submit case +case = fl.Case.create("ONERAM6_Automated_Meshing", params, volume_mesh.id) +case_name_list.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/ONERAM6/Convergence_Plots.py b/examples/Quick_Starts/ONERAM6/Convergence_Plots.py deleted file mode 100644 index 5e02433..0000000 --- a/examples/Quick_Starts/ONERAM6/Convergence_Plots.py +++ /dev/null @@ -1,85 +0,0 @@ -import matplotlib.pyplot as plt -from collections import defaultdict -import os, csv -import numpy as np -import pandas as pd - - - - -def plotLoads(data, plotName): - axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) - axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) - axes[0].set_ylabel('CL'); - axes[1].set_ylabel('CD'); - axes[0].set_ylim(0.255, 0.265); - axes[1].set_ylim(0.0203, 0.0212); - - for ax in axes: - ax.legend() - ax.set_xlabel('Pseudo step'); - ax.grid(which='major', color='gray') - -def plotResiduals(res, plotName): - x = res['pseudo_step'] - keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] - labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] - for ax, key, label in zip(axes, keys, labels): - for j, key in enumerate(keys): - ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) - ax.set_ylabel('Residuals') - ax.legend() - ax.set_title("ONERAM6 - Quickstart") - ax.grid(which='major', color='gray') - ax.set_xlabel('Pseudo step'); - ax.set_yticks(10.0**np.arange(-14,-3)); - ax.set_ylim([1e-9,1e-2]) - ax.set_xlim([0,500]) - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -naming = ["ONERAM6"] -figures = [] -axes = [] - -def main(): - - #Plot loads convergence histories - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - - csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotLoads(data, naming[j]) - - - figures[0].savefig(f'figures/CL.png', dpi=500); - figures[1].savefig(f'figures/CD.png', dpi=500); - - for i in range(0,2): - axes[i].cla(); - - #Plot residual convergence histories - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') - res = pd.read_csv(csvPath, skipinitialspace=True) - plotResiduals(res, naming[j]) - - figures[0].savefig(f'figures/Residuals.png', dpi=500); - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/ONERAM6/Download_Data.py b/examples/Quick_Starts/ONERAM6/Download_Data.py deleted file mode 100644 index 3f7c98f..0000000 --- a/examples/Quick_Starts/ONERAM6/Download_Data.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import flow360 as fl -from flow360 import MyCases - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - #Download the files - case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py deleted file mode 100644 index 28e85a1..0000000 --- a/examples/Quick_Starts/ONERAM6/Download_Plot_Sectional_Forces.py +++ /dev/null @@ -1,70 +0,0 @@ -import os, csv -import numpy as np -import flow360 as fl -import pandas as pd -from flow360 import MyCases -import matplotlib.pyplot as plt -from collections import defaultdict - - - -def plotSectionalForces(data, plotName): - axes[0].plot(data['Y'], data['wing_CFx_per_span'], label=plotName) - axes[1].plot(data['Y'], data['wing_CFz_per_span'], label=plotName) - axes[0].set_ylabel('CFx per span'); - axes[1].set_ylabel('CFz per span'); - for ax in axes: - ax.legend() - ax.set_xlabel('Y'); - ax.grid(which='major', color='gray') - - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -naming = ["ONERAM6"] -figures = [] -axes = [] - - -def main(): - - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - forces = f"results/postprocess/forceDistribution.csv" - #print(case.results) - case._download_file(forces, to_folder=caseFolder) - - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotSectionalForces(data, naming[j]) - - figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); - figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); - - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py b/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py deleted file mode 100644 index aa27a96..0000000 --- a/examples/Quick_Starts/ONERAM6/Download_Vis_Figures.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import numpy as np -import flow360 as fl -#from flow360.component.case import CaseDownloadable -#from flow360.component.resource_base import Flow360Resource - -from flow360 import MyCases -from PIL import Image - -def fetchPNG(case, field, res = 'H'): - # download the legend i.e. color bar - casename = case.name - os.makedirs('visualize', exist_ok=True) - - legend = f'visualize/{field}Legend.png' - case._download_file(legend, to_file=legend) - - # list all available theta and phi - viewAngles = [(0, 0),(180, 0)] - for theta in [60, 120]: - for phi in range(0,360,90): - viewAngles.append((theta, phi)) - # download the contour + axis files - for theta, phi in viewAngles: - fname = f'{theta:03d}_{phi:03d}.png' - contour = f'visualize/{field}_{res}_{fname}' - axis = f'visualize/Ax_{fname}' - case._download_file(contour, to_file=contour) - case._download_file(axis, to_file=axis) - # load and overlap contour + axis + legend - imgContour = Image.open(contour).convert('RGBA') - imgAxis = Image.open(axis) - imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) - imgAxis = imgAxis.convert('RGBA') - imgLegend = Image.open(legend).convert('RGBA') - background = Image.new('RGBA', imgContour.size, (67,100,200)) - imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) - imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) - background.paste(imgContour, (0,0), imgContour) - background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') - -def main(): - os.makedirs('visualization_figures', exist_ok=True) - with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - my_cases = MyCases(limit=None) - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - fetchPNG(case, 'Cp') - fetchPNG(case, 'Cf') - fetchPNG(case, 'Fv') - -if __name__ == "__main__": - main() diff --git a/examples/Quick_Starts/ONERAM6/Files.py b/examples/Quick_Starts/ONERAM6/Files.py deleted file mode 100644 index a69b6ad..0000000 --- a/examples/Quick_Starts/ONERAM6/Files.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from flow360.examples import base_test_case -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument('--localFiles', default = 0, type = int, required = False) -args = parser.parse_args() -local = args.localFiles - -base_test_case.here = os.path.dirname(os.path.abspath(__file__)) - -class ONERAM6(base_test_case.BaseTestCase): - name = "localFiles" - if local==1: - class url: - mesh = "local://wing_tetra.1.lb8.ugrid" - case_json = "local://Flow360.json" - else: - class url: - mesh = "https://simcloud-public-1.s3.amazonaws.com/om6/wing_tetra.1.lb8.ugrid" - case_json = "local://Flow360.json" diff --git a/examples/Quick_Starts/ONERAM6/localFiles/Flow360.json b/examples/Quick_Starts/ONERAM6/Flow360.json similarity index 100% rename from examples/Quick_Starts/ONERAM6/localFiles/Flow360.json rename to examples/Quick_Starts/ONERAM6/Flow360.json diff --git a/examples/Quick_Starts/ONERAM6/README.md b/examples/Quick_Starts/ONERAM6/README.md index c475c38..02ec3b8 100644 --- a/examples/Quick_Starts/ONERAM6/README.md +++ b/examples/Quick_Starts/ONERAM6/README.md @@ -1,16 +1,16 @@ +The following example, launches the OM6 Quick-start case. + To run the demo case follow these steps: -1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. +1. Run `python submit_cases_from_downloads.py` -> this script will download the ONERAM6 mesh, and upload it to the Flexcompute servers. The case will also be submitted. Alternatively the script `submit_cases_from_id.py` can also be used to run the case from a mesh already uploaded to Flexcompute servers. -2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. +2. Run `python download_data.py` -> this script will download the csv files containing the loads and residual histories. -3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. +3. Run `python convergence_plots.py` -> this script will plot the load and residual convergence histories. -4. Run Download_Plot_Sectional_Forces.py -> this script will download and plot the sectional loads. +4. Run `python download_plot_sectional_forces.py` -> this script will download and plot the sectional loads. -5. Download_Vis_Figures.py -> this script will download Cp, Cf and streamline visualizations. +5. Run `download_vis_figures.py` -> this script will download Cp, Cf and streamline visualizations to the vis_figures folder. -Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh from Flexcompute storage servers and the Flow360.json file located in the "localFiles directory". -To run with a mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 diff --git a/examples/Quick_Starts/ONERAM6/Submit_Case.py b/examples/Quick_Starts/ONERAM6/Submit_Case.py deleted file mode 100644 index 25349ee..0000000 --- a/examples/Quick_Starts/ONERAM6/Submit_Case.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -import flow360 as fl -from Files import ONERAM6 - -print("Obtaining mesh and solver files") - -ONERAM6.get_files() - -print("Files accessed") - -caseNameList=[] - -# submit mesh -volume_mesh = fl.VolumeMesh.from_file(ONERAM6.mesh_filename, name="ONERAM6_Mesh") -volume_mesh = volume_mesh.submit() - - -params = fl.Flow360Params(ONERAM6.case_json) - -#submit case -case = fl.Case.create("ONERAM6_Quickstart", params, volume_mesh.id) -caseNameList.append(case.name) -case = case.submit() - -# dump case name for use in download and post-processing scripts - -with open('caseNameList.dat', 'w') as f: - for line in caseNameList: - f.write(f"{line}\n") diff --git a/examples/Quick_Starts/ONERAM6/convergence_plots.py b/examples/Quick_Starts/ONERAM6/convergence_plots.py new file mode 100644 index 0000000..3f1abfc --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/convergence_plots.py @@ -0,0 +1,82 @@ +"""Plot the loads and residuals convergence plots for ONERA M6 quick-start""" + +import os + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["CL", "CD"] +residuals = ["0_cont", "1_momx", "2_momy", "3_momz", "4_energ", "5_nuHat"] +figures = [] +axes = [] + + +def plot_loads(data, plot_name): + """Plot the loads""" + x = data["pseudo_step"] + for ax, load in zip(axes, loads): + ax.plot(x, data[load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Pseudo step") + ax.grid(which="major", color="gray") + if load == "CL": + ax.set_ylim(0.255, 0.265) + elif load == "CD": + ax.set_ylim(0.0203, 0.0212) + + +def plot_residuals(data, plot_name): + """Plot the residuals""" + x = data["pseudo_step"] + for ax, res in zip(axes, residuals): + for res in residuals: + ax.semilogy(x, data[res], label=plot_name + " " + res) + ax.set_ylabel("Residuals") + ax.legend(fontsize="8") + ax.set_title("ONERAM6 - Quick-Start") + ax.grid(which="major", color="gray") + ax.set_xlabel("Pseudo step") + ax.set_yticks(10.0 ** np.arange(-14, -2)) + ax.set_ylim([1e-9, 1e-2]) + ax.set_xlim([0, 500]) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +# calculate and plot loads convergence histories +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "total_forces_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_loads(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) + +for ax in axes: + ax.cla() + + +# plot residual convergence histories +fig, ax = plt.subplots(figsize=(8, 6)) +figures.append(fig) +axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), case_name, "nonlinear_residual_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_residuals(data, case_name) + +figures[0].savefig(os.path.join(dir_path, "Residuals.png"), dpi=500) diff --git a/examples/Quick_Starts/ONERAM6/download_data.py b/examples/Quick_Starts/ONERAM6/download_data.py new file mode 100644 index 0000000..2429730 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/download_data.py @@ -0,0 +1,29 @@ +"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" + +import os + +from flow360 import MyCases + +# read in case_name_list +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +my_cases = MyCases(limit=None) + +case = None + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + # download the files + case.results.download( + nonlinear_residuals=True, + surface_forces=True, + total_forces=True, + destination=case_folder, + ) diff --git a/examples/Quick_Starts/ONERAM6/download_plot_sectional_forces.py b/examples/Quick_Starts/ONERAM6/download_plot_sectional_forces.py new file mode 100644 index 0000000..e3c72f7 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/download_plot_sectional_forces.py @@ -0,0 +1,57 @@ +"""Plot the sectional loads for the ONERA M6 wing quick-start case""" + +import os + +import matplotlib.pyplot as plt +import pandas as pd +from flow360 import MyCases + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["wing_CFx_per_span", "wing_CFz_per_span"] +figures = [] +axes = [] + + +def plot_sectional_forces(data, plot_name): + """Plots the sectional loads""" + for ax, load in zip(axes, loads): + ax.plot(data["Y"], data[load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Y") + ax.grid(which="major", color="gray") + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == case_name: + break + print(case.name) + forces = "results/postprocess/forceDistribution.csv" + # print(case.results) + case._download_file(forces, to_folder=case_folder) + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "forceDistribution.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_sectional_forces(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) diff --git a/examples/Quick_Starts/ONERAM6/download_vis_figures.py b/examples/Quick_Starts/ONERAM6/download_vis_figures.py new file mode 100644 index 0000000..b91ed37 --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/download_vis_figures.py @@ -0,0 +1,70 @@ +"""Downloads visualizations of surface CP, CF and streamlines""" + +import os + +from flow360 import MyCases +from PIL import Image + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +fields = ["Cp", "Cf", "Fv"] + + +def fetch_png(case, field, res="H"): + """Download the required figures from Flexcompute servers""" + # download the legend i.e. color bar + case_name = case.name + os.makedirs("visualize", exist_ok=True) + + legend = f"visualize/{field}Legend.png" + case._download_file(legend, to_file=legend) + + # list all available theta and phi + view_angles = [(0, 0), (180, 0)] + for theta in [60, 120]: + for phi in range(0, 360, 90): + view_angles.append((theta, phi)) + # download the contour + axis files + for theta, phi in view_angles: + fname = f"{theta:03d}_{phi:03d}.png" + contour = f"visualize/{field}_{res}_{fname}" + axis = f"visualize/Ax_{fname}" + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + img_contour = Image.open(contour).convert("RGBA") + img_axis = Image.open(axis) + img_axis = img_axis.resize((img_axis.width * 3, img_axis.height * 3)) + img_axis = img_axis.convert("RGBA") + img_legend = Image.open(legend).convert("RGBA") + background = Image.new("RGBA", img_contour.size, (67, 100, 200)) + img_contour.paste(img_axis, (0, img_contour.height - img_axis.height), img_axis) + img_contour.paste( + img_legend, + (-int(0.1 * img_contour.height), int(0.1 * img_contour.height)), + img_legend, + ) + background.paste(img_contour, (0, 0), img_contour) + background.save( + f"vis_figures/{case_name}_{field}_{res}_{theta:03d}_{phi:03d}_final.png" + ) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "vis_figures") +os.makedirs(dir_path, exist_ok=True) + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + for field in fields: + fetch_png(case, field) diff --git a/examples/Quick_Starts/ONERAM6/submit_case_from_download.py b/examples/Quick_Starts/ONERAM6/submit_case_from_download.py new file mode 100644 index 0000000..f66211c --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/submit_case_from_download.py @@ -0,0 +1,33 @@ +"""Case submission through mesh download and upload onto Flexcompute servers""" + +import os.path +from urllib.request import urlretrieve + +import flow360 as fl + +case_name_list = [] + +# download mesh to the current directory +URL = "https://simcloud-public-1.s3.amazonaws.com/om6/wing_tetra.1.lb8.ugrid" +MESH_FILENAME = "wing_tetra.1.lb8.ugrid" + +if not os.path.isfile(MESH_FILENAME): + urlretrieve(URL, MESH_FILENAME) + + +# submit mesh +volume_mesh = fl.VolumeMesh.from_file(MESH_FILENAME, name="ONERAM6_Mesh") +volume_mesh = volume_mesh.submit() + +params = fl.Flow360Params("Flow360.json") + +# submit case +case = fl.Case.create("ONERAM6_Quickstart", params, volume_mesh.id) +case_name_list.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/ONERAM6/submit_case_from_id.py b/examples/Quick_Starts/ONERAM6/submit_case_from_id.py new file mode 100644 index 0000000..b9dc79f --- /dev/null +++ b/examples/Quick_Starts/ONERAM6/submit_case_from_id.py @@ -0,0 +1,20 @@ +"""Case submission from mesh ID's present on the Flexcompute servers""" + +import flow360 as fl + +case_name_list = [] + +MESH_ID = "33f46ac0-e4e6-468e-be86-2289817b6472" + +params = fl.Flow360Params("Flow360.json") + +# submit case +case = fl.Case.create("ONERAM6_Quickstart", params, MESH_ID) +case_name_list.append(case.name) +case = case.submit() + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/S809/Convergence_Plots.py b/examples/Quick_Starts/S809/Convergence_Plots.py deleted file mode 100644 index f95dd45..0000000 --- a/examples/Quick_Starts/S809/Convergence_Plots.py +++ /dev/null @@ -1,90 +0,0 @@ -import matplotlib.pyplot as plt -from collections import defaultdict -import os, csv -import numpy as np -import pandas as pd - - - - -def plotLoads(data, plotName): - axes[0].plot(data['pseudo_step'], data['CL'], label=plotName) - axes[1].plot(data['pseudo_step'], data['CD'], label=plotName) - axes[0].set_ylabel('CL'); - axes[1].set_ylabel('CD'); - axes[0].set_ylim(0.7, 0.8); - axes[1].set_ylim(0.01, 0.016); - - for ax in axes: - ax.legend() - ax.set_xlabel('Pseudo step'); - ax.grid(which='major', color='gray') - -def plotResiduals(res, plotName): - x = res['pseudo_step'] - keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] - labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] - for ax, key, label in zip(axes, keys, labels): - for j, key in enumerate(keys): - if plotName =="Automated Meshing": - ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) - else: - ax.semilogy(x, res[key], "--", label = plotName + ' ' + labels[j]) - ax.set_ylabel('Residuals') - ax.legend() - ax.set_title("ONERAM6") - ax.grid(which='major', color='gray') - ax.set_xlabel('Pseudo step'); - ax.set_yticks(10.0**np.arange(-14,-3)); - ax.set_ylim([1e-13,1e-2]) - ax.set_xlim([0,5000]) - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -naming = ["Automated Meshing", "Structured Mesh"] -figures = [] -axes = [] - -def main(): - - #Plot loads convergence histories - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - - csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotLoads(data, naming[j]) - - - figures[0].savefig(f'figures/CL.png', dpi=500); - figures[1].savefig(f'figures/CD.png', dpi=500); - - for i in range(0,2): - axes[i].cla(); - - #Plot residual convergence histories - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - for j, resolution in enumerate(caseNameList): - if "Structured" in resolution: - figures[0].gca().set_prop_cycle(None) - csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') - res = pd.read_csv(csvPath, skipinitialspace=True) - plotResiduals(res, naming[j]) - - figures[0].savefig(f'figures/Residuals.png', dpi=500); - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/S809/Download_Data.py b/examples/Quick_Starts/S809/Download_Data.py deleted file mode 100644 index 3f7c98f..0000000 --- a/examples/Quick_Starts/S809/Download_Data.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import flow360 as fl -from flow360 import MyCases - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - #Download the files - case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/S809/Files.py b/examples/Quick_Starts/S809/Files.py deleted file mode 100644 index b1c6072..0000000 --- a/examples/Quick_Starts/S809/Files.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -from flow360.examples import base_test_case -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument('--localFiles', default = 0, type = int, required = False) -args = parser.parse_args() -local = args.localFiles - -base_test_case.here = os.path.dirname(os.path.abspath(__file__)) - -class S809(base_test_case.BaseTestCase): - name = "localFiles" - if local==1: - class url: - geometry = "local://s809.csm" - surface_json = "local://s809SurfaceMesh.json" - volume_json = "local://s809VolumeMesh.json" - case_json = "local://s809Case.json" - mesh = "local://s809structured.cgns" - else: - class url: - geometry = "https://simcloud-public-1.s3.amazonaws.com/s809/s809.csm" - surface_json = "local://s809SurfaceMesh.json" - volume_json = "local://s809VolumeMesh.json" - case_json = "local://s809Case.json" - mesh = "https://simcloud-public-1.s3.amazonaws.com/s809/s809structured.cgns" - diff --git a/examples/Quick_Starts/S809/README.md b/examples/Quick_Starts/S809/README.md index 6d1b6bb..c4b70b2 100644 --- a/examples/Quick_Starts/S809/README.md +++ b/examples/Quick_Starts/S809/README.md @@ -1,11 +1,9 @@ -To run the demo case follow these steps: - -1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. +The following example demonstrates the automated meshing workflow for the S809 airfoil and compares the results with the pre-meshed structured grid. -2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. - -3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. +To run the demo case follow these steps: -Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh/files from Flexcompute storage servers. To run with a files/mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 +1. Run `python submit_cases_from_downloads.py` -> this script will download the structured S809 mesh, and upload it to the Flexcompute servers. The cases will also be submitted for both automated mesh and structured mesh. Alternatively the script `submit_cases_from_id.py` can also be used to run the structured mesh case from a meshe already uploaded to Flexcompute servers. +2. Run `python download_data.py` -> this script will download the csv files containing the loads and residual histories. +3. Run `python convergence_plots.py` to cross-plot the loads and residual convergence histories for the automated mesh and structured mesh cases. diff --git a/examples/Quick_Starts/S809/Submit_Case.py b/examples/Quick_Starts/S809/Submit_Case.py deleted file mode 100644 index 26c6611..0000000 --- a/examples/Quick_Starts/S809/Submit_Case.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import flow360 as fl -from Files import S809 - -print("Obtaining mesh and solver files") - -S809.get_files() - -print("Files accessed") - -caseNameList=[] - -params = fl.SurfaceMeshingParams(S809.surface_json) -surface_mesh = fl.SurfaceMesh.create( - S809.geometry, params=params, name="NREL_S809_Surface" -) -surface_mesh = surface_mesh.submit() - -params = fl.VolumeMeshingParams(S809.volume_json) -volume_mesh = surface_mesh.create_volume_mesh("NREL_S809_Volume", params=params) -volume_mesh = volume_mesh.submit() - -params = fl.Flow360Params(S809.case_json) - -#submit case -case = fl.Case.create("NREL_S809_Automated_Meshing", params, volume_mesh.id) -caseNameList.append(case.name) -case = case.submit() -# submit mesh - -volume_mesh2 = fl.VolumeMesh.from_file(S809.mesh_filename, name="S809_Structured_Mesh") -volume_mesh2 = volume_mesh2.submit() - - - -#submit case -case = fl.Case.create("NREL_S809_Structured", params, volume_mesh2.id) -caseNameList.append(case.name) -case = case.submit() - -# dump case name for use in download and post-processing scripts - -with open('caseNameList.dat', 'w') as f: - for line in caseNameList: - f.write(f"{line}\n") diff --git a/examples/Quick_Starts/S809/convergence_plots.py b/examples/Quick_Starts/S809/convergence_plots.py new file mode 100644 index 0000000..5068a3e --- /dev/null +++ b/examples/Quick_Starts/S809/convergence_plots.py @@ -0,0 +1,87 @@ +"""Plot the loads and residuals convergence plots for automated meshing and structured grids S809 cases""" + +import os + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["CL", "CD"] +residuals = ["0_cont", "1_momx", "2_momy", "3_momz", "4_energ", "5_nuHat"] +figures = [] +axes = [] + + +def plot_loads(data, plot_name): + """Plot the loads""" + x = data["pseudo_step"] + for ax, load in zip(axes, loads): + ax.plot(x, data[load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Pseudo step") + ax.grid(which="major", color="gray") + if load == "CL": + ax.set_ylim(0.7, 0.8) + elif load == "CD": + ax.set_ylim(0.01, 0.016) + + +def plot_residuals(data, plot_name): + """Plot the residuals""" + x = data["pseudo_step"] + for ax, res in zip(axes, residuals): + for res in residuals: + if "Structured" in plot_name: + ax.semilogy(x, data[res], label=plot_name + " " + res) + else: + ax.semilogy(x, data[res], "--", label=plot_name + " " + res) + ax.set_ylabel("Residuals") + ax.legend(fontsize="8") + ax.set_title("S809") + ax.grid(which="major", color="gray") + ax.set_xlabel("Pseudo step") + ax.set_yticks(10.0 ** np.arange(-14, -3)) + ax.set_ylim([1e-11, 1e-3]) + ax.set_xlim([0, 5000]) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +# calculate and plot loads convergence histories +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "total_forces_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_loads(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) + +for ax in axes: + ax.cla() + + +# plot residual convergence histories +fig, ax = plt.subplots(figsize=(8, 6)) +figures.append(fig) +axes.append(ax) + +for case_name in case_name_list: + if "Structured" in case_name: + figures[0].gca().set_prop_cycle(None) + csv_path = os.path.join(os.getcwd(), case_name, "nonlinear_residual_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_residuals(data, case_name) + +figures[0].savefig(os.path.join(dir_path, "Residuals.png"), dpi=500) diff --git a/examples/Quick_Starts/S809/download_data.py b/examples/Quick_Starts/S809/download_data.py new file mode 100644 index 0000000..2429730 --- /dev/null +++ b/examples/Quick_Starts/S809/download_data.py @@ -0,0 +1,29 @@ +"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" + +import os + +from flow360 import MyCases + +# read in case_name_list +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +my_cases = MyCases(limit=None) + +case = None + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + # download the files + case.results.download( + nonlinear_residuals=True, + surface_forces=True, + total_forces=True, + destination=case_folder, + ) diff --git a/examples/Quick_Starts/S809/localFiles/s809.csm b/examples/Quick_Starts/S809/s809.csm similarity index 100% rename from examples/Quick_Starts/S809/localFiles/s809.csm rename to examples/Quick_Starts/S809/s809.csm diff --git a/examples/Quick_Starts/S809/localFiles/s809Case.json b/examples/Quick_Starts/S809/s809Case.json similarity index 100% rename from examples/Quick_Starts/S809/localFiles/s809Case.json rename to examples/Quick_Starts/S809/s809Case.json diff --git a/examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json b/examples/Quick_Starts/S809/s809SurfaceMesh.json similarity index 100% rename from examples/Quick_Starts/S809/localFiles/s809SurfaceMesh.json rename to examples/Quick_Starts/S809/s809SurfaceMesh.json diff --git a/examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json b/examples/Quick_Starts/S809/s809VolumeMesh.json similarity index 100% rename from examples/Quick_Starts/S809/localFiles/s809VolumeMesh.json rename to examples/Quick_Starts/S809/s809VolumeMesh.json diff --git a/examples/Quick_Starts/S809/submit_case_from_download.py b/examples/Quick_Starts/S809/submit_case_from_download.py new file mode 100644 index 0000000..14d0954 --- /dev/null +++ b/examples/Quick_Starts/S809/submit_case_from_download.py @@ -0,0 +1,47 @@ +"""Case submission using automated meshing and structured pre-generated mesh case submission from mesh download""" + +import os.path +from urllib.request import urlretrieve + +import flow360 as fl + +# inputs for structured mesh download +URL = "https://simcloud-public-1.s3.amazonaws.com/s809/s809structured.cgns" +MESH_FILENAME = "s809structured.cgns" + +case_name_list = [] + +params = fl.SurfaceMeshingParams("s809SurfaceMesh.json") +surface_mesh = fl.SurfaceMesh.create( + "s809.csm", params=params, name="NREL_S809_Surface" +) +surface_mesh = surface_mesh.submit() + +params = fl.VolumeMeshingParams("s809VolumeMesh.json") +volume_mesh = surface_mesh.create_volume_mesh("NREL_S809_Volume", params=params) +volume_mesh = volume_mesh.submit() + +params = fl.Flow360Params("s809Case.json") + +# submit case +case = fl.Case.create("NREL_S809_Automated_Meshing", params, volume_mesh.id) +case_name_list.append(case.name) +case = case.submit() + +if not os.path.isfile(MESH_FILENAME): + urlretrieve(URL, MESH_FILENAME) + +# submit mesh +volume_mesh2 = fl.VolumeMesh.from_file(MESH_FILENAME, name="S809_Structured_Mesh") +volume_mesh2 = volume_mesh2.submit() + +# submit case +case2 = fl.Case.create("NREL_S809_Structured", params, volume_mesh2.id) +case_name_list.append(case2.name) +case2 = case2.submit() + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/S809/submit_case_from_id.py b/examples/Quick_Starts/S809/submit_case_from_id.py new file mode 100644 index 0000000..6b10bd2 --- /dev/null +++ b/examples/Quick_Starts/S809/submit_case_from_id.py @@ -0,0 +1,37 @@ +"""Case submission using automated meshing and structured pre-generated mesh case submission from mesh ID""" + +import flow360 as fl + +# mesh ID for structured mesh +MESH_ID = "0ab89ce3-33e0-4d4e-bd4a-7249da3cd9c3" + +case_name_list = [] + +params = fl.SurfaceMeshingParams("s809SurfaceMesh.json") +surface_mesh = fl.SurfaceMesh.create( + "s809.csm", params=params, name="NREL_S809_Surface" +) +surface_mesh = surface_mesh.submit() + +params = fl.VolumeMeshingParams("s809VolumeMesh.json") +volume_mesh = surface_mesh.create_volume_mesh("NREL_S809_Volume", params=params) +volume_mesh = volume_mesh.submit() + +params = fl.Flow360Params("s809Case.json") + +# submit case +case = fl.Case.create("NREL_S809_Automated_Meshing", params, volume_mesh.id) +case_name_list.append(case.name) +case = case.submit() + + +# submit case +case2 = fl.Case.create("NREL_S809_Structured", params, MESH_ID) +case_name_list.append(case2.name) +case2 = case2.submit() + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/XV15/Convergence_Plots.py b/examples/Quick_Starts/XV15/Convergence_Plots.py deleted file mode 100644 index a2953b5..0000000 --- a/examples/Quick_Starts/XV15/Convergence_Plots.py +++ /dev/null @@ -1,96 +0,0 @@ -import matplotlib.pyplot as plt -from collections import defaultdict -import os, csv -import numpy as np -import pandas as pd - -def addAccumPseudoStep(history): - pseudoSteps = history['pseudo_step'] - accum = list() - accum.append(0) - for i in range(1,len(pseudoSteps)): - if pseudoSteps[i] > pseudoSteps[i-1]: - accum.append(accum[-1]+abs(pseudoSteps[i] - pseudoSteps[i-1])) - else: - accum.append(accum[-1]+1) - history['accum_pseudo_step'] = accum - - -def plotLoads(data, plotName): - axes[0].plot(data['accum_pseudo_step'], data['CFz'], label=plotName) - axes[1].plot(data['accum_pseudo_step'], data['CMz'], label=plotName) - axes[0].set_ylabel('CFz'); - axes[1].set_ylabel('CMz'); - #axes[0].set_ylim(0.255, 0.265); - #axes[1].set_ylim(0.0203, 0.0212); - - for ax in axes: - ax.legend() - ax.set_xlabel('Pseudo step'); - ax.grid(which='major', color='gray') - -def plotResiduals(res, plotName): - x = res['accum_pseudo_step'] - keys = ['0_cont', '1_momx', '2_momy', '3_momz', '4_energ', '5_nuHat'] - labels = ['Cont Residual', 'Momx Residual', 'Momy Residual', 'Momz Residual', 'Energy Residual', 'nuHat Residual'] - for ax, key, label in zip(axes, keys, labels): - for j, key in enumerate(keys): - ax.semilogy(x, res[key], label = plotName + ' ' + labels[j]) - ax.set_ylabel('Residuals') - ax.legend() - ax.set_title("XV-15 - Quickstart") - ax.grid(which='major', color='gray') - ax.set_xlabel('Pseudo step'); - ax.set_yticks(10.0**np.arange(-14,-3)); - ax.set_ylim([1e-9,1e-2]) - ax.set_xlim([0,7000]) - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -naming = ["XV15"] -figures = [] -axes = [] - -def main(): - - #Plot loads convergence histories - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - - csvPath = os.path.join(os.getcwd(),f'{resolution}','total_forces_v2.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - addAccumPseudoStep(data) - plotLoads(data, naming[j]) - - - figures[0].savefig(f'figures/CFz.png', dpi=500); - figures[1].savefig(f'figures/CMz.png', dpi=500); - - for i in range(0,2): - axes[i].cla(); - - #Plot residual convergence histories - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}', 'nonlinear_residual_v2.csv') - res = pd.read_csv(csvPath, skipinitialspace=True) - addAccumPseudoStep(res) - plotResiduals(res, naming[j]) - - figures[0].savefig(f'figures/Residuals.png', dpi=500); - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/XV15/Download_Data.py b/examples/Quick_Starts/XV15/Download_Data.py deleted file mode 100644 index 3f7c98f..0000000 --- a/examples/Quick_Starts/XV15/Download_Data.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import flow360 as fl -from flow360 import MyCases - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - #Download the files - case.results.download(nonlinear_residuals=True, surface_forces=True, total_forces=True, destination=caseFolder) diff --git a/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py b/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py deleted file mode 100644 index 2471eda..0000000 --- a/examples/Quick_Starts/XV15/Download_Plot_Sectional_Forces.py +++ /dev/null @@ -1,71 +0,0 @@ -import os, csv -import numpy as np -import flow360 as fl -import pandas as pd -from flow360 import MyCases -import matplotlib.pyplot as plt -from collections import defaultdict - - - -def plotSectionalForces(data, plotName): - axes[0].plot(data['Y'], data['innerRotating/blade_CFx_per_span'], label=plotName) - axes[1].plot(data['Y'], data['innerRotating/blade_CFz_per_span'], label=plotName) - axes[0].set_ylabel('CFx per span'); - axes[1].set_ylabel('CFz per span'); - axes[1].set_ylim(-0.002,0.01) - for ax in axes: - ax.legend() - ax.set_xlabel('Y'); - ax.grid(which='major', color='gray') - - - -#read in caseNameList - -with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - -my_cases = MyCases(limit=None) - -naming = ["XV15"] -figures = [] -axes = [] - - -def main(): - - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - forces = f"results/postprocess/forceDistribution.csv" - #print(case.results) - case._download_file(forces, to_folder=caseFolder) - - for i in [0,1]: - fig, ax = plt.subplots(figsize=(8,6)) - figures.append(fig) - axes.append(ax) - dir_path= os.path.join(os.getcwd(),f'figures') - if not os.path.isdir(dir_path): # if directory already exists: - os.makedirs(dir_path) # make that dir - - for j, resolution in enumerate(caseNameList): - csvPath = os.path.join(os.getcwd(),f'{resolution}','forceDistribution.csv') - data = pd.read_csv(csvPath, skipinitialspace=True) - plotSectionalForces(data, naming[j]) - - figures[0].savefig(f'figures/CFx_per_span.png', dpi=500); - figures[1].savefig(f'figures/CFz_per_span.png', dpi=500); - - - - -if __name__ == '__main__': - main() - diff --git a/examples/Quick_Starts/XV15/Download_Vis_Figures.py b/examples/Quick_Starts/XV15/Download_Vis_Figures.py deleted file mode 100644 index aa27a96..0000000 --- a/examples/Quick_Starts/XV15/Download_Vis_Figures.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import numpy as np -import flow360 as fl -#from flow360.component.case import CaseDownloadable -#from flow360.component.resource_base import Flow360Resource - -from flow360 import MyCases -from PIL import Image - -def fetchPNG(case, field, res = 'H'): - # download the legend i.e. color bar - casename = case.name - os.makedirs('visualize', exist_ok=True) - - legend = f'visualize/{field}Legend.png' - case._download_file(legend, to_file=legend) - - # list all available theta and phi - viewAngles = [(0, 0),(180, 0)] - for theta in [60, 120]: - for phi in range(0,360,90): - viewAngles.append((theta, phi)) - # download the contour + axis files - for theta, phi in viewAngles: - fname = f'{theta:03d}_{phi:03d}.png' - contour = f'visualize/{field}_{res}_{fname}' - axis = f'visualize/Ax_{fname}' - case._download_file(contour, to_file=contour) - case._download_file(axis, to_file=axis) - # load and overlap contour + axis + legend - imgContour = Image.open(contour).convert('RGBA') - imgAxis = Image.open(axis) - imgAxis = imgAxis.resize((imgAxis.width * 3, imgAxis.height * 3)) - imgAxis = imgAxis.convert('RGBA') - imgLegend = Image.open(legend).convert('RGBA') - background = Image.new('RGBA', imgContour.size, (67,100,200)) - imgContour.paste(imgAxis, (0,imgContour.height-imgAxis.height), imgAxis) - imgContour.paste(imgLegend, (-int(0.1*imgContour.height), int(0.1*imgContour.height)), imgLegend) - background.paste(imgContour, (0,0), imgContour) - background.save(f'visualization_figures/{casename}_{field}_{res}_{theta:03d}_{phi:03d}_final.png') - -def main(): - os.makedirs('visualization_figures', exist_ok=True) - with open('caseNameList.dat', 'r') as file: - caseNameList = file.read().splitlines() - my_cases = MyCases(limit=None) - for i in range(0, len(caseNameList)): - caseFolder = os.path.join(os.getcwd(), caseNameList[i]) - os.makedirs(caseFolder, exist_ok = True) - #Find the latest case with the name corresponding to the name in caseNameList - for case in my_cases: - if case.name == caseNameList[i]: - break - print(case.name) - fetchPNG(case, 'Cp') - fetchPNG(case, 'Cf') - fetchPNG(case, 'Fv') - -if __name__ == "__main__": - main() diff --git a/examples/Quick_Starts/XV15/Files.py b/examples/Quick_Starts/XV15/Files.py deleted file mode 100644 index 3afb576..0000000 --- a/examples/Quick_Starts/XV15/Files.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -from flow360.examples import base_test_case -import argparse - -parser = argparse.ArgumentParser() -parser.add_argument('--localFiles', default = 0, type = int, required = False) -args = parser.parse_args() -local = args.localFiles - -base_test_case.here = os.path.dirname(os.path.abspath(__file__)) - -class XV15_1st(base_test_case.BaseTestCase): - name = "localFiles" - if local==1: - class url: - case_json = "local://XV15_quick_start_flow360_1st.json" - mesh = "local://XV15_Hover_ascent_coarse.cgns" - else: - class url: - case_json = "local://XV15_quick_start_flow360_1st.json" - mesh = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_Hover_ascent_coarse.cgns" - -class XV15_2nd(base_test_case.BaseTestCase): - name = "localFiles" - if local==1: - class url: - case_json = "local://XV15_quick_start_flow360_2nd.json" - else: - class url: - case_json = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_quick_start_flow360_2nd.json" - diff --git a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json b/examples/Quick_Starts/XV15/Flow360.json similarity index 96% rename from examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json rename to examples/Quick_Starts/XV15/Flow360.json index 99ac463..8f85fab 100644 --- a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_2nd.json +++ b/examples/Quick_Starts/XV15/Flow360.json @@ -63,9 +63,7 @@ "physicalSteps" : 600, "maxPseudoSteps" : 35, "CFL" : { - "initial" : 1, - "final" : 1e7, - "rampSteps" : 33 + "type": "adaptive" } } } diff --git a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json b/examples/Quick_Starts/XV15/Flow360_New.json similarity index 67% rename from examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json rename to examples/Quick_Starts/XV15/Flow360_New.json index 57260d8..5377358 100644 --- a/examples/Quick_Starts/XV15/localFiles/XV15_quick_start_flow360_1st.json +++ b/examples/Quick_Starts/XV15/Flow360_New.json @@ -17,7 +17,7 @@ "relativeTolerance" : 1e-2, "linearIterations" : 35, "kappaMUSCL" : -1.0, - "orderOfAccuracy" : 1, + "orderOfAccuracy" : 2, "updateJacobianFrequency" : 4, "equationEvalFrequency" : 1 }, @@ -28,7 +28,7 @@ "linearIterations" : 25, "kappaMUSCL" : -1.0, "DDES" : true, - "orderOfAccuracy" : 1, + "orderOfAccuracy" : 2, "updateJacobianFrequency" : 4, "equationEvalFrequency" : 1, "rotationCorrection" : true @@ -44,28 +44,23 @@ }, "boundaries" : { "farField/farField" : { "type" : "Freestream" }, - "farField/rotationInterface" : { "type" : "SlidingInterface" }, - "innerRotating/rotationInterface" : { "type" : "SlidingInterface" }, "innerRotating/blade" : { "type" : "NoSlipWall" } }, - "slidingInterfaces" : [ - { - "stationaryPatches" : ["farField/rotationInterface"], - "rotatingPatches" : ["innerRotating/rotationInterface"], - "axisOfRotation" : [0,0,-1], - "centerOfRotation" : [0,0,0], - "omega" : 1.84691e-01, - "volumeName" : ["innerRotating"] - } - ], + "volumeZones":{ + "innerRotating":{ + "referenceFrame":{ + "axisOfRotation" : [0,0,-1], + "centerOfRotation" : [0,0,0], + "omegaRadians" : 1.84691e-01 + } + } + }, "timeStepping" : { - "timeStepSize" : 5.67000e-01, - "physicalSteps" : 60, - "maxPseudoSteps" : 12, + "timeStepSize" : 2.83500e-01, + "physicalSteps" : 600, + "maxPseudoSteps" : 35, "CFL" : { - "initial" : 1, - "final" : 1000, - "rampSteps" : 10 + "type": "adaptive" } } } diff --git a/examples/Quick_Starts/XV15/README.md b/examples/Quick_Starts/XV15/README.md index 6d1b6bb..54d8e74 100644 --- a/examples/Quick_Starts/XV15/README.md +++ b/examples/Quick_Starts/XV15/README.md @@ -1,11 +1,13 @@ -To run the demo case follow these steps: +The following example, launches the XV-15 Quick-start case. -1. Run Submit_Cases.py -> this script will obtain the mesh, and upload it to the Flexcompute servers. The case will also be submitted. +To run the demo case follow these steps: -2. Run Download_Data.py -> this script will download the csv files containing the loads and residual histories. +1. Run `python submit_cases_from_downloads.py` -> this script will download the XV15 mesh, and upload it to the Flexcompute servers. The case will also be submitted. Alternatively the script `submit_cases_from_id.py` can also be used to run the case from a mesh already uploaded to Flexcompute servers. -3. Run Convergence_Plots.py -> this script will plot the load and residual convergence histories. +2. Run `python download_data.py` -> this script will download the csv files containing the loads and residual histories. -Files.py contains reference to the location of the meshes and JSON files. The default option is to use a mesh/files from Flexcompute storage servers. To run with a files/mesh stored in the "localFiles" directory, Run Submit_Cases.py --localFiles 1 +3. Run `python convergence_plots.py` -> this script will plot the load and residual convergence histories. +4. Run `python download_plot_sectional_forces.py` -> this script will download and plot the sectional loads. +5. Run `download_vis_figures.py` -> this script will download Cp, Cf and streamline visualizations to the vis_figures folder. diff --git a/examples/Quick_Starts/XV15/Submit_Case.py b/examples/Quick_Starts/XV15/Submit_Case.py deleted file mode 100644 index fdbe44a..0000000 --- a/examples/Quick_Starts/XV15/Submit_Case.py +++ /dev/null @@ -1,38 +0,0 @@ -import os -import flow360 as fl -from Files import XV15_1st, XV15_2nd - -print("Obtaining mesh and solver files") - -XV15_1st.get_files() -XV15_2nd.get_files() - -print("Files accessed") - -caseNameList=[] - -# submit mesh - -volume_mesh = fl.VolumeMesh.from_file(XV15_1st.mesh_filename, name="XV15_Mesh") -volume_mesh = volume_mesh.submit() - -#submit case 1st order - -params = fl.Flow360Params(XV15_1st.case_json) -case = fl.Case.create("XV15_1st_order", params, volume_mesh.id) -case = case.submit() - -#submit case 2nd order -print(params) -case_fork_1 = case.fork(params=params) -case_fork_1.name = "XV15_2nd_order" -params2 = fl.Flow360Params(XV15_2nd.case_json) -case_fork_1.params = params2 -caseNameList.append(case_fork_1.name) -case_fork_1 = case_fork_1.submit() - -# dump case name for use in download and post-processing scripts - -with open('caseNameList.dat', 'w') as f: - for line in caseNameList: - f.write(f"{line}\n") diff --git a/examples/Quick_Starts/XV15/convergence_plots.py b/examples/Quick_Starts/XV15/convergence_plots.py new file mode 100644 index 0000000..8b7ad1a --- /dev/null +++ b/examples/Quick_Starts/XV15/convergence_plots.py @@ -0,0 +1,92 @@ +"""Plot the loads and residuals convergence plots for XV-15 quick-start""" + +import os + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["CFz", "CMz"] +residuals = ["0_cont", "1_momx", "2_momy", "3_momz", "4_energ", "5_nuHat"] +figures = [] +axes = [] + + +def add_accum_pseudo_step(history): + "Compute the accumulated pseudo-step" + pseudo_steps = history["pseudo_step"] + accum = [] + accum.append(0) + for i in range(1, len(pseudo_steps)): + if pseudo_steps[i] > pseudo_steps[i - 1]: + accum.append(accum[-1] + abs(pseudo_steps[i] - pseudo_steps[i - 1])) + else: + accum.append(accum[-1] + 1) + history["accum_pseudo_step"] = accum + + +def plot_loads(data, plot_name): + """Plot the loads""" + x = data["accum_pseudo_step"] + for ax, load in zip(axes, loads): + ax.plot(x, data[load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Pseudo step") + ax.grid(which="major", color="gray") + + +def plot_residuals(res, plot_name): + """Plot the residuals""" + x = data["accum_pseudo_step"] + for ax, res in zip(axes, residuals): + for res in residuals: + ax.semilogy(x, data[res], label=plot_name + " " + res) + ax.set_ylabel("Residuals") + ax.legend(fontsize="8") + ax.set_title("XV-15 - Quick-Start") + ax.grid(which="major", color="gray") + ax.set_xlabel("Pseudo step") + ax.set_yticks(10.0 ** np.arange(-14, -2)) + ax.set_ylim([1e-9, 1e-2]) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +# calculate and plot loads convergence histories +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "total_forces_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + add_accum_pseudo_step(data) + plot_loads(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) + +for ax in axes: + ax.cla() + + +# plot residual convergence histories +fig, ax = plt.subplots(figsize=(8, 6)) +figures.append(fig) +axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), case_name, "nonlinear_residual_v2.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + add_accum_pseudo_step(data) + plot_residuals(data, case_name) + +figures[0].savefig(os.path.join(dir_path, "Residuals.png"), dpi=500) diff --git a/examples/Quick_Starts/XV15/download_data.py b/examples/Quick_Starts/XV15/download_data.py new file mode 100644 index 0000000..52253b3 --- /dev/null +++ b/examples/Quick_Starts/XV15/download_data.py @@ -0,0 +1,28 @@ +"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" + +import os + +from flow360 import MyCases + +# read in case_name_list +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + # download the files + case.results.download( + nonlinear_residuals=True, + surface_forces=True, + total_forces=True, + destination=case_folder, + ) diff --git a/examples/Quick_Starts/XV15/download_plot_sectional_forces.py b/examples/Quick_Starts/XV15/download_plot_sectional_forces.py new file mode 100644 index 0000000..f0ce56c --- /dev/null +++ b/examples/Quick_Starts/XV15/download_plot_sectional_forces.py @@ -0,0 +1,63 @@ +"""Plot the sectional loads for the XV-15 rotor quick-start case""" + +import os + +import matplotlib.pyplot as plt +import pandas as pd +from flow360 import MyCases + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +loads = ["blade_CFz_per_span", "blade_CFx_per_span"] +figures = [] +axes = [] + + +def plot_sectional_forces(data, plot_name): + """Plot the sectional loads""" + for ax, load in zip(axes, loads): + ax.plot(data["Y"], data["innerRotating/" + load], label=plot_name) + ax.set_ylabel(load) + ax.legend() + ax.set_xlabel("Y") + ax.grid(which="major", color="gray") + ax.set_xlim(0, 4) + if "CFz" in load: + ax.set_ylim(-0.002, 0.01) + + +# read in case_name_list + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in caseNameList + for case in my_cases: + if case.name == case_name: + break + print(case.name) + forces = "results/postprocess/forceDistribution.csv" + # print(case.results) + case._download_file(forces, to_folder=case_folder) + +# set output directory +dir_path = os.path.join(os.getcwd(), "figures") +os.makedirs(dir_path, exist_ok=True) + +# initialize figures & axes +for load in loads: + fig, ax = plt.subplots(figsize=(8, 6)) + figures.append(fig) + axes.append(ax) + +for case_name in case_name_list: + csv_path = os.path.join(os.getcwd(), f"{case_name}", "forceDistribution.csv") + data = pd.read_csv(csv_path, skipinitialspace=True) + plot_sectional_forces(data, case_name) + +for i, load in enumerate(loads): + figures[i].savefig(os.path.join(dir_path, load + ".png"), dpi=500) diff --git a/examples/Quick_Starts/XV15/download_vis_figures.py b/examples/Quick_Starts/XV15/download_vis_figures.py new file mode 100644 index 0000000..b91ed37 --- /dev/null +++ b/examples/Quick_Starts/XV15/download_vis_figures.py @@ -0,0 +1,70 @@ +"""Downloads visualizations of surface CP, CF and streamlines""" + +import os + +from flow360 import MyCases +from PIL import Image + +with open("case_name_list.dat", "r", encoding="utf-8") as file: + case_name_list = file.read().splitlines() + +fields = ["Cp", "Cf", "Fv"] + + +def fetch_png(case, field, res="H"): + """Download the required figures from Flexcompute servers""" + # download the legend i.e. color bar + case_name = case.name + os.makedirs("visualize", exist_ok=True) + + legend = f"visualize/{field}Legend.png" + case._download_file(legend, to_file=legend) + + # list all available theta and phi + view_angles = [(0, 0), (180, 0)] + for theta in [60, 120]: + for phi in range(0, 360, 90): + view_angles.append((theta, phi)) + # download the contour + axis files + for theta, phi in view_angles: + fname = f"{theta:03d}_{phi:03d}.png" + contour = f"visualize/{field}_{res}_{fname}" + axis = f"visualize/Ax_{fname}" + case._download_file(contour, to_file=contour) + case._download_file(axis, to_file=axis) + # load and overlap contour + axis + legend + img_contour = Image.open(contour).convert("RGBA") + img_axis = Image.open(axis) + img_axis = img_axis.resize((img_axis.width * 3, img_axis.height * 3)) + img_axis = img_axis.convert("RGBA") + img_legend = Image.open(legend).convert("RGBA") + background = Image.new("RGBA", img_contour.size, (67, 100, 200)) + img_contour.paste(img_axis, (0, img_contour.height - img_axis.height), img_axis) + img_contour.paste( + img_legend, + (-int(0.1 * img_contour.height), int(0.1 * img_contour.height)), + img_legend, + ) + background.paste(img_contour, (0, 0), img_contour) + background.save( + f"vis_figures/{case_name}_{field}_{res}_{theta:03d}_{phi:03d}_final.png" + ) + + +# set output directory +dir_path = os.path.join(os.getcwd(), "vis_figures") +os.makedirs(dir_path, exist_ok=True) + +case = None +my_cases = MyCases(limit=None) + +for case_name in case_name_list: + case_folder = os.path.join(os.getcwd(), case_name) + os.makedirs(case_folder, exist_ok=True) + # Find the latest case with the name corresponding to the name in case_name_list + for case in my_cases: + if case.name == case_name: + break + print(case.name) + for field in fields: + fetch_png(case, field) diff --git a/examples/Quick_Starts/XV15/submit_case_from_downloads.py b/examples/Quick_Starts/XV15/submit_case_from_downloads.py new file mode 100644 index 0000000..669b06f --- /dev/null +++ b/examples/Quick_Starts/XV15/submit_case_from_downloads.py @@ -0,0 +1,32 @@ +"""Case submission through mesh download and upload onto Flexcompute servers""" + +import os.path +from urllib.request import urlretrieve + +import flow360 as fl + +case_name_list = [] + +# download meshes to the current directory + +URL = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_Hover_ascent_coarse.cgns" +MESH_FILENAME = "XV15_Hover_ascent_coarse.cgns" + +if not os.path.isfile(MESH_FILENAME): + urlretrieve(URL, MESH_FILENAME) + +volume_mesh = fl.VolumeMesh.from_file(MESH_FILENAME, name="XV15_Mesh") +volume_mesh = volume_mesh.submit() + +# submit case +params = fl.Flow360Params("Flow360.json") +case = fl.Case.create("XV15_Quick_Start", params, volume_mesh.id) +case = case.submit() + +case_name_list.append(case.name) + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") diff --git a/examples/Quick_Starts/XV15/submit_case_from_id.py b/examples/Quick_Starts/XV15/submit_case_from_id.py new file mode 100644 index 0000000..a9213fe --- /dev/null +++ b/examples/Quick_Starts/XV15/submit_case_from_id.py @@ -0,0 +1,21 @@ +"""Case submission from mesh ID's present on the Flexcompute servers""" + +import flow360 as fl + +case_name_list = [] + +MESH_ID = "a823b33f-7523-4aa4-9881-99964917cc47" + +# submit case + +params = fl.Flow360Params("Flow360.json") +case = fl.Case.create("XV15_Quick_Start", params, MESH_ID) +case = case.submit() + +case_name_list.append(case.name) + +# dump case name for use in download and post-processing scripts + +with open("case_name_list.dat", "w", encoding="utf-8") as f: + for line in case_name_list: + f.write(f"{line}\n") From 4346e8fc1d65c96efd0ac1c5eecde290c333f414 Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Sun, 10 Dec 2023 22:17:53 +0000 Subject: [PATCH 5/8] Minor changes in code headers --- examples/Quick_Starts/Automated_Meshing/download_data.py | 2 +- examples/Quick_Starts/ONERAM6/download_data.py | 2 +- examples/Quick_Starts/S809/download_data.py | 2 +- examples/Quick_Starts/XV15/download_data.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Quick_Starts/Automated_Meshing/download_data.py b/examples/Quick_Starts/Automated_Meshing/download_data.py index 2429730..d7e4095 100644 --- a/examples/Quick_Starts/Automated_Meshing/download_data.py +++ b/examples/Quick_Starts/Automated_Meshing/download_data.py @@ -1,4 +1,4 @@ -"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" +"""Download the results for the ONERA M6 Automeshing Quick-start""" import os diff --git a/examples/Quick_Starts/ONERAM6/download_data.py b/examples/Quick_Starts/ONERAM6/download_data.py index 2429730..867c03d 100644 --- a/examples/Quick_Starts/ONERAM6/download_data.py +++ b/examples/Quick_Starts/ONERAM6/download_data.py @@ -1,4 +1,4 @@ -"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" +"""Download the results for the ONERA M6 Quick start case""" import os diff --git a/examples/Quick_Starts/S809/download_data.py b/examples/Quick_Starts/S809/download_data.py index 2429730..83c3c19 100644 --- a/examples/Quick_Starts/S809/download_data.py +++ b/examples/Quick_Starts/S809/download_data.py @@ -1,4 +1,4 @@ -"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" +"""Download the results for the S809 Quick start""" import os diff --git a/examples/Quick_Starts/XV15/download_data.py b/examples/Quick_Starts/XV15/download_data.py index 52253b3..550e0b4 100644 --- a/examples/Quick_Starts/XV15/download_data.py +++ b/examples/Quick_Starts/XV15/download_data.py @@ -1,4 +1,4 @@ -"""Download the results for the ramp and adaptive CFL cases at both angles of attack""" +"""Download the results for the XV-15 Quick start""" import os From 2d2781b0e066117f39f39a4f5ecebe6d46fc7518 Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Tue, 9 Jan 2024 15:48:44 +0000 Subject: [PATCH 6/8] Removed old Flow360 json file --- examples/Quick_Starts/XV15/Flow360.json | 21 +++--- examples/Quick_Starts/XV15/Flow360_New.json | 66 ------------------- .../XV15/submit_case_from_downloads.py | 4 +- 3 files changed, 11 insertions(+), 80 deletions(-) delete mode 100644 examples/Quick_Starts/XV15/Flow360_New.json diff --git a/examples/Quick_Starts/XV15/Flow360.json b/examples/Quick_Starts/XV15/Flow360.json index 8f85fab..5377358 100644 --- a/examples/Quick_Starts/XV15/Flow360.json +++ b/examples/Quick_Starts/XV15/Flow360.json @@ -44,20 +44,17 @@ }, "boundaries" : { "farField/farField" : { "type" : "Freestream" }, - "farField/rotationInterface" : { "type" : "SlidingInterface" }, - "innerRotating/rotationInterface" : { "type" : "SlidingInterface" }, "innerRotating/blade" : { "type" : "NoSlipWall" } }, - "slidingInterfaces" : [ - { - "stationaryPatches" : ["farField/rotationInterface"], - "rotatingPatches" : ["innerRotating/rotationInterface"], - "axisOfRotation" : [0,0,-1], - "centerOfRotation" : [0,0,0], - "omega" : 1.84691e-01, - "volumeName" : ["innerRotating"] - } - ], + "volumeZones":{ + "innerRotating":{ + "referenceFrame":{ + "axisOfRotation" : [0,0,-1], + "centerOfRotation" : [0,0,0], + "omegaRadians" : 1.84691e-01 + } + } + }, "timeStepping" : { "timeStepSize" : 2.83500e-01, "physicalSteps" : 600, diff --git a/examples/Quick_Starts/XV15/Flow360_New.json b/examples/Quick_Starts/XV15/Flow360_New.json deleted file mode 100644 index 5377358..0000000 --- a/examples/Quick_Starts/XV15/Flow360_New.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "geometry" : { - "refArea" : 45.604, - "momentCenter" : [0, 0, 0], - "momentLength" : [3.81, 3.81, 3.81] - }, - "volumeOutput" : { - "outputFormat" : "tecplot", - "outputFields": ["primitiveVars", "T","Cp", "Mach", "qcriterion", "VelocityRelative"] - }, - "surfaceOutput" : { - "outputFormat" : "tecplot", - "outputFields": ["primitiveVars", "Cp", "Cf","CfTangent", "CfNormal","nodeForcesPerUnitArea"] - }, - "navierStokesSolver" : { - "absoluteTolerance" : 1e-9, - "relativeTolerance" : 1e-2, - "linearIterations" : 35, - "kappaMUSCL" : -1.0, - "orderOfAccuracy" : 2, - "updateJacobianFrequency" : 4, - "equationEvalFrequency" : 1 - }, - "turbulenceModelSolver" : { - "modelType" : "SpalartAllmaras", - "absoluteTolerance" : 1e-8, - "relativeTolerance" : 1e-2, - "linearIterations" : 25, - "kappaMUSCL" : -1.0, - "DDES" : true, - "orderOfAccuracy" : 2, - "updateJacobianFrequency" : 4, - "equationEvalFrequency" : 1, - "rotationCorrection" : true - }, - "freestream" : - { - "muRef" : 4.29279e-08, - "Mach" : 1.46972e-02, - "MachRef" : 0.70, - "Temperature" : 288.15, - "alphaAngle" : -90.0, - "betaAngle" : 0.0 - }, - "boundaries" : { - "farField/farField" : { "type" : "Freestream" }, - "innerRotating/blade" : { "type" : "NoSlipWall" } - }, - "volumeZones":{ - "innerRotating":{ - "referenceFrame":{ - "axisOfRotation" : [0,0,-1], - "centerOfRotation" : [0,0,0], - "omegaRadians" : 1.84691e-01 - } - } - }, - "timeStepping" : { - "timeStepSize" : 2.83500e-01, - "physicalSteps" : 600, - "maxPseudoSteps" : 35, - "CFL" : { - "type": "adaptive" - } - } -} diff --git a/examples/Quick_Starts/XV15/submit_case_from_downloads.py b/examples/Quick_Starts/XV15/submit_case_from_downloads.py index 669b06f..7319f8e 100644 --- a/examples/Quick_Starts/XV15/submit_case_from_downloads.py +++ b/examples/Quick_Starts/XV15/submit_case_from_downloads.py @@ -9,8 +9,8 @@ # download meshes to the current directory -URL = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_Hover_ascent_coarse.cgns" -MESH_FILENAME = "XV15_Hover_ascent_coarse.cgns" +URL = "https://simcloud-public-1.s3.amazonaws.com/xv15/XV15_Hover_ascent_coarse_v2.cgns" +MESH_FILENAME = "XV15_Hover_ascent_coarse_v2.cgns" if not os.path.isfile(MESH_FILENAME): urlretrieve(URL, MESH_FILENAME) From 1ed0bcd7d84c43a91335012835fc39528f04591f Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Wed, 17 Jan 2024 13:17:29 +0000 Subject: [PATCH 7/8] Changed mesh ID for updated mesh with volumeZones --- examples/Quick_Starts/XV15/submit_case_from_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Quick_Starts/XV15/submit_case_from_id.py b/examples/Quick_Starts/XV15/submit_case_from_id.py index a9213fe..6001102 100644 --- a/examples/Quick_Starts/XV15/submit_case_from_id.py +++ b/examples/Quick_Starts/XV15/submit_case_from_id.py @@ -4,7 +4,7 @@ case_name_list = [] -MESH_ID = "a823b33f-7523-4aa4-9881-99964917cc47" +MESH_ID = "cfd9dbc1-367e-4737-bdb0-ae6b2622dfaf" # submit case From ec2b9c60ee1f41f83e96318b1020348d402f82d6 Mon Sep 17 00:00:00 2001 From: Thomas Fitzgibbon Date: Wed, 17 Jan 2024 14:30:07 +0000 Subject: [PATCH 8/8] Fixed some header titles --- examples/Quick_Starts/Automated_Meshing/convergence_plots.py | 2 +- examples/Quick_Starts/Automated_Meshing/download_data.py | 2 +- .../Automated_Meshing/download_plot_sectional_forces.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Quick_Starts/Automated_Meshing/convergence_plots.py b/examples/Quick_Starts/Automated_Meshing/convergence_plots.py index 9d69a2a..0bf098a 100644 --- a/examples/Quick_Starts/Automated_Meshing/convergence_plots.py +++ b/examples/Quick_Starts/Automated_Meshing/convergence_plots.py @@ -1,4 +1,4 @@ -"""Plot the loads and residuals convergence plots for ONERA M6 quick-start""" +"""Plot the loads and residuals convergence plots for ONERA M6 automeshing quick-start""" import os diff --git a/examples/Quick_Starts/Automated_Meshing/download_data.py b/examples/Quick_Starts/Automated_Meshing/download_data.py index d7e4095..bb46afc 100644 --- a/examples/Quick_Starts/Automated_Meshing/download_data.py +++ b/examples/Quick_Starts/Automated_Meshing/download_data.py @@ -1,4 +1,4 @@ -"""Download the results for the ONERA M6 Automeshing Quick-start""" +"""Download the results for the ONERA M6 automeshing quick-start""" import os diff --git a/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py b/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py index 5b8242d..7c2641b 100644 --- a/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py +++ b/examples/Quick_Starts/Automated_Meshing/download_plot_sectional_forces.py @@ -1,4 +1,4 @@ -"""Plot the sectional loads for the ONERA M6 wing quick-start case""" +"""Plot the sectional loads for the ONERA M6 wing automeshing quick-start case""" import os