From 2ac8cbec90182b23b42aca66027accfc29373a4b Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 12:25:27 -0400 Subject: [PATCH 1/9] modify mesh generator to accept even nx --- .../docs/user_reference/mesh_surface_dict.rst | 2 +- openaerostruct/geometry/utils.py | 75 ++++++++++--------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/openaerostruct/docs/user_reference/mesh_surface_dict.rst b/openaerostruct/docs/user_reference/mesh_surface_dict.rst index 795ad1e91..6ab4c5ca9 100644 --- a/openaerostruct/docs/user_reference/mesh_surface_dict.rst +++ b/openaerostruct/docs/user_reference/mesh_surface_dict.rst @@ -19,7 +19,7 @@ Here is a list of the keys and default values of the ``mesh_dict``, which is use * - num_x - 3 - - - Number of chordwise vertices. Needs to be 2 or an odd number. + - Number of chordwise vertices. * - num_y - 5 - diff --git a/openaerostruct/geometry/utils.py b/openaerostruct/geometry/utils.py index bb747c859..c0f5c9e24 100644 --- a/openaerostruct/geometry/utils.py +++ b/openaerostruct/geometry/utils.py @@ -374,6 +374,7 @@ def gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing=0.0, chord_cos_spa mesh = np.zeros((num_x, num_y, 3)) ny2 = (num_y + 1) // 2 + # --- spanwise discretization --- # Hotfix a special case for spacing bunched at the root and tips if span_cos_spacing == 2.0: beta = np.linspace(0, np.pi, ny2) @@ -395,23 +396,17 @@ def gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing=0.0, chord_cos_spa half_wing = cosine * span_cos_spacing + (1 - span_cos_spacing) * uniform full_wing = np.hstack((-half_wing[:-1], half_wing[::-1])) * span - nx2 = (num_x + 1) // 2 - beta = np.linspace(0, np.pi / 2, nx2) - - # mixed spacing with span_cos_spacing as a weighting factor - # this is for the chordwise spacing - cosine = 0.5 * np.cos(beta) # cosine spacing - uniform = np.linspace(0, 0.5, nx2)[::-1] # uniform spacing - half_wing = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform - full_wing_x = np.hstack((-half_wing[:-1], half_wing[::-1])) * chord - - # Special case if there are only 2 chordwise nodes - if num_x <= 2: - full_wing_x = np.array([0.0, chord]) + # --- chordwise discretization --- + cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 + uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + # mixed spacing with chord_cos_spacing as a weighting factor + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 + wing_x *= chord # apply chord length + # --- form 3D mesh array --- for ind_x in range(num_x): for ind_y in range(num_y): - mesh[ind_x, ind_y, :] = [full_wing_x[ind_x], full_wing[ind_y], 0] + mesh[ind_x, ind_y, :] = [wing_x[ind_x], full_wing[ind_y], 0] return mesh @@ -556,24 +551,13 @@ def add_chordwise_panels(mesh, num_x, chord_cos_spacing): # Obtain mesh and num properties num_y = mesh.shape[1] - nx2 = (num_x + 1) // 2 - - # Create beta, an array of linear sampling points to pi/2 - beta = np.linspace(0, np.pi / 2, nx2) - - # Obtain the two spacings that we will use to blend - cosine = 0.5 * np.cos(beta) # cosine spacing - uniform = np.linspace(0, 0.5, nx2)[::-1] # uniform spacing - - # Create half of the wing in the chordwise direction - half_wing = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform - if chord_cos_spacing == 0.0: - full_wing_x = np.linspace(0, 1.0, num_x) - - else: - # Mirror this half wing into a full wing; offset by 0.5 so it goes 0 to 1 - full_wing_x = np.hstack((-half_wing[:-1], half_wing[::-1])) + 0.5 + # chordwise discretization + cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 + uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + # mixed spacing with chord_cos_spacing as a weighting factor + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 + wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 # Obtain the leading and trailing edges le = mesh[0, :, :] @@ -585,7 +569,7 @@ def add_chordwise_panels(mesh, num_x, chord_cos_spacing): new_mesh[-1, :, :] = te for i in range(1, num_x - 1): - w = full_wing_x[i] + w = wing_x[i] new_mesh[i, :, :] = (1 - w) * le + w * te return new_mesh @@ -634,6 +618,29 @@ def get_default_geo_dict(): def generate_mesh(input_dict): + """ + Generate an OAS mesh + + Parameters + ---------- + input_dict : dict + Dictionary containing user-provided parameters for the surface definition. + See the following for more information: + https://mdolab-openaerostruct.readthedocs-hosted.com/en/latest/user_reference/mesh_surface_dict.html#mesh-dict + + Returns + ------- + mesh : numpy array + Nodal coordinates defining the mesh. + shape = (nx, ny, 3), + where nx is the number of chordwise discretization nodes; + ny is the number of spanwise discretization nodes. + If input_dict["symmetry"] is True, mesh defines left half of wing. + twist : numpy array, optional + Only for CRM wing (input_dict["wing_type"] == "CRM"). + Twist values at the spanwise locations. + """ + # Get defaults and update surface with the user-provided input surf_dict = get_default_geo_dict() surf_dict.update(input_dict) @@ -649,10 +656,6 @@ def generate_mesh(input_dict): if not num_y % 2: raise ValueError("num_y must be an odd number.") - # Check to make sure that an odd number of chordwise points (num_x) was provided - if not num_x % 2 and not num_x == 2: - raise ValueError("num_x must be an odd number.") - # Generate rectangular mesh if surf_dict["wing_type"] == "rect": mesh = gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing, chord_cos_spacing) From 02eaaf940c14cdebdf1df709e1dd9deff6b3f60f Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 12:47:22 -0400 Subject: [PATCH 2/9] fixed ref of chordwise coordinate for rectangle wings --- openaerostruct/geometry/utils.py | 1 + openaerostruct/tests/test_multiple_rect.py | 2 +- openaerostruct/tests/test_scaneagle.py | 2 +- openaerostruct/tests/test_struct_point_masses.py | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/openaerostruct/geometry/utils.py b/openaerostruct/geometry/utils.py index c0f5c9e24..1c567c70b 100644 --- a/openaerostruct/geometry/utils.py +++ b/openaerostruct/geometry/utils.py @@ -401,6 +401,7 @@ def gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing=0.0, chord_cos_spa uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing # mixed spacing with chord_cos_spacing as a weighting factor wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 + wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 (leading edge = 0, trailing edge = 1) wing_x *= chord # apply chord length # --- form 3D mesh array --- diff --git a/openaerostruct/tests/test_multiple_rect.py b/openaerostruct/tests/test_multiple_rect.py index 02e3aa072..20e51a3fd 100644 --- a/openaerostruct/tests/test_multiple_rect.py +++ b/openaerostruct/tests/test_multiple_rect.py @@ -44,7 +44,7 @@ def test(self): } # Create a dictionary to store options about the surface - mesh_dict = {"num_y": 5, "num_x": 3, "wing_type": "rect", "symmetry": True, "offset": np.array([50, 0.0, 0.0])} + mesh_dict = {"num_y": 5, "num_x": 3, "wing_type": "rect", "symmetry": True, "offset": np.array([49.5, 0.0, 0.0])} mesh = generate_mesh(mesh_dict) diff --git a/openaerostruct/tests/test_scaneagle.py b/openaerostruct/tests/test_scaneagle.py index 5cf43b454..72d65e04b 100644 --- a/openaerostruct/tests/test_scaneagle.py +++ b/openaerostruct/tests/test_scaneagle.py @@ -115,7 +115,7 @@ def test(self): indep_var_comp.add_output("W0", val=10.0, units="kg") indep_var_comp.add_output("speed_of_sound", val=322.2, units="m/s") indep_var_comp.add_output("load_factor", val=1.0) - indep_var_comp.add_output("empty_cg", val=np.array([0.2, 0.0, 0.0]), units="m") + indep_var_comp.add_output("empty_cg", val=np.array([0.35, 0.0, 0.0]), units="m") prob.model.add_subsystem("prob_vars", indep_var_comp, promotes=["*"]) diff --git a/openaerostruct/tests/test_struct_point_masses.py b/openaerostruct/tests/test_struct_point_masses.py index b5ef926ae..7e138165a 100644 --- a/openaerostruct/tests/test_struct_point_masses.py +++ b/openaerostruct/tests/test_struct_point_masses.py @@ -11,7 +11,7 @@ class Test(unittest.TestCase): def test(self): # Create a dictionary to store options about the surface - mesh_dict = {"num_y": 31, "wing_type": "rect", "span": 10, "symmetry": True} + mesh_dict = {"num_y": 31, "num_x": 3, "wing_type": "rect", "span": 10, "symmetry": True} mesh = generate_mesh(mesh_dict) @@ -48,7 +48,7 @@ def test(self): point_masses = np.array([[10.0]]) - point_mass_locations = np.array([[1.0, -10.0, 0.0]]) + point_mass_locations = np.array([[1.5, -10.0, 0.0]]) indep_var_comp.add_output("point_masses", val=point_masses, units="kg") indep_var_comp.add_output("point_mass_locations", val=point_mass_locations, units="m") @@ -69,7 +69,7 @@ def test(self): def test_multiple_masses(self): # Create a dictionary to store options about the surface - mesh_dict = {"num_y": 31, "wing_type": "rect", "span": 10, "symmetry": True} + mesh_dict = {"num_y": 31, "num_x": 3, "wing_type": "rect", "span": 10, "symmetry": True} mesh = generate_mesh(mesh_dict) @@ -106,7 +106,7 @@ def test_multiple_masses(self): point_masses = np.array([[10.0, 20.0]]) - point_mass_locations = np.array([[1.0, -1.0, 0.0], [1.0, -2.0, 0.0]]) + point_mass_locations = np.array([[1.5, -1.0, 0.0], [1.5, -2.0, 0.0]]) indep_var_comp.add_output("point_masses", val=point_masses, units="kg") indep_var_comp.add_output("point_mass_locations", val=point_mass_locations, units="m") From 52a13336105341557a7852b7f3dcff4d8608c1ec Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 13:10:52 -0400 Subject: [PATCH 3/9] vsp test fix --- openaerostruct/geometry/tests/test_vsp_mesh.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openaerostruct/geometry/tests/test_vsp_mesh.py b/openaerostruct/geometry/tests/test_vsp_mesh.py index 014bd0f54..0b00cd412 100644 --- a/openaerostruct/geometry/tests/test_vsp_mesh.py +++ b/openaerostruct/geometry/tests/test_vsp_mesh.py @@ -27,6 +27,7 @@ def test_full(self): "span": 10.0, "root_chord": 1, "span_cos_spacing": 0.0, + "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 } oas_mesh = generate_mesh(mesh_dict) @@ -46,6 +47,7 @@ def test_symm(self): "span": 10.0, "root_chord": 1, "span_cos_spacing": 0.0, + "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 } oas_mesh = generate_mesh(mesh_dict) From d628f4499068e1ce5eb75e0c143f8faf0aa0fdb9 Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 13:16:29 -0400 Subject: [PATCH 4/9] formatting --- openaerostruct/geometry/utils.py | 15 ++++++++------- openaerostruct/tests/test_multiple_rect.py | 8 +++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/openaerostruct/geometry/utils.py b/openaerostruct/geometry/utils.py index 569a3c575..04f81c4d6 100644 --- a/openaerostruct/geometry/utils.py +++ b/openaerostruct/geometry/utils.py @@ -398,12 +398,12 @@ def gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing=0.0, chord_cos_spa full_wing = np.hstack((-half_wing[:-1], half_wing[::-1])) * span # --- chordwise discretization --- - cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 - uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 + uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing # mixed spacing with chord_cos_spacing as a weighting factor - wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 (leading edge = 0, trailing edge = 1) - wing_x *= chord # apply chord length + wing_x *= chord # apply chord length # --- form 3D mesh array --- for ind_x in range(num_x): @@ -555,10 +555,10 @@ def add_chordwise_panels(mesh, num_x, chord_cos_spacing): num_y = mesh.shape[1] # chordwise discretization - cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 - uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 + uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing # mixed spacing with chord_cos_spacing as a weighting factor - wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 # Obtain the leading and trailing edges @@ -641,6 +641,7 @@ def generate_mesh(input_dict): twist : numpy array, optional Only for CRM wing (input_dict["wing_type"] == "CRM"). Twist values at the spanwise locations. + """ # Get defaults and update surface with the user-provided input diff --git a/openaerostruct/tests/test_multiple_rect.py b/openaerostruct/tests/test_multiple_rect.py index 20e51a3fd..a6e4e3dde 100644 --- a/openaerostruct/tests/test_multiple_rect.py +++ b/openaerostruct/tests/test_multiple_rect.py @@ -44,7 +44,13 @@ def test(self): } # Create a dictionary to store options about the surface - mesh_dict = {"num_y": 5, "num_x": 3, "wing_type": "rect", "symmetry": True, "offset": np.array([49.5, 0.0, 0.0])} + mesh_dict = { + "num_y": 5, + "num_x": 3, + "wing_type": "rect", + "symmetry": True, + "offset": np.array([49.5, 0.0, 0.0]), + } mesh = generate_mesh(mesh_dict) From 307b66e7e04f697577cc55539861a67a594b19b4 Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 13:51:48 -0400 Subject: [PATCH 5/9] formatting --- openaerostruct/geometry/tests/test_vsp_mesh.py | 4 ++-- openaerostruct/geometry/utils.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openaerostruct/geometry/tests/test_vsp_mesh.py b/openaerostruct/geometry/tests/test_vsp_mesh.py index 0b00cd412..24c9784da 100644 --- a/openaerostruct/geometry/tests/test_vsp_mesh.py +++ b/openaerostruct/geometry/tests/test_vsp_mesh.py @@ -27,7 +27,7 @@ def test_full(self): "span": 10.0, "root_chord": 1, "span_cos_spacing": 0.0, - "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 + "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 } oas_mesh = generate_mesh(mesh_dict) @@ -47,7 +47,7 @@ def test_symm(self): "span": 10.0, "root_chord": 1, "span_cos_spacing": 0.0, - "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 + "offset": [-0.5, 0, 0], # chordwise coordinate goes from -0.5 to 0.5 } oas_mesh = generate_mesh(mesh_dict) diff --git a/openaerostruct/geometry/utils.py b/openaerostruct/geometry/utils.py index 04f81c4d6..aa1497eef 100644 --- a/openaerostruct/geometry/utils.py +++ b/openaerostruct/geometry/utils.py @@ -621,7 +621,7 @@ def get_default_geo_dict(): def generate_mesh(input_dict): """ - Generate an OAS mesh + Generate an OAS mesh. Parameters ---------- @@ -635,8 +635,7 @@ def generate_mesh(input_dict): mesh : numpy array Nodal coordinates defining the mesh. shape = (nx, ny, 3), - where nx is the number of chordwise discretization nodes; - ny is the number of spanwise discretization nodes. + where nx is the number of chordwise discretization nodes, ny is the number of spanwise discretization nodes. If input_dict["symmetry"] is True, mesh defines left half of wing. twist : numpy array, optional Only for CRM wing (input_dict["wing_type"] == "CRM"). From 3a2729afc4e6c77d1025632ebfa7da9dad2e30ff Mon Sep 17 00:00:00 2001 From: kanekosh Date: Mon, 3 Jun 2024 14:17:26 -0400 Subject: [PATCH 6/9] minor edits to GHA yml --- .github/workflows/oas.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/oas.yml b/.github/workflows/oas.yml index c11171576..32647afd8 100644 --- a/.github/workflows/oas.yml +++ b/.github/workflows/oas.yml @@ -20,6 +20,7 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false # continue other jobs even if one of the jobs in matrix fails matrix: dep-versions: ["oldest", "latest"] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set versions to test here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 5f721a425e3fc400740a761ee85366299c13880f Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Tue, 4 Jun 2024 11:42:07 -0400 Subject: [PATCH 7/9] Use newer codecov version in GHA --- .github/workflows/oas.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/oas.yml b/.github/workflows/oas.yml index 32647afd8..43820afa2 100644 --- a/.github/workflows/oas.yml +++ b/.github/workflows/oas.yml @@ -164,12 +164,13 @@ jobs: coverage xml - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} verbose: true - + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # --- linting and formatting --- black: uses: mdolab/.github/.github/workflows/black.yaml@main From e8d688fd876a71a000552676824227a80d66d69a Mon Sep 17 00:00:00 2001 From: Eytan Adler Date: Tue, 4 Jun 2024 11:49:00 -0400 Subject: [PATCH 8/9] Whack the chunk adaptation --- openaerostruct/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openaerostruct/__init__.py b/openaerostruct/__init__.py index 7a38ae062..7f9085047 100644 --- a/openaerostruct/__init__.py +++ b/openaerostruct/__init__.py @@ -1 +1 @@ -__version__ = "2.7.1" +__version__ = "2.7.2" From 29fe136106846c0cc62d9fb928b47cd4fe4150ba Mon Sep 17 00:00:00 2001 From: kanekosh Date: Tue, 4 Jun 2024 13:13:57 -0400 Subject: [PATCH 9/9] clean up --- openaerostruct/geometry/utils.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/openaerostruct/geometry/utils.py b/openaerostruct/geometry/utils.py index aa1497eef..643c02d83 100644 --- a/openaerostruct/geometry/utils.py +++ b/openaerostruct/geometry/utils.py @@ -398,11 +398,10 @@ def gen_rect_mesh(num_x, num_y, span, chord, span_cos_spacing=0.0, chord_cos_spa full_wing = np.hstack((-half_wing[:-1], half_wing[::-1])) * span # --- chordwise discretization --- - cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 - uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + cosine = 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from 0 to 1 + uniform = np.linspace(0, 1, num_x) # uniform spacing # mixed spacing with chord_cos_spacing as a weighting factor - wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 - wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 (leading edge = 0, trailing edge = 1) + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform wing_x *= chord # apply chord length # --- form 3D mesh array --- @@ -555,11 +554,10 @@ def add_chordwise_panels(mesh, num_x, chord_cos_spacing): num_y = mesh.shape[1] # chordwise discretization - cosine = -0.5 + 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from -0.5 to 0.5 - uniform = np.linspace(-0.5, 0.5, num_x) # uniform spacing + cosine = 0.5 * (1 - np.cos(np.linspace(0, np.pi, num_x))) # cosine spacing from 0 to 1 + uniform = np.linspace(0, 1, num_x) # uniform spacing # mixed spacing with chord_cos_spacing as a weighting factor - wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # from -0.5 to 0.5 - wing_x += 0.5 # offset by 0.5 so it goes 0 to 1 + wing_x = cosine * chord_cos_spacing + (1 - chord_cos_spacing) * uniform # Obtain the leading and trailing edges le = mesh[0, :, :]