Skip to content

Quick_Start_Examples #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The following example demonstrates the use of automated meshing using the ONERA M6 wing.

To run the demo case follow these steps:

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.

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` -> 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.
82 changes: 82 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/convergence_plots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Plot the loads and residuals convergence plots for ONERA M6 automeshing 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)
29 changes: 29 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/download_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Download the results for the ONERA M6 automeshing quick-start"""

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,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Plot the sectional loads for the ONERA M6 wing automeshing 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)
70 changes: 70 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/download_vis_figures.py
Original file line number Diff line number Diff line change
@@ -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)
54 changes: 54 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/om6Case.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
28 changes: 28 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/om6SurfaceMesh.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
49 changes: 49 additions & 0 deletions examples/Quick_Starts/Automated_Meshing/om6VolumeMesh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"refinementFactor": 1.45,
"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
}
}
Loading