├── .git-blame-ignore-revs ├── .github ├── CODEOWNERS ├── azure-pipelines.yaml ├── build_real.sh └── test_real.sh ├── .gitignore ├── .readthedocs.yaml ├── .zenodo.json ├── CITATION.cff ├── LICENSE ├── README.md ├── doc ├── API.rst ├── DVConstraints.rst ├── DVGeometry.rst ├── DVGeometryCST.rst ├── DVGeometryESP.rst ├── DVGeometryMulti.rst ├── DVGeometryVSP.rst ├── Makefile ├── advanced_ffd.rst ├── citation.rst ├── conf.py ├── contribute.rst ├── cst_tutorial.rst ├── esp_airfoil.rst ├── geo_utils.rst ├── getting_started.rst ├── images │ ├── DPW4_FFD-27745.gif │ ├── c172.jpg │ ├── wing.png │ └── wingNew.png ├── index.rst ├── install.rst ├── introduction.rst ├── make.bat ├── mphys_dvgeo.rst ├── pyGeo.rst ├── ref.bib ├── requirements.txt ├── tutorial.rst └── update_pygeo.rst ├── examples ├── bwb │ ├── bwb.py │ ├── bwb_front_low.out │ ├── bwb_front_up.out │ ├── bwb_le.out │ ├── bwb_le_spar.out │ ├── bwb_rib_list.out │ ├── bwb_te.out │ ├── bwb_te_spar.out │ ├── digitizing │ │ ├── FS-1997-07-24-LaRC.pdf │ │ ├── Screenshot.png │ │ ├── bwb_layout.dig │ │ ├── bwb_layout.png │ │ └── bwb_output.dig │ └── naca0012.dat ├── c172_wing │ ├── baseline_wing.stl │ ├── c172.py │ ├── genFFD.py │ ├── images │ │ ├── all_3d.png │ │ ├── baseline_3d.png │ │ ├── local_3d.png │ │ ├── refaxis.png │ │ ├── sweep_3d.png │ │ └── twist_3d.png │ ├── naca2412.dat │ ├── runFFDExample.py │ └── tecplot_layouts │ │ ├── README.md │ │ ├── all.lay │ │ ├── baseline.lay │ │ ├── local.lay │ │ ├── refaxis.lay │ │ ├── sweep.lay │ │ └── twist.lay ├── cst │ ├── CSTTutorial.py │ ├── naca2412.dat │ ├── original_points.svg │ ├── perturbed_class_chord.svg │ └── perturbed_coeff.svg ├── deform_geometry │ ├── ffd │ │ └── simple_ffd.py │ ├── geo │ │ ├── generate_wing.py │ │ └── rae2822.dat │ └── runScript.py ├── esp_airfoil │ ├── esp_airfoil.py │ ├── esp_airfoil.stl │ ├── images │ │ ├── new_airfoil.png │ │ └── orig_airfoil.png │ ├── modified_example.csm │ └── naca0012.csm ├── ffd │ ├── generateBWBFFD.py │ ├── generateFFD.py │ ├── generatec172FFD.py │ └── generaterectFFD.py └── ffd_cylinder │ ├── cylinder.csm │ ├── cylinder.stl │ ├── genFFD.py │ ├── images │ ├── cyl_embedded_deformed.png │ ├── cyl_embedded_undeformed.png │ ├── cylinder_ffd_3d.png │ ├── cylinder_only_3d.png │ └── deformed_cylinder.png │ ├── runFFDExample.py │ └── tecplot_layouts │ ├── README.md │ ├── cyl_embedded_deformed.lay │ ├── cyl_embedded_undeformed.lay │ ├── cyl_only.lay │ └── ffd_demo.lay ├── input_files ├── e63.dat ├── get-input-files.sh ├── naca0012.dat ├── naca0012_clockwise.dat ├── naca0012_closed.dat ├── naca0012_sharp.dat ├── naca0012_zeroLE.dat └── naca2412.dat ├── paper ├── Global-FFD-DV-Demo.py ├── child_ffd.pdf ├── constraints_3d.pdf ├── cst_example.pdf ├── cst_example.py ├── esp_example.png ├── ffd.xyz ├── ffd_dvs.pdf ├── ffd_multi.png ├── paper.bib ├── paper.md ├── trisurfcon.pdf └── vsp_example.png ├── pygeo ├── __init__.py ├── constraints │ ├── DVCon.py │ ├── __init__.py │ ├── areaConstraint.py │ ├── baseConstraint.py │ ├── circularityConstraint.py │ ├── colinearityConstraint.py │ ├── curvatureConstraint.py │ ├── gearPostConstraint.py │ ├── locationConstraint.py │ ├── planarityConstraint.py │ ├── radiusConstraint.py │ ├── thicknessConstraint.py │ └── volumeConstraint.py ├── geo_utils │ ├── __init__.py │ ├── bilinear_map.py │ ├── dcel.py │ ├── ffd_generation.py │ ├── file_io.py │ ├── index_position.py │ ├── knotvector.py │ ├── misc.py │ ├── node_edge_face.py │ ├── norm.py │ ├── orientation.py │ ├── pointselect.py │ ├── polygon.py │ ├── projection.py │ ├── remove_duplicates.py │ ├── rotation.py │ └── split_quad.py ├── mphys │ ├── __init__.py │ └── mphys_dvgeo.py ├── parameterization │ ├── BaseDVGeo.py │ ├── DVGeo.py │ ├── DVGeoAxi.py │ ├── DVGeoCST.py │ ├── DVGeoESP.py │ ├── DVGeoMulti.py │ ├── DVGeoSketch.py │ ├── DVGeoVSP.py │ ├── __init__.py │ └── designVars.py ├── pyBlock.py ├── pyGeo.py ├── pyNetwork.py └── topology.py ├── setup.py └── tests ├── README.md ├── reg_tests ├── commonUtils.py ├── ref │ ├── test_Blocks_01.ref │ ├── test_Blocks_02.ref │ ├── test_Blocks_03.ref │ ├── test_Blocks_04.ref │ ├── test_Blocks_05.ref │ ├── test_Cylinder_01.ref │ ├── test_Cylinder_spanwise_dvs.ref │ ├── test_DVConstraints_LERadius.ref │ ├── test_DVConstraints_LeTe.ref │ ├── test_DVConstraints_circularity.ref │ ├── test_DVConstraints_colinearity.ref │ ├── test_DVConstraints_compositeVolume_box.ref │ ├── test_DVConstraints_curvature.ref │ ├── test_DVConstraints_curvature1D.ref │ ├── test_DVConstraints_linearConstraintShape.ref │ ├── test_DVConstraints_location_box.ref │ ├── test_DVConstraints_monotonic.ref │ ├── test_DVConstraints_planarity_box.ref │ ├── test_DVConstraints_planarity_tri.ref │ ├── test_DVConstraints_projectedArea.ref │ ├── test_DVConstraints_projectedArea_box.ref │ ├── test_DVConstraints_projectedArea_box_sens.ref │ ├── test_DVConstraints_projected_thickness1D_box.ref │ ├── test_DVConstraints_projected_thickness2D_box.ref │ ├── test_DVConstraints_surfaceArea.ref │ ├── test_DVConstraints_surfaceArea_box.ref │ ├── test_DVConstraints_thickness1D.ref │ ├── test_DVConstraints_thickness1D_box.ref │ ├── test_DVConstraints_thickness2D.ref │ ├── test_DVConstraints_thickness2D_box.ref │ ├── test_DVConstraints_thicknessToChord.ref │ ├── test_DVConstraints_triangulatedSurface.ref │ ├── test_DVConstraints_triangulatedSurface_intersected.ref │ ├── test_DVConstraints_triangulatedSurface_intersected_2DVGeos.ref │ ├── test_DVConstraints_triangulatedVolume_box.ref │ ├── test_DVConstraints_triangulatedVolume_bwb.ref │ ├── test_DVConstraints_volume.ref │ ├── test_DVConstraints_volume_box.ref │ ├── test_DVGeometryCST_e63.ref │ ├── test_DVGeometryCST_naca0012.ref │ ├── test_DVGeometryCST_naca0012_sharp.ref │ ├── test_DVGeometryCST_naca0012_zeroLE.ref │ ├── test_DVGeometryCST_naca2412.ref │ ├── test_DVGeometryESP_01.ref │ ├── test_DVGeometryESP_02.ref │ ├── test_DVGeometryMulti.ref │ ├── test_DVGeometryVSP_01.ref │ ├── test_DVGeometryVSP_02.ref │ ├── test_DVGeometryVSP_03.ref │ ├── test_DVGeometry_01.ref │ ├── test_DVGeometry_02.ref │ ├── test_DVGeometry_03.ref │ ├── test_DVGeometry_04.ref │ ├── test_DVGeometry_05.ref │ ├── test_DVGeometry_06.ref │ ├── test_DVGeometry_07.ref │ ├── test_DVGeometry_08.ref │ ├── test_DVGeometry_09.ref │ ├── test_DVGeometry_10.ref │ ├── test_DVGeometry_11.ref │ ├── test_DVGeometry_12.ref │ ├── test_DVGeometry_13.ref │ ├── test_DVGeometry_14.ref │ ├── test_DVGeometry_15.ref │ ├── test_DVGeometry_16.ref │ ├── test_DVGeometry_17.ref │ ├── test_DVGeometry_18.ref │ ├── test_DVGeometry_19.ref │ ├── test_DVGeometry_20.ref │ ├── test_DVGeometry_21.ref │ ├── test_DVGeometry_23.ref │ ├── test_DVGeometry_24.ref │ ├── test_DVGeometry_25.ref │ ├── test_active_children.ref │ ├── test_custom_ray_projections.ref │ ├── test_ffd_spline_order.ref │ ├── test_proximity_constraints.ref │ ├── test_pyGeo.ref │ ├── test_shape_function_dv.ref │ └── test_vol_bounds.ref ├── test_Blocks.py ├── test_Cylinder.py ├── test_DVConstraints.py ├── test_DVGeometry.py ├── test_DVGeometryCST.py ├── test_DVGeometryESP.py ├── test_DVGeometryMulti.py ├── test_DVGeometryVSP.py ├── test_MPhysGeo.py ├── test_examples.py ├── test_ffdGeneration.py ├── test_pyGeo.py └── warning_childFFD.py └── unit_tests └── test_import_guards.py /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 8ebf48deefbee52560197721d02d7f2cc8408c15 2 | b192604451ac9427ddccfc2ca24a6796a22e4cc1 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @mdolab/pygeo_maintainers 2 | -------------------------------------------------------------------------------- /.github/azure-pipelines.yaml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - main 3 | 4 | pr: 5 | - main 6 | 7 | resources: 8 | repositories: 9 | - repository: azure_template 10 | type: github 11 | name: mdolab/.github 12 | endpoint: mdolab 13 | 14 | extends: 15 | template: azure/azure_template.yaml@azure_template 16 | parameters: 17 | REPO_NAME: pygeo 18 | ISORT: true 19 | COVERAGE: true 20 | -------------------------------------------------------------------------------- /.github/build_real.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | pip install .[testing] 4 | -------------------------------------------------------------------------------- /.github/test_real.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | ./input_files/get-input-files.sh 4 | 5 | # No tests should be skipped on GCC, non Intel MPI, and x86 arch 6 | if [[ $COMPILERS == "gcc" ]] && [[ -z $I_MPI_ROOT ]] && [[ "$(arch)" == "x86_64" ]]; then 7 | EXTRA_FLAGS='--disallow_skipped' 8 | fi 9 | 10 | # Set OpenMPI env variables only on non-Intel MPI 11 | if [[ -z $I_MPI_ROOT ]]; then 12 | # Set these to allow MPI oversubscription because the tests need to run on specific number of procs but the test runner may have fewer 13 | export OMPI_MCA_rmaps_base_oversubscribe=1 # This works for OpenMPI <= 4 14 | export PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe # This works from OpenMPI >= 5 15 | fi 16 | 17 | cd tests 18 | testflo -v -n 1 --coverage --coverpkg pygeo $EXTRA_FLAGS 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | pygeo.egg-info 4 | reg_tests/pygeo_reg 5 | reg_tests/pygeo_reg.orig 6 | doc/_build 7 | *testflo_report.out 8 | input_files 9 | .isort.cfg 10 | *.vscode 11 | tests/reg_tests/reports/ 12 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | version: 2 6 | 7 | build: 8 | os: ubuntu-22.04 9 | tools: 10 | python: "3.11" 11 | 12 | sphinx: 13 | configuration: doc/conf.py 14 | 15 | python: 16 | install: 17 | - requirements: doc/requirements.txt 18 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "creators": [ 3 | { 4 | "name": "Hannah M. Hajdik" 5 | }, 6 | { 7 | "name": "Anil Yildirim" 8 | }, 9 | { 10 | "name": "Ella Wu" 11 | }, 12 | { 13 | "name": "Benjamin J. Brelje" 14 | }, 15 | { 16 | "name": "Sabet Seraj" 17 | }, 18 | { 19 | "name": "Marco Mangano" 20 | }, 21 | { 22 | "name": "Joshua L. Anibal" 23 | }, 24 | { 25 | "name": "Eirikur Jonsson" 26 | }, 27 | { 28 | "name": "Eytan J. Adler" 29 | }, 30 | { 31 | "name": "Charles A. Mader" 32 | }, 33 | { 34 | "name": "Gaetan K. W. Kenway" 35 | }, 36 | { 37 | "name": "Joaquim R. R. A. Martins" 38 | } 39 | ], 40 | "title": "pyGeo: A geometry package for multidisciplinary design optimization" 41 | } -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: "1.2.0" 2 | authors: 3 | - family-names: Hajdik 4 | given-names: Hannah M. 5 | orcid: "https://orcid.org/0000-0002-5103-7159" 6 | - family-names: Yildirim 7 | given-names: Anil 8 | orcid: "https://orcid.org/0000-0002-1814-9191" 9 | - family-names: Wu 10 | given-names: Ella 11 | orcid: "https://orcid.org/0000-0001-8856-9661" 12 | - family-names: Brelje 13 | given-names: Benjamin J. 14 | orcid: "https://orcid.org/0000-0002-9819-3028" 15 | - family-names: Seraj 16 | given-names: Sabet 17 | orcid: "https://orcid.org/0000-0002-7364-0071" 18 | - family-names: Mangano 19 | given-names: Marco 20 | orcid: "https://orcid.org/0000-0001-8495-3578" 21 | - family-names: Anibal 22 | given-names: Joshua L. 23 | orcid: "https://orcid.org/0000-0002-7795-2523" 24 | - family-names: Jonsson 25 | given-names: Eirikur 26 | orcid: "https://orcid.org/0000-0002-5166-3889" 27 | - family-names: Adler 28 | given-names: Eytan J. 29 | orcid: "https://orcid.org/0000-0002-8716-1805" 30 | - family-names: Mader 31 | given-names: Charles A. 32 | orcid: "https://orcid.org/0000-0002-2744-1151" 33 | - family-names: Kenway 34 | given-names: Gaetan K. W. 35 | orcid: "https://orcid.org/0000-0003-1352-5458" 36 | - family-names: Martins 37 | given-names: Joaquim R. R. A. 38 | orcid: "https://orcid.org/0000-0003-2143-1478" 39 | doi: 10.5281/zenodo.8027706 40 | message: If you use this software, please cite our article in the 41 | Journal of Open Source Software. 42 | preferred-citation: 43 | authors: 44 | - family-names: Hajdik 45 | given-names: Hannah M. 46 | orcid: "https://orcid.org/0000-0002-5103-7159" 47 | - family-names: Yildirim 48 | given-names: Anil 49 | orcid: "https://orcid.org/0000-0002-1814-9191" 50 | - family-names: Wu 51 | given-names: Ella 52 | orcid: "https://orcid.org/0000-0001-8856-9661" 53 | - family-names: Brelje 54 | given-names: Benjamin J. 55 | orcid: "https://orcid.org/0000-0002-9819-3028" 56 | - family-names: Seraj 57 | given-names: Sabet 58 | orcid: "https://orcid.org/0000-0002-7364-0071" 59 | - family-names: Mangano 60 | given-names: Marco 61 | orcid: "https://orcid.org/0000-0001-8495-3578" 62 | - family-names: Anibal 63 | given-names: Joshua L. 64 | orcid: "https://orcid.org/0000-0002-7795-2523" 65 | - family-names: Jonsson 66 | given-names: Eirikur 67 | orcid: "https://orcid.org/0000-0002-5166-3889" 68 | - family-names: Adler 69 | given-names: Eytan J. 70 | orcid: "https://orcid.org/0000-0002-8716-1805" 71 | - family-names: Mader 72 | given-names: Charles A. 73 | orcid: "https://orcid.org/0000-0002-2744-1151" 74 | - family-names: Kenway 75 | given-names: Gaetan K. W. 76 | orcid: "https://orcid.org/0000-0003-1352-5458" 77 | - family-names: Martins 78 | given-names: Joaquim R. R. A. 79 | orcid: "https://orcid.org/0000-0003-2143-1478" 80 | date-published: 2023-07-19 81 | doi: 10.21105/joss.05319 82 | issn: 2475-9066 83 | issue: 87 84 | journal: Journal of Open Source Software 85 | publisher: 86 | name: Open Journals 87 | start: 5319 88 | title: "pyGeo: A geometry package for multidisciplinary design 89 | optimization" 90 | type: article 91 | url: "https://joss.theoj.org/papers/10.21105/joss.05319" 92 | volume: 8 93 | title: "pyGeo: A geometry package for multidisciplinary design 94 | optimization" 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyGeo 2 | [![Build Status](https://dev.azure.com/mdolab/Public/_apis/build/status/mdolab.pygeo?branchName=main)](https://dev.azure.com/mdolab/Public/_build/latest?definitionId=17&branchName=main) 3 | [![Documentation Status](https://readthedocs.com/projects/mdolab-pygeo/badge/?version=latest)](https://mdolab-pygeo.readthedocs-hosted.com/en/latest/?badge=latest) 4 | [![codecov](https://codecov.io/gh/mdolab/pygeo/branch/main/graph/badge.svg?token=N2L58WGCDI)](https://codecov.io/gh/mdolab/pygeo) 5 | [![DOI](https://joss.theoj.org/papers/10.21105/joss.05319/status.svg)](https://doi.org/10.21105/joss.05319) 6 | 7 | pyGeo is an object oriented geometry manipulation framework for multidisciplinary design optimization (MDO). 8 | It can be used for MDO within the [MACH framework](https://github.com/mdolab/MACH-Aero) and within [OpenMDAO](https://github.com/OpenMDAO/OpenMDAO) through [MPhys](https://github.com/OpenMDAO/mphys). 9 | Its parameterization options include a free form deformation (FFD) implementation, an interface to NASA's [OpenVSP](https://openvsp.org/) parametric geometry tool, and an interface to the CAD package [ESP](https://acdl.mit.edu/ESP/). 10 | It also includes geometric constraints and utility functions for geometry manipulation. 11 | 12 | ![](doc/images/DPW4_FFD-27745.gif) 13 | 14 | ## Documentation 15 | Please see the [documentation](https://mdolab-pygeo.readthedocs-hosted.com/en/latest/) for installation details and API documentation. 16 | 17 | To locally build the documentation, enter the `doc` folder and enter `make html` in terminal. 18 | You can then view the built documentation in the `_build` folder. 19 | 20 | 21 | ## Citation 22 | Please cite pyGeo in any publication for which you find it useful. 23 | Citation information can be found [here](https://mdolab-pygeo.readthedocs-hosted.com/en/latest/citation.html). 24 | 25 | 26 | ## License 27 | pyGeo is licensed under the Apache License, Version 2.0 (the "License"). See `LICENSE` for the full license. 28 | 29 | ## Copyright 30 | Copyright (c) 2012 University of Toronto 31 | Copyright (c) 2014 University of Michigan 32 | Additional copyright (c) 2014 Gaetan K. W. Kenway, Charles A. Mader, and Joaquim R. R. A. Martins 33 | All rights reserved. 34 | -------------------------------------------------------------------------------- /doc/API.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | pyGeo API 4 | ========= 5 | 6 | This package consists of the following modules: 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | DVConstraints 12 | mphys_dvgeo 13 | DVGeometry 14 | DVGeometryMulti 15 | DVGeometryESP 16 | DVGeometryVSP 17 | DVGeometryCST 18 | pyGeo 19 | geo_utils 20 | -------------------------------------------------------------------------------- /doc/DVConstraints.rst: -------------------------------------------------------------------------------- 1 | .. _DVConstraints: 2 | 3 | DVConstraints 4 | ------------- 5 | 6 | .. autoclass:: pygeo.constraints.DVCon.DVConstraints 7 | :members: 8 | -------------------------------------------------------------------------------- /doc/DVGeometry.rst: -------------------------------------------------------------------------------- 1 | .. _DVGeometry: 2 | 3 | DVGeometry 4 | ---------- 5 | .. autoclass:: pygeo.parameterization.DVGeo.DVGeometry 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/DVGeometryCST.rst: -------------------------------------------------------------------------------- 1 | .. _DVGeometryCST: 2 | 3 | DVGeometryCST 4 | ------------- 5 | 6 | .. autoclass:: pygeo.parameterization.DVGeoCST.DVGeometryCST 7 | :members: 8 | -------------------------------------------------------------------------------- /doc/DVGeometryESP.rst: -------------------------------------------------------------------------------- 1 | .. _DVGeometryESP: 2 | 3 | DVGeometryESP 4 | ------------- 5 | .. autoclass:: pygeo.parameterization.DVGeoESP.DVGeometryESP 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/DVGeometryMulti.rst: -------------------------------------------------------------------------------- 1 | .. _DVGeometryMulti: 2 | 3 | DVGeometryMulti 4 | --------------- 5 | .. autoclass:: pygeo.parameterization.DVGeoMulti.DVGeometryMulti 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/DVGeometryVSP.rst: -------------------------------------------------------------------------------- 1 | .. _DVGeometryVSP: 2 | 3 | DVGeometryVSP 4 | ------------- 5 | .. autoclass:: pygeo.parameterization.DVGeoVSP.DVGeometryVSP 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/citation.rst: -------------------------------------------------------------------------------- 1 | .. _citation: 2 | 3 | Citation 4 | ======== 5 | If you use pyGeo, please cite the following paper: 6 | 7 | \H. Hajdik, A. Yildirim, E. Wu, B. Brelje, S. Seraj, M. Mangano, J. Anibal, E. Jonsson, E. Adler, C. A. Mader, G. Kenway, and J. R. R. A. Martins. pyGeo: A geometry package for multidisciplinary design optimization. Journal of Open Source Software, 8(87), 5319, July 2023. https://doi.org/10.21105/joss.05319 8 | 9 | The paper is available online from the Journal of Open Source Software `here `__. 10 | To cite this paper, you can use the following BibTeX entry: 11 | 12 | .. code-block:: bibtex 13 | 14 | @article{Hajdik2023, 15 | doi = {10.21105/joss.05319}, 16 | year = {2023}, 17 | publisher = {The Open Journal}, 18 | volume = {8}, 19 | number = {87}, 20 | pages = {5319}, 21 | author = {Hannah M. Hajdik and Anil Yildirim and Ella Wu and Benjamin J. Brelje and Sabet Seraj and Marco Mangano and Joshua L. Anibal and Eirikur Jonsson and Eytan J. Adler and Charles A. Mader and Gaetan K. W. Kenway and Joaquim R. R. A. Martins}, 22 | title = {{pyGeo}: A geometry package for multidisciplinary design optimization}, 23 | journal = {Journal of Open Source Software} 24 | } 25 | 26 | 27 | For more background and theory on the FFD implementation, see `this paper `__. 28 | 29 | \G. K. W. Kenway, G. J. Kennedy, and J. R. R. A. Martins, “A CAD-Free Approach to High-Fidelity Aerostructural Optimization”, in Proceedings of the 13th AIAA/ISSMO Multidisciplinary Analysis Optimization Conference, Fort Worth, TX, 2010. 30 | 31 | 32 | .. code-block:: bibtex 33 | 34 | @conference {Kenway2010, 35 | title = {A {CAD}-Free Approach to High-Fidelity Aerostructural Optimization}, 36 | booktitle = {Proceedings of the 13th AIAA/ISSMO Multidisciplinary Analysis Optimization Conference}, 37 | year = {2010}, 38 | note = {AIAA 2010-9231}, 39 | month = {September}, 40 | address = {Fort Worth,~TX}, 41 | author = {Gaetan K. W. Kenway and Graeme J. Kennedy and Joaquim R. R. A. Martins} 42 | } 43 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | import sys 4 | 5 | # External modules 6 | from sphinx_mdolab_theme.config import * 7 | 8 | # -- Path setup -------------------------------------------------------------- 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | 13 | 14 | sys.path.insert(0, os.path.abspath("../")) 15 | sys.path.insert(0, os.path.abspath("../../")) 16 | sys.path.insert(0, os.path.abspath("./_exts")) 17 | 18 | # -- Project information ----------------------------------------------------- 19 | project = "pyGeo" 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | # Built-in Sphinx extensions are already contained in the imported variable 23 | # here we add external extensions, which must also be added to requirements.txt 24 | # so that RTD can import and use them 25 | extensions.extend( 26 | [ 27 | "sphinx_mdolab_theme.ext.embed_code", 28 | "sphinxcontrib.bibtex", 29 | ] 30 | ) 31 | 32 | # mock import for autodoc 33 | autodoc_mock_imports = [ 34 | "numpy", 35 | "mpi4py", 36 | "scipy", 37 | "pyspline", 38 | "baseclasses", 39 | "pysurf", 40 | "prefoil", 41 | "pyOCSM", 42 | "openvsp", 43 | "openmdao", 44 | ] 45 | 46 | # This sets the bibtex bibliography file(s) to reference in the documentation 47 | bibtex_bibfiles = ["ref.bib"] 48 | -------------------------------------------------------------------------------- /doc/contribute.rst: -------------------------------------------------------------------------------- 1 | .. _contribute: 2 | 3 | ===================== 4 | Contributing to pyGeo 5 | ===================== 6 | 7 | A general guide to contributing to MDO Lab codes, including bug reports, coding style, and pull requests, can be found :doc:`here `. 8 | pyGeo does require additional packages to run tests, information on which can be found on the :ref:`installation page ` in these docs. -------------------------------------------------------------------------------- /doc/geo_utils.rst: -------------------------------------------------------------------------------- 1 | geo_utils 2 | --------- 3 | File IO 4 | ~~~~~~~ 5 | .. automodule:: pygeo.geo_utils.file_io 6 | :members: 7 | 8 | FFD Generation 9 | ~~~~~~~~~~~~~~ 10 | .. automodule:: pygeo.geo_utils.ffd_generation 11 | :members: 12 | 13 | Point Reduce 14 | ~~~~~~~~~~~~ 15 | .. automodule:: pygeo.geo_utils.remove_duplicates 16 | :members: 17 | -------------------------------------------------------------------------------- /doc/images/DPW4_FFD-27745.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/doc/images/DPW4_FFD-27745.gif -------------------------------------------------------------------------------- /doc/images/c172.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/doc/images/c172.jpg -------------------------------------------------------------------------------- /doc/images/wing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/doc/images/wing.png -------------------------------------------------------------------------------- /doc/images/wingNew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/doc/images/wingNew.png -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. pyGeo documentation master file, created by 2 | sphinx-quickstart on Sat Dec 7 13:50:49 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. _pygeo_index: 7 | 8 | ====== 9 | pyGeo 10 | ====== 11 | 12 | pyGeo is a geometry manipulation framework for multidisciplinary design optimization. 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | introduction 18 | contribute 19 | install 20 | citation 21 | tutorial 22 | API 23 | -------------------------------------------------------------------------------- /doc/install.rst: -------------------------------------------------------------------------------- 1 | .. _install: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | Installation requires a working copy of the ``pyspline`` package, which requires a Fortran compiler and is available on `GitHub `_. 8 | Because of this dependency, pyGeo is only supported on Linux. 9 | On other platforms, a Docker image that includes pyGeo and other MDO Lab codes can be used following `these instructions `_. 10 | 11 | To install ``pyGeo``, first clone the `repo `_, then go into the root directory and type:: 12 | 13 | pip install . 14 | 15 | The tests require unique dependencies ``numpy-stl`` and ``parameterized``. 16 | These and additional, generic, testing dependencies can be installed by using:: 17 | 18 | pip install .[testing] 19 | 20 | For stability we recommend cloning or checking out a tagged release. 21 | 22 | ------------- 23 | pyGeo and ESP 24 | ------------- 25 | The simplest way to install ESP so that it works with pyGeo is to use the Docker image mentioned above, which will have pyGeo and ESP both installed. 26 | The ESP GUI can then be installed on your local machine for Mac, Windows, and Linux following the instructions in their README to view ESP models. 27 | 28 | Our currently tested versions are the ``x86`` and ``arm`` versions of 1.23 and 1.24, which can be installed from the `archive page `_. 29 | ESP does not currently support Intel compilers for C/C++, so ESP is compiled with gcc in the Intel versions of our Docker images. 30 | -------------------------------------------------------------------------------- /doc/introduction.rst: -------------------------------------------------------------------------------- 1 | .. _introduction: 2 | 3 | ============ 4 | Introduction 5 | ============ 6 | 7 | ``pyGeo`` is a package for generating and manipulating geometry, particularly for applications involving shape optimization. 8 | 9 | .. figure:: images/DPW4_FFD-27745.gif 10 | :width: 600 11 | :align: center 12 | 13 | Manipulation of individual control points on an FFD volume of an airplane 14 | 15 | ---------- 16 | Background 17 | ---------- 18 | 19 | *Shape optimization* is a field of engineering that uses applied mathematics and computer simulation to find the best possible shape for an object, such as an aircraft or a ship. 20 | To do this, optimization software needs to perturb a baseline geometry into a new shape at each iteration. 21 | Users define *design variables* which map numeric inputs from the optimizer to changes in the geometry. 22 | 23 | Examples of geometric design variables include: 24 | 25 | - Span, aspect ratio, and sweep in a wing design problem 26 | - Freeboard, deadrise, and beam for a ship design problem 27 | - Blade length, twist distribution, and airfoil shape modes for a wind turbine problem 28 | 29 | The set of all geometric design variables and their effects on the baseline geometry is called a *parameterization*. 30 | Parameterizations are not unique, and choosing a good one is both an art and a science. 31 | 32 | There are two primary approaches to geometry parameterization: generative and deformative. 33 | In pyGeo, we primarily use a free form deformation (FFD) approach, which belongs to the latter category. 34 | This approach does not require the parameterization of the baseline geometry (as is the case for a generative approach such as CAD) because we only parameterized the changes with respect to the unperturbed geometry 35 | 36 | Some geometries that can be generated by a parameterization are not allowable for physical or other engineering reasons. 37 | For this reason, *geometric constraints* must often also be imposed (such as a minimum volume inside a car's cabin, or a wing thickness). 38 | 39 | ``pyGeo`` enables users to set up a variety of geometric parameterizations and geometric constraints. 40 | It is particularly useful for *gradient-based* optimization because it provides accurate derivatives of geometry with respect to the design variables. 41 | 42 | ------------------- 43 | Package Description 44 | ------------------- 45 | 46 | The package consists of the following high-level modules: 47 | 48 | - **DVGeometry** (short for "Design Variable Geometry") parameterizes and modifies geometry (pointsets) in shape optimization problems, using the free-form deformation (FFD) method 49 | - **DVGeometryMulti** parameterizes multiple geometric components using multiple FFDs and handles design changes near component intersections 50 | - **DVGeometryESP** enables CAD-based geometry parameterization using Engineering Sketch Pad (ESP) software 51 | - **DVGeometryVSP** enables conceptual geometry parameterization using the Vehicle Sketch Pad (OpenVSP) software 52 | - **DVConstraints** allows users to constrain important geometric parameters during optimization (such as thicknesses and volumes) 53 | - **pyGeo** generates spline surfaces, either from geometry files or user-defined wing surfaces 54 | -------------------------------------------------------------------------------- /doc/mphys_dvgeo.rst: -------------------------------------------------------------------------------- 1 | .. _OM_DVGEOCOMP: 2 | 3 | MPhys DVGeo Component 4 | --------------------- 5 | .. autoclass:: pygeo.mphys.mphys_dvgeo.OM_DVGEOCOMP 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/pyGeo.rst: -------------------------------------------------------------------------------- 1 | .. _pyGeo: 2 | 3 | pyGeo 4 | ----- 5 | .. autoclass:: pygeo.pyGeo.pyGeo 6 | :members: 7 | -------------------------------------------------------------------------------- /doc/ref.bib: -------------------------------------------------------------------------------- 1 | @article{CST, 2 | author = {B. M. Kulfan}, 3 | doi = {10.2514/1.29958}, 4 | journal = {Journal of Aircraft}, 5 | month = {January}, 6 | number = {1}, 7 | pages = {142--158}, 8 | publisher = {American Institute of Aeronautics and Astronautics ({AIAA})}, 9 | title = {Universal Parametric Geometry Representation Method}, 10 | url = {https://doi.org/10.2514/1.29958}, 11 | volume = {45}, 12 | year = {2008} 13 | } 14 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | numpydoc 2 | sphinx_mdolab_theme 3 | -------------------------------------------------------------------------------- /doc/tutorial.rst: -------------------------------------------------------------------------------- 1 | .. _tutorial: 2 | 3 | Tutorials 4 | ========= 5 | 6 | In this section, there is a collection of example use cases for pyGeo. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | getting_started 12 | advanced_ffd 13 | update_pygeo 14 | cst_tutorial 15 | esp_airfoil 16 | -------------------------------------------------------------------------------- /examples/bwb/bwb.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | from pyspline import Curve 4 | 5 | # First party modules 6 | from pygeo import pyGeo 7 | 8 | # ============================================================================== 9 | # Start of Script 10 | # ============================================================================== 11 | naf = 20 12 | n0012 = "naca0012.dat" 13 | 14 | airfoil_list = [n0012 for i in range(naf)] 15 | for i in range(1, naf - 1): 16 | airfoil_list[i] = None 17 | 18 | # Use the digitize it data for the planform: 19 | le = np.array(np.loadtxt("bwb_le.out")) 20 | te = np.array(np.loadtxt("bwb_te.out")) 21 | front_up = np.array(np.loadtxt("bwb_front_up.out")) 22 | front_low = np.array(np.loadtxt("bwb_front_low.out")) 23 | 24 | le[0, :] = 0 25 | te[0, 0] = 0 26 | front_up[0, 0] = 0 27 | front_low[0, 0] = 0 28 | 29 | # Now make a ONE-DIMENSIONAL spline for each of the le and trailing edes 30 | le_spline = Curve(X=le[:, 1], s=le[:, 0], nCtl=11, k=4) 31 | te_spline = Curve(X=te[:, 1], s=te[:, 0], nCtl=11, k=4) 32 | up_spline = Curve(X=front_up[:, 1], s=front_up[:, 0], nCtl=11, k=4) 33 | low_spline = Curve(X=front_low[:, 1], s=front_low[:, 0], nCtl=11, k=4) 34 | 35 | # Generate consistent equally spaced spline data 36 | span = np.linspace(0, 1, naf) 37 | 38 | le = le_spline(span) 39 | te = te_spline(span) 40 | up = up_spline(span) 41 | low = low_spline(span) 42 | 43 | ref_span = 138 44 | chord = te - le 45 | x = le 46 | z = span * ref_span 47 | mid_y = (up[0] + low[0]) / 2.0 48 | y = -(up + low) / 2 + mid_y 49 | 50 | # Scale the thicknesses 51 | toc = -(up - low) / chord 52 | thickness = toc / 0.12 53 | 54 | rot_x = np.zeros(naf) 55 | rot_y = np.zeros(naf) 56 | rot_z = np.zeros(naf) 57 | offset = np.zeros((naf, 2)) 58 | 59 | bwb = pyGeo( 60 | "liftingSurface", 61 | xsections=airfoil_list, 62 | scale=chord, 63 | offset=offset, 64 | thickness=thickness, 65 | bluntTe=True, 66 | teHeight=0.05, 67 | tip="rounded", 68 | x=x, 69 | y=y, 70 | z=z, 71 | ) 72 | 73 | bwb.writeIGES("bwb.igs") 74 | -------------------------------------------------------------------------------- /examples/bwb/bwb_front_low.out: -------------------------------------------------------------------------------- 1 | -0.203169 -25.7217 2 | 2.24896 -25.7128 3 | 4.3506 -25.8802 4 | 6.9775 -26.2206 5 | 8.90398 -26.3885 6 | 10.6553 -26.5572 7 | 12.9317 -27.0738 8 | 15.3839 -27.0649 9 | 17.6606 -27.2316 10 | 20.1124 -27.5726 11 | 22.0389 -27.7406 12 | 24.1405 -27.908 13 | 26.2421 -28.0753 14 | 28.6941 -28.2414 15 | 30.6208 -28.2344 16 | 33.5976 -28.9234 17 | 36.3991 -29.7881 18 | 39.0254 -30.6534 19 | 41.3014 -31.52 20 | 43.9278 -32.3854 21 | 46.3793 -32.9014 22 | 49.181 -33.5911 23 | 51.6325 -34.1071 24 | 54.2593 -34.6225 25 | 56.8862 -34.9629 26 | 59.5129 -35.4783 27 | 61.9648 -35.6443 28 | 64.7667 -36.1591 29 | 67.3938 -36.3245 30 | 69.8455 -36.6655 31 | 71.9472 -36.8329 32 | 74.5742 -36.9983 33 | 77.0262 -37.1644 34 | 79.8284 -37.3292 35 | 82.4557 -37.3196 36 | 85.4329 -37.6587 37 | 87.3598 -37.4768 38 | 91.0378 -37.6384 39 | 95.0663 -37.6238 40 | 98.394 -37.7866 41 | 101.722 -38.1245 42 | 104.874 -38.288 43 | 108.027 -38.4515 44 | 111.529 -38.7888 45 | 114.331 -39.1285 46 | 117.834 -39.1158 47 | 120.987 -39.4543 48 | 123.964 -39.6185 49 | 126.942 -39.7826 50 | 130.444 -39.9449 51 | 133.422 -40.284 52 | 136.574 -40.4475 53 | 139.727 -40.611 54 | -------------------------------------------------------------------------------- /examples/bwb/bwb_front_up.out: -------------------------------------------------------------------------------- 1 | 0.120643 -50.0418 2 | 3.44892 -49.6797 3 | 6.60223 -49.1434 4 | 9.75554 -48.607 5 | 12.9089 -48.0706 6 | 16.2375 -47.3586 7 | 19.2159 -46.6479 8 | 22.0187 -46.2878 9 | 25.172 -45.7514 10 | 27.4493 -45.3932 11 | 30.2524 -44.8581 12 | 33.4055 -44.4967 13 | 36.2083 -44.1366 14 | 39.1864 -43.6008 15 | 42.5149 -43.0638 16 | 45.8428 -43.0517 17 | 48.9959 -42.6903 18 | 51.7985 -42.5052 19 | 54.0757 -42.322 20 | 56.3529 -42.1387 21 | 58.4551 -41.7811 22 | 60.9072 -41.7722 23 | 64.0603 -41.4108 24 | 66.3373 -41.4025 25 | 69.1401 -41.0424 26 | 71.5925 -40.8585 27 | 73.8696 -40.6753 28 | 75.7965 -40.4933 29 | 77.8983 -40.4857 30 | 80.1753 -40.4774 31 | 81.9268 -40.471 32 | 84.7293 -40.4609 33 | 87.5317 -40.4507 34 | 90.6842 -40.6142 35 | 93.837 -40.6028 36 | 96.8146 -40.5919 37 | 99.4417 -40.7574 38 | 102.069 -41.0978 39 | 105.221 -41.0863 40 | 108.374 -41.2498 41 | 112.052 -41.4115 42 | 114.854 -41.4013 43 | 117.481 -41.5667 44 | 120.459 -41.7309 45 | 122.911 -41.722 46 | 125.363 -41.888 47 | 128.515 -42.0515 48 | 131.843 -42.0394 49 | 134.82 -42.5536 50 | 137.272 -42.5446 51 | 139.724 -42.7107 52 | -------------------------------------------------------------------------------- /examples/bwb/bwb_le.out: -------------------------------------------------------------------------------- 1 | 0.00019059 0.174973 2 | 2.10354 .174973 # Modified this one 3 | 3.33094 2.81166 4 | 4.9096 4.91707 5 | 6.83989 8.24857 6 | 8.24397 10.8783 7 | 9.82377 14.0335 8 | 10.8774 16.487 9 | 11.9307 18.7654 10 | 12.9839 20.8689 11 | 14.3882 23.6736 12 | 15.4414 25.7771 13 | 16.6698 27.8812 14 | 18.0735 30.161 15 | 19.6525 32.6163 16 | 21.5818 35.073 17 | 24.2131 38.7569 18 | 26.6685 41.7404 19 | 28.9483 44.3733 20 | 30.8775 46.6549 21 | 33.3325 49.2884 22 | 36.138 52.0982 23 | 38.4174 54.3811 24 | 41.7495 58.2426 25 | 44.0284 60.0006 26 | 48.9374 64.3928 27 | 51.5672 66.677 28 | 54.0218 68.9606 29 | 56.3007 70.7186 30 | 58.7549 72.6522 31 | 61.2095 74.9357 32 | 63.8391 77.045 33 | 65.7673 78.4517 34 | 71.5524 83.197 35 | 74.7078 85.6581 36 | 77.5128 87.9429 37 | 80.493 90.4034 38 | 82.2463 91.9845 39 | 85.5768 94.4462 40 | 88.2064 96.5555 41 | 91.5371 99.1922 42 | 94.5174 101.653 43 | 96.446 103.409 44 | 99.7765 105.871 45 | 102.757 108.157 46 | 105.211 110.265 47 | 108.366 112.551 48 | 110.645 114.309 49 | 113.801 116.945 50 | 116.43 119.055 51 | 119.761 121.516 52 | 122.215 123.625 53 | 124.845 125.734 54 | 128 128.02 55 | 130.454 129.954 56 | 133.084 132.063 57 | 135.538 133.822 58 | 139.044 136.634 59 | 140.447 138.039 60 | -------------------------------------------------------------------------------- /examples/bwb/bwb_le_spar.out: -------------------------------------------------------------------------------- 1 | 0.217674 24.7023 2 | 5.44186 29.1216 3 | 16.9786 41.3747 4 | 31.5628 56.6409 5 | 44.4056 70.0992 6 | 52.8949 78.9376 7 | 58.3367 84.9636 8 | 67.0437 92.3962 9 | 79.2335 100.03 10 | 90.1172 107.262 11 | 97.9535 112.686 12 | 109.49 120.12 13 | 121.462 128.356 14 | 139.529 140.008 15 | -------------------------------------------------------------------------------- /examples/bwb/bwb_rib_list.out: -------------------------------------------------------------------------------- 1 | 0.217674 66.2743 2 | 12.6251 66.4777 3 | 24.8149 68.6894 4 | 49.6298 88.1751 5 | 55.0716 90.9879 6 | 61.3842 94.6041 7 | 67.0437 97.0153 8 | 71.8326 101.435 9 | 78.3628 104.649 10 | 84.24 107.261 11 | 88.3758 110.676 12 | 92.294 112.886 13 | 95.5591 114.895 14 | 98.8242 117.105 15 | 102.089 119.315 16 | 105.572 121.324 17 | 109.273 123.935 18 | 112.755 126.145 19 | 116.02 128.556 20 | 119.503 130.565 21 | 122.333 132.775 22 | 125.163 134.784 23 | 128.428 136.391 24 | 131.475 138.199 25 | 134.74 140.007 26 | 137.57 141.614 27 | -------------------------------------------------------------------------------- /examples/bwb/bwb_te.out: -------------------------------------------------------------------------------- 1 | 0.15838 145.403 2 | 6.98855 144.728 3 | 11.5416 143.87 4 | 15.2188 143.008 5 | 19.2463 142.148 6 | 31.8524 137.644 7 | 36.0539 135.735 8 | 39.0298 134.171 9 | 42.5307 132.259 10 | 45.8569 130.696 11 | 48.8332 129.482 12 | 52.3349 128.27 13 | 55.6614 127.057 14 | 58.813 126.019 15 | 61.9646 124.981 16 | 64.9413 124.117 17 | 67.7427 123.252 18 | 69.1434 122.732 19 | 70.8945 122.389 20 | 72.8206 121.871 21 | 75.4475 121.53 22 | 77.1991 121.537 23 | 80.0015 121.547 24 | 82.454 121.906 25 | 84.0306 122.086 26 | 85.6071 122.267 27 | 88.2352 122.976 28 | 90.1626 123.683 29 | 91.7399 124.564 30 | 93.3172 125.445 31 | 94.1934 125.798 32 | 96.1212 126.855 33 | 98.9255 128.614 34 | 101.729 129.849 35 | 104.358 131.259 36 | 107.337 132.844 37 | 110.667 134.781 38 | 112.42 135.662 39 | 113.646 136.017 40 | 116.626 137.777 41 | 119.079 139.011 42 | 121.007 140.243 43 | 123.11 141.3 44 | 125.388 142.358 45 | 127.492 143.591 46 | 129.945 144.825 47 | 132.223 146.058 48 | 134.677 147.116 49 | 136.78 148.524 50 | -------------------------------------------------------------------------------- /examples/bwb/bwb_te_spar.out: -------------------------------------------------------------------------------- 1 | 0.217674 98.0056 2 | 12.6251 98.0082 3 | 25.0326 97.8099 4 | 37.0047 100.825 5 | 50.2828 104.443 6 | 67.0437 108.663 7 | 84.24 114.692 8 | 99.9126 123.532 9 | 113.191 130.965 10 | 126.687 138.6 11 | 139.529 146.033 12 | -------------------------------------------------------------------------------- /examples/bwb/digitizing/FS-1997-07-24-LaRC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/bwb/digitizing/FS-1997-07-24-LaRC.pdf -------------------------------------------------------------------------------- /examples/bwb/digitizing/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/bwb/digitizing/Screenshot.png -------------------------------------------------------------------------------- /examples/bwb/digitizing/bwb_layout.dig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/bwb/digitizing/bwb_layout.dig -------------------------------------------------------------------------------- /examples/bwb/digitizing/bwb_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/bwb/digitizing/bwb_layout.png -------------------------------------------------------------------------------- /examples/bwb/digitizing/bwb_output.dig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/bwb/digitizing/bwb_output.dig -------------------------------------------------------------------------------- /examples/bwb/naca0012.dat: -------------------------------------------------------------------------------- 1 | 1.000000 0.001260 2 | 0.992704 0.002274 3 | 0.979641 0.004079 4 | 0.964244 0.006169 5 | 0.947231 0.008434 6 | 0.929323 0.010765 7 | 0.910956 0.013101 8 | 0.892372 0.015420 9 | 0.873723 0.017700 10 | 0.855041 0.019931 11 | 0.836311 0.022119 12 | 0.817558 0.024266 13 | 0.798819 0.026366 14 | 0.780088 0.028414 15 | 0.761336 0.030413 16 | 0.742560 0.032370 17 | 0.723780 0.034284 18 | 0.705012 0.036149 19 | 0.686255 0.037964 20 | 0.667502 0.039728 21 | 0.648751 0.041440 22 | 0.630004 0.043098 23 | 0.611266 0.044701 24 | 0.592538 0.046245 25 | 0.573821 0.047728 26 | 0.555117 0.049149 27 | 0.536430 0.050503 28 | 0.517763 0.051786 29 | 0.499117 0.052996 30 | 0.480488 0.054127 31 | 0.461875 0.055178 32 | 0.443287 0.056144 33 | 0.424740 0.057019 34 | 0.406241 0.057796 35 | 0.387789 0.058466 36 | 0.369372 0.059023 37 | 0.350989 0.059462 38 | 0.332648 0.059779 39 | 0.314366 0.059965 40 | 0.296159 0.060009 41 | 0.278033 0.059903 42 | 0.259997 0.059634 43 | 0.242060 0.059191 44 | 0.224236 0.058562 45 | 0.206544 0.057733 46 | 0.189011 0.056692 47 | 0.171676 0.055421 48 | 0.154596 0.053909 49 | 0.137852 0.052138 50 | 0.121548 0.050098 51 | 0.105827 0.047785 52 | 0.090903 0.045220 53 | 0.077039 0.042449 54 | 0.064541 0.039548 55 | 0.053594 0.036612 56 | 0.044211 0.033717 57 | 0.036254 0.030913 58 | 0.029567 0.028218 59 | 0.023982 0.025653 60 | 0.019310 0.023217 61 | 0.015371 0.020871 62 | 0.012012 0.018579 63 | 0.009117 0.016316 64 | 0.006653 0.014058 65 | 0.004621 0.011797 66 | 0.003007 0.009544 67 | 0.001777 0.007318 68 | 0.000894 0.005155 69 | 0.000322 0.003059 70 | 0.000036 0.001014 71 | 0.000036 -0.001014 72 | 0.000322 -0.003059 73 | 0.000894 -0.005155 74 | 0.001777 -0.007318 75 | 0.003007 -0.009544 76 | 0.004621 -0.011797 77 | 0.006653 -0.014058 78 | 0.009117 -0.016316 79 | 0.012012 -0.018579 80 | 0.015371 -0.020871 81 | 0.019310 -0.023217 82 | 0.023982 -0.025653 83 | 0.029567 -0.028218 84 | 0.036254 -0.030913 85 | 0.044211 -0.033717 86 | 0.053594 -0.036612 87 | 0.064541 -0.039548 88 | 0.077039 -0.042449 89 | 0.090903 -0.045220 90 | 0.105827 -0.047784 91 | 0.121548 -0.050098 92 | 0.137852 -0.052138 93 | 0.154596 -0.053909 94 | 0.171676 -0.055421 95 | 0.189011 -0.056692 96 | 0.206544 -0.057733 97 | 0.224236 -0.058562 98 | 0.242060 -0.059191 99 | 0.259997 -0.059634 100 | 0.278033 -0.059903 101 | 0.296159 -0.060009 102 | 0.314366 -0.059965 103 | 0.332648 -0.059779 104 | 0.350989 -0.059462 105 | 0.369372 -0.059023 106 | 0.387789 -0.058466 107 | 0.406241 -0.057796 108 | 0.424740 -0.057019 109 | 0.443287 -0.056144 110 | 0.461875 -0.055178 111 | 0.480488 -0.054127 112 | 0.499117 -0.052996 113 | 0.517763 -0.051786 114 | 0.536430 -0.050503 115 | 0.555117 -0.049149 116 | 0.573821 -0.047728 117 | 0.592538 -0.046245 118 | 0.611266 -0.044701 119 | 0.630004 -0.043098 120 | 0.648751 -0.041440 121 | 0.667502 -0.039728 122 | 0.686255 -0.037964 123 | 0.705012 -0.036149 124 | 0.723780 -0.034284 125 | 0.742560 -0.032370 126 | 0.761336 -0.030413 127 | 0.780088 -0.028414 128 | 0.798819 -0.026366 129 | 0.817558 -0.024266 130 | 0.836311 -0.022119 131 | 0.855041 -0.019931 132 | 0.873723 -0.017700 133 | 0.892372 -0.015420 134 | 0.910956 -0.013101 135 | 0.929323 -0.010765 136 | 0.947231 -0.008434 137 | 0.964244 -0.006169 138 | 0.979641 -0.004079 139 | 0.992704 -0.002274 140 | 1.000000 -0.001260 141 | -------------------------------------------------------------------------------- /examples/c172_wing/c172.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # First party modules 5 | from pygeo import pyGeo 6 | 7 | # ============================================================================== 8 | # Start of Script 9 | # ============================================================================== 10 | naf = 3 11 | airfoil_list = ["naca2412.dat", "naca2412.dat", "naca2412.dat"] 12 | chord = [1.67, 1.67, 1.18] 13 | x = [0, 0, 0.125 * 1.18] 14 | y = [0, 0, 0] 15 | z = [0, 2.5, 10.58 / 2] 16 | rot_x = [0, 0, 0] 17 | rot_y = [0, 0, 0] 18 | rot_z = [0, 0, 2] 19 | offset = np.zeros((naf, 2)) 20 | 21 | # There are several examples that follow showing many of the different 22 | # combinations of tip/trailing edge options that are available. 23 | 24 | # --------- Sharp Trailing Edge / Rounded Tip ------- 25 | wing = pyGeo( 26 | "liftingSurface", 27 | xsections=airfoil_list, 28 | scale=chord, 29 | offset=offset, 30 | x=x, 31 | y=y, 32 | z=z, 33 | rotX=rot_x, 34 | rotY=rot_y, 35 | rotZ=rot_z, 36 | kSpan=2, 37 | tip="rounded", 38 | ) 39 | 40 | wing.writeTecplot("c172_sharp_te_rounded_tip.dat") 41 | wing.writeIGES("c172_sharp_te_rounded_tip.igs") 42 | 43 | # --------- Sharp Trailing Edge / Pinched Tip ------- 44 | wing = pyGeo( 45 | "liftingSurface", 46 | xsections=airfoil_list, 47 | scale=chord, 48 | offset=offset, 49 | x=x, 50 | y=y, 51 | z=z, 52 | rotX=rot_x, 53 | rotY=rot_y, 54 | rotZ=rot_z, 55 | kSpan=2, 56 | tip="pinched", 57 | ) 58 | 59 | wing.writeTecplot("c172_sharp_te_pinched_tip.dat") 60 | wing.writeIGES("c172_sharp_te_pinched_tip.igs") 61 | 62 | # --------- Sharp Trailing Edge / Rounded Tip with Fitting ------- 63 | # This option shouldn't be used except to match previously generated 64 | # geometries 65 | wing = pyGeo( 66 | "liftingSurface", 67 | xsections=airfoil_list, 68 | nCtl=29, 69 | scale=chord, 70 | offset=offset, 71 | x=x, 72 | y=y, 73 | z=z, 74 | rotX=rot_x, 75 | rotY=rot_y, 76 | rotZ=rot_z, 77 | kSpan=2, 78 | tip="rounded", 79 | ) 80 | 81 | wing.writeTecplot("c172_sharp_te_rounded_tip_fitted.dat") 82 | wing.writeIGES("c172_sharp_te_rounded_tip_fitted.igs") 83 | 84 | # --------- Blunt Trailing (Flat) / Rounded Tip ------- 85 | 86 | # This is the normal way of producing blunt TE geometries. The 87 | # thickness of the trailing edge is specified with 'te_height', either 88 | # a constant value or an array of length naf. This is in physical 89 | # units. Alternatively, 'te_height_scaled' can be specified to have a 90 | # scaled thickness. This option is specified as a fraction of initial 91 | # chord, so te_height_scaled=0.002 will give a 0.2% trailing edge 92 | # thickness 93 | wing = pyGeo( 94 | "liftingSurface", 95 | xsections=airfoil_list, 96 | scale=chord, 97 | offset=offset, 98 | x=x, 99 | y=y, 100 | z=z, 101 | rotX=rot_x, 102 | rotY=rot_y, 103 | rotZ=rot_z, 104 | bluntTe=True, 105 | teHeightScaled=0.002, 106 | kSpan=2, 107 | tip="rounded", 108 | ) 109 | 110 | wing.writeTecplot("c172_blunt_te_rounded_tip.dat") 111 | wing.writeIGES("c172_blunt_te_rounded_tip.igs") 112 | 113 | # --------- Blunt Trailing (Rounded) / Rounded Tip ------- 114 | 115 | # Alternative way of producing rounded trailing edges that can be easier 116 | # to mesh and extrude with pyHyp. 117 | wing = pyGeo( 118 | "liftingSurface", 119 | xsections=airfoil_list, 120 | scale=chord, 121 | offset=offset, 122 | x=x, 123 | y=y, 124 | z=z, 125 | rotX=rot_x, 126 | rotY=rot_y, 127 | rotZ=rot_z, 128 | bluntTe=True, 129 | roundedTe=True, 130 | teHeightScaled=0.002, 131 | kSpan=2, 132 | tip="rounded", 133 | ) 134 | 135 | wing.writeTecplot("c172_rounded_te_rounded_tip.dat") 136 | wing.writeIGES("c172_rounded_te_rounded_tip.igs") 137 | -------------------------------------------------------------------------------- /examples/c172_wing/genFFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | nstreamwise = 10 5 | nspanwise = 8 6 | FFDbox = np.zeros((nstreamwise, 2, nspanwise, 3)) 7 | 8 | zspan1 = np.linspace(-0.001, 2.5, 4) 9 | zspan2 = np.linspace(2.5, 5.4, 5)[1:] 10 | chord1 = 1.70 * np.ones(4) 11 | chord2 = np.linspace(1.70, 1.20, 5)[1:] 12 | xoff1 = -0.01 * np.ones(4) 13 | xoff2 = np.linspace(-0.01, 0.1375, 5)[1:] 14 | toverc = np.ones(8) * 0.16 15 | 16 | chords = np.concatenate([chord1, chord2]) 17 | xle = np.concatenate([xoff1, xoff2]) 18 | xte = xle + chords 19 | yl = -toverc * chords / 3 20 | yu = 2 * toverc * chords / 3 21 | z = np.concatenate([zspan1, zspan2]) 22 | 23 | for k in range(nspanwise): 24 | # create the FFD box topology 25 | # 1st dim = plot3d i dimension 26 | # 2nd = plot3d j dimension 27 | # 3rd = plot3d k dimension 28 | # 4th = xyz coordinate dimension 29 | # the result is a three-axis tensor of points in R3 30 | FFDbox[:, 0, k, 0] = np.linspace(xle[k], xte[k], nstreamwise) 31 | FFDbox[:, 1, k, 0] = np.linspace(xle[k], xte[k], nstreamwise) 32 | # Y 33 | # lower 34 | FFDbox[:, 0, k, 1] = yl[k] 35 | # upper 36 | FFDbox[:, 1, k, 1] = yu[k] 37 | # Z 38 | FFDbox[:, :, k, 2] = z[k] 39 | 40 | # write the result to disk in plot3d format 41 | # the i dimension is on the rows 42 | # j and k are on newlines 43 | # k changes slower than j 44 | # xyz changes slowest of all 45 | f = open("ffdbox.xyz", "w") 46 | f.write("1\n") 47 | # header row with block topology n x 2 x 8 48 | f.write(str(nstreamwise) + " 2 8\n") 49 | for ell in range(3): 50 | for k in range(8): 51 | for j in range(2): 52 | for i in range(nstreamwise): 53 | f.write("%.15f " % (FFDbox[i, j, k, ell])) 54 | f.write("\n") 55 | f.close() 56 | -------------------------------------------------------------------------------- /examples/c172_wing/images/all_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/all_3d.png -------------------------------------------------------------------------------- /examples/c172_wing/images/baseline_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/baseline_3d.png -------------------------------------------------------------------------------- /examples/c172_wing/images/local_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/local_3d.png -------------------------------------------------------------------------------- /examples/c172_wing/images/refaxis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/refaxis.png -------------------------------------------------------------------------------- /examples/c172_wing/images/sweep_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/sweep_3d.png -------------------------------------------------------------------------------- /examples/c172_wing/images/twist_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/c172_wing/images/twist_3d.png -------------------------------------------------------------------------------- /examples/c172_wing/tecplot_layouts/README.md: -------------------------------------------------------------------------------- 1 | These Tecplot layouts were used to generate the visualizations in the documentation. 2 | Copy them into the parent folder containing "runFFDExample.py" and run that script, which will generate the required geometry files. -------------------------------------------------------------------------------- /examples/cst/CSTTutorial.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | 4 | # External modules 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | 8 | # First party modules 9 | from pygeo import DVGeometryCST 10 | 11 | 12 | # rst Plot func (beg) 13 | def plot_points(points, filename=None): 14 | fig, ax = plt.subplots(figsize=[10, 3]) 15 | ax.plot(points[:, 0], points[:, 1], "-o") 16 | ax.set_aspect("equal") 17 | ax.spines["right"].set_visible(False) 18 | ax.spines["left"].set_visible(False) 19 | ax.spines["top"].set_visible(False) 20 | ax.spines["bottom"].set_visible(False) 21 | ax.set_yticks([]) 22 | 23 | if filename: 24 | fig.savefig(filename) 25 | 26 | return fig, ax 27 | # rst Plot func (end) 28 | 29 | 30 | # rst Init (beg) 31 | # Initialize the DVGeometryCST object 32 | curDir = os.path.dirname(__file__) # directory of this script 33 | airfoilFile = os.path.join(curDir, "naca2412.dat") 34 | nCoeff = 4 # number of CST coefficients on each surface 35 | 36 | DVGeo = DVGeometryCST(airfoilFile, numCST=nCoeff) 37 | # rst Init (end) 38 | 39 | # rst DV (beg) 40 | # Add design variables that we can perturb 41 | DVGeo.addDV("upper_shape", dvType="upper", lowerBound=-0.1, upperBound=0.5) 42 | DVGeo.addDV("lower_shape", dvType="lower", lowerBound=-0.5, upperBound=0.1) 43 | DVGeo.addDV("class_shape_n1", dvType="N1") 44 | DVGeo.addDV("class_shape_n2", dvType="N2") 45 | DVGeo.addDV("chord", dvType="chord") 46 | # rst DV (end) 47 | 48 | # rst Create pointset (beg) 49 | # For this case, we'll just use the points in the airfoil file as the pointset 50 | points = [] 51 | with open(airfoilFile, "r") as f: 52 | for line in f: 53 | points.append([float(n) for n in line.split()]) 54 | 55 | points = np.array(points) 56 | points = np.hstack((points, np.zeros((points.shape[0], 1)))) # add 0s for z coordinates (unused) 57 | # rst Create pointset (end) 58 | 59 | # rst Add pointset (beg) 60 | ptName = "pointset" 61 | DVGeo.addPointSet(points, ptName=ptName) 62 | # rst Add pointset (end) 63 | 64 | # Show current geometry 65 | points = DVGeo.update(ptName) 66 | fig, ax = plot_points(points, filename=os.path.join(curDir, "original_points.svg")) 67 | plt.show() 68 | plt.close(fig) 69 | 70 | # rst Perturb one (beg) 71 | DVGeo.setDesignVars( 72 | { 73 | "upper_shape": np.array([0.3, 0.7, -0.1, 0.6]), 74 | "lower_shape": np.array([-0.1, 0.1, 0.1, -0.3]), 75 | } 76 | ) 77 | points = DVGeo.update(ptName) 78 | # rst Perturb one (end) 79 | 80 | # Show current geometry 81 | fig, ax = plot_points(points, filename=os.path.join(curDir, "perturbed_coeff.svg")) 82 | plt.show() 83 | plt.close(fig) 84 | 85 | # rst Perturb two (beg) 86 | DVGeo.setDesignVars( 87 | { 88 | "class_shape_n1": np.array([0.6]), 89 | "class_shape_n2": np.array([0.8]), 90 | "chord": np.array([2.0]), 91 | } 92 | ) 93 | points = DVGeo.update(ptName) 94 | # rst Perturb two (end) 95 | 96 | # Show current geometry 97 | fig, ax = plot_points(points, filename=os.path.join(curDir, "perturbed_class_chord.svg")) 98 | plt.show() 99 | plt.close(fig) 100 | -------------------------------------------------------------------------------- /examples/deform_geometry/ffd/simple_ffd.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # Bounding box for root airfoil 5 | x_root_range = [-0.1, 5.1] 6 | y_root_range = [-0.5, 0.5] 7 | z_root = -0.01 8 | 9 | # Bounding box for tip airfoil 10 | x_tip_range = [7.4, 9.2] 11 | y_tip_range = [-0.25, 0.25] 12 | z_tip = 14.25 13 | 14 | # Number of FFD control points per dimension 15 | nX = 6 # streamwise 16 | nY = 2 # perpendicular to wing planform 17 | nZ = 8 # spanwise 18 | 19 | # Compute grid points 20 | span_dist = np.linspace(0, 1, nZ) ** 0.8 21 | z_sections = span_dist * (z_tip - z_root) + z_root 22 | 23 | x_te = span_dist * (x_tip_range[0] - x_root_range[0]) + x_root_range[0] 24 | x_le = span_dist * (x_tip_range[1] - x_root_range[1]) + x_root_range[1] 25 | y_coords = np.vstack( 26 | ( 27 | span_dist * (y_tip_range[0] - y_root_range[0]) + y_root_range[0], 28 | span_dist * (y_tip_range[1] - y_root_range[1]) + y_root_range[1], 29 | ) 30 | ) 31 | 32 | X = np.zeros((nY * nZ, nX)) 33 | Y = np.zeros((nY * nZ, nX)) 34 | Z = np.zeros((nY * nZ, nX)) 35 | row = 0 36 | for k in range(nZ): 37 | for j in range(nY): 38 | X[row, :] = np.linspace(x_te[k], x_le[k], nX) 39 | Y[row, :] = np.ones(nX) * y_coords[j, k] 40 | Z[row, :] = np.ones(nX) * z_sections[k] 41 | row += 1 42 | 43 | # Write FFD to file 44 | filename = "ffd.xyz" 45 | f = open(filename, "w") 46 | f.write("\t\t1\n") 47 | f.write("\t\t%d\t\t%d\t\t%d\n" % (nX, nY, nZ)) 48 | for i in [X, Y, Z]: 49 | for row in i: 50 | vals = tuple(row) 51 | f.write("\t%3.8f\t%3.8f\t%3.8f\t%3.8f\t%3.8f\t%3.8f\n" % vals) 52 | f.close() 53 | -------------------------------------------------------------------------------- /examples/deform_geometry/geo/generate_wing.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # First party modules 5 | from pygeo import pyGeo 6 | 7 | # Number of airfoil sections 8 | naf = 10 9 | airfoil_list = ["rae2822.dat"] * naf 10 | 11 | # Airfoil leading edge positions 12 | x = np.linspace(0.0, 7.5, naf) 13 | y = np.linspace(0.0, 0.0, naf) 14 | z = np.linspace(0.0, 14.0, naf) 15 | 16 | offset = np.zeros((naf, 2)) # x-y offset applied to airfoil position before scaling 17 | 18 | # Airfoil rotations 19 | rot_x = [0.0] * naf 20 | rot_y = [0.0] * naf 21 | rot_z = [0.0] * naf 22 | 23 | # Airfoil scaling 24 | chord = np.linspace(5.0, 1.5, naf) 25 | 26 | # Run pyGeo 27 | wing = pyGeo( 28 | "liftingSurface", 29 | xsections=airfoil_list, 30 | scale=chord, 31 | offset=offset, 32 | x=x, 33 | y=y, 34 | z=z, 35 | rotX=rot_x, 36 | rotY=rot_y, 37 | rotZ=rot_z, 38 | tip="rounded", 39 | bluntTe=True, 40 | squareTeTip=True, 41 | teHeight=0.25 * 0.0254, 42 | ) 43 | 44 | # Write Output File 45 | wing.writeTecplot("wing.dat") 46 | wing.writeIGES("wing.igs") 47 | # wing.writeTin("wing.tin") 48 | -------------------------------------------------------------------------------- /examples/deform_geometry/geo/rae2822.dat: -------------------------------------------------------------------------------- 1 | ZONE="RAE 2822 TRANSONIC AIRFOIL" 2 | 1 0 3 | 0.999398 0.000128 4 | 0.997592 0.00051 5 | 0.994588 0.001137 6 | 0.990393 0.002001 7 | 0.985016 0.003092 8 | 0.97847 0.004401 9 | 0.970772 0.005915 10 | 0.96194 0.007622 11 | 0.951995 0.009508 12 | 0.940961 0.011562 13 | 0.928864 0.013769 14 | 0.915735 0.016113 15 | 0.901604 0.01858 16 | 0.886505 0.021153 17 | 0.870476 0.023817 18 | 0.853553 0.026554 19 | 0.835779 0.029347 20 | 0.817197 0.032176 21 | 0.79785 0.035017 22 | 0.777785 0.037847 23 | 0.757051 0.040641 24 | 0.735698 0.043377 25 | 0.713778 0.046029 26 | 0.691342 0.048575 27 | 0.668445 0.050993 28 | 0.645142 0.053258 29 | 0.62149 0.055344 30 | 0.597545 0.057218 31 | 0.573365 0.058845 32 | 0.549009 0.060194 33 | 0.524534 0.061254 34 | 0.5 0.062029 35 | 0.475466 0.06253 36 | 0.450991 0.062774 37 | 0.426635 0.062779 38 | 0.402455 0.062562 39 | 0.37851 0.062133 40 | 0.354858 0.061497 41 | 0.331555 0.06066 42 | 0.308658 0.059629 43 | 0.286222 0.058414 44 | 0.264302 0.057026 45 | 0.242949 0.05547 46 | 0.222215 0.053753 47 | 0.20215 0.051885 48 | 0.182803 0.049874 49 | 0.164221 0.047729 50 | 0.146447 0.045457 51 | 0.129524 0.043071 52 | 0.113495 0.040585 53 | 0.098396 0.038011 54 | 0.084265 0.03536 55 | 0.071136 0.032644 56 | 0.059039 0.029874 57 | 0.048005 0.027062 58 | 0.03806 0.024219 59 | 0.029228 0.021348 60 | 0.02153 0.018441 61 | 0.014984 0.015489 62 | 0.009607 0.01248 63 | 0.005412 0.009416 64 | 0.002408 0.006306 65 | 0.000602 0.003165 66 | 0 0 67 | 0.000602 -0.00316 68 | 0.002408 -0.006308 69 | 0.005412 -0.009443 70 | 0.009607 -0.012559 71 | 0.014984 -0.015649 72 | 0.02153 -0.018707 73 | 0.029228 -0.021722 74 | 0.03806 -0.024685 75 | 0.048005 -0.027586 76 | 0.059039 -0.030416 77 | 0.071136 -0.03317 78 | 0.084265 -0.035843 79 | 0.098396 -0.038431 80 | 0.113495 -0.040929 81 | 0.129524 -0.043326 82 | 0.146447 -0.04561 83 | 0.164221 -0.047773 84 | 0.182803 -0.049805 85 | 0.20215 -0.051694 86 | 0.222215 -0.053427 87 | 0.242949 -0.054994 88 | 0.264302 -0.056376 89 | 0.286222 -0.057547 90 | 0.308658 -0.058459 91 | 0.331555 -0.059046 92 | 0.354858 -0.059236 93 | 0.37851 -0.058974 94 | 0.402455 -0.058224 95 | 0.426635 -0.056979 96 | 0.450991 -0.055257 97 | 0.475466 -0.053099 98 | 0.5 -0.050563 99 | 0.524534 -0.047719 100 | 0.549009 -0.044642 101 | 0.573365 -0.041397 102 | 0.597545 -0.038043 103 | 0.62149 -0.034631 104 | 0.645142 -0.031207 105 | 0.668445 -0.027814 106 | 0.691342 -0.024495 107 | 0.713778 -0.021289 108 | 0.735698 -0.018232 109 | 0.757051 -0.015357 110 | 0.777785 -0.01269 111 | 0.79785 -0.010244 112 | 0.817197 -0.008027 113 | 0.835779 -0.006048 114 | 0.853553 -0.004314 115 | 0.870476 -0.002829 116 | 0.886505 -0.001592 117 | 0.901604 -0.0006 118 | 0.915735 0.000157 119 | 0.928864 0.000694 120 | 0.940961 0.001033 121 | 0.951995 0.001197 122 | 0.96194 0.001212 123 | 0.970772 0.001112 124 | 0.97847 0.000935 125 | 0.985016 0.000719 126 | 0.990393 0.000497 127 | 0.994588 0.000296 128 | 0.997592 0.000137 129 | 0.999398 0.000035 130 | 1 0 131 | -------------------------------------------------------------------------------- /examples/deform_geometry/runScript.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script demonstrates the deformation of a geometry object using FFD and 3 | the process for exporting the geometry as a tecplot or IGES file. 4 | """ 5 | # Standard Python modules 6 | import argparse 7 | 8 | # External modules 9 | import numpy as np 10 | 11 | # First party modules 12 | from pygeo import DVGeometry, pyGeo 13 | 14 | input_files = "../../input_files/" 15 | 16 | 17 | def deform_liftingsurface(): 18 | # ========================================================================= 19 | # Set Up pyGeo Object 20 | # ========================================================================= 21 | # rst LiftingSurface 22 | # --------------------- Lifting Surface Definition --------------------- # 23 | # Airfoil file 24 | naf = 10 # number of airfoils 25 | airfoil_list = ["./geo/rae2822.dat"] * naf 26 | 27 | # Airfoil leading edge positions 28 | x = np.linspace(0.0, 7.5, naf) 29 | y = np.linspace(0.0, 0.0, naf) 30 | z = np.linspace(0.0, 14.0, naf) 31 | 32 | offset = np.zeros((naf, 2)) # x-y offset applied to airfoil position before scaling 33 | 34 | # Airfoil rotations 35 | rot_x = [0.0] * naf 36 | rot_y = [0.0] * naf 37 | rot_z = [0.0] * naf 38 | 39 | # Airfoil scaling 40 | chord = np.linspace(5.0, 1.5, naf) 41 | 42 | # Run pyGeo 43 | geo = pyGeo( 44 | "liftingSurface", 45 | xsections=airfoil_list, 46 | scale=chord, 47 | offset=offset, 48 | x=x, 49 | y=y, 50 | z=z, 51 | rotX=rot_x, 52 | rotY=rot_y, 53 | rotZ=rot_z, 54 | tip="rounded", 55 | bluntTe=True, 56 | squareTeTip=True, 57 | teHeight=0.25 * 0.0254, 58 | ) 59 | # rst LiftingSurface (end) 60 | 61 | # Deform Geometry Object and Output 62 | deform_DVGeo(geo) 63 | 64 | 65 | def deform_iges(): 66 | # ========================================================================= 67 | # Set Up pyGeo Object 68 | # ========================================================================= 69 | # rst IGES 70 | # ------------------------------ IGES File ------------------------------ # 71 | geo = pyGeo(fileName=input_files + "deform_geometry_wing.igs", initType="iges") 72 | geo.doConnectivity() 73 | # rst IGES (end) 74 | 75 | # Deform Geometry Object and Output 76 | deform_DVGeo(geo) 77 | 78 | 79 | def deform_plot3d(): 80 | # ========================================================================= 81 | # Set Up pyGeo Object 82 | # ========================================================================= 83 | # rst plot3d 84 | # ----------------------------- Plot3D File ----------------------------- # 85 | geo = pyGeo(fileName=input_files + "deform_geometry_wing.xyz", initType="plot3d") 86 | geo.doConnectivity() 87 | geo.fitGlobal() 88 | # rst plot3d (end) 89 | 90 | # Deform Geometry Object and Output 91 | deform_DVGeo(geo) 92 | 93 | 94 | def deform_DVGeo(geo): 95 | # ========================================================================= 96 | # Setup DVGeometry object 97 | # ========================================================================= 98 | # rst DVGeometry 99 | DVGeo = DVGeometry(input_files + "deform_geometry_ffd.xyz") 100 | 101 | # Create reference axis 102 | nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k") 103 | 104 | # Set the Twist Variable 105 | def twist(val, geo): 106 | for i in range(nRefAxPts): 107 | geo.rot_z["wing"].coef[i] = val[i] 108 | 109 | # Add the Twist Design Variable to DVGeo 110 | DVGeo.addGlobalDV(dvName="twist", value=[0] * nRefAxPts, func=twist, lower=-10, upper=10, scale=1.0) 111 | 112 | # Get Design Variables 113 | dvDict = DVGeo.getValues() 114 | 115 | # Set First Twist Section to 5deg 116 | dvDict["twist"][0] = 5 117 | 118 | # Set Design Variables 119 | DVGeo.setDesignVars(dvDict) 120 | # rst DVGeometry (end) 121 | 122 | # ========================================================================= 123 | # Update pyGeo Object and output result 124 | # ========================================================================= 125 | # rst UpdatePyGeo 126 | DVGeo.updatePyGeo(geo, "tecplot", "wingNew", nRefU=10, nRefV=10) 127 | # rst UpdatePyGeo (end) 128 | 129 | 130 | if __name__ == "__main__": 131 | parser = argparse.ArgumentParser() 132 | parser.add_argument("--input_type", type=str, default="iges", choices=["iges", "plot3d", "liftingsurface"]) 133 | args = parser.parse_args() 134 | if args.input_type == "liftingsurface": 135 | deform_liftingsurface() 136 | elif args.input_type == "iges": 137 | deform_iges() 138 | elif args.input_type == "plot3d": 139 | deform_plot3d() 140 | -------------------------------------------------------------------------------- /examples/esp_airfoil/esp_airfoil.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | from stl import mesh 3 | 4 | # First party modules 5 | from pygeo import DVGeometryESP 6 | 7 | # rst create dvgeo start 8 | # initialize the DVGeometry object with the csm file 9 | csm_file = "naca0012.csm" 10 | DVGeo = DVGeometryESP(csm_file) 11 | # rst create dvgeo end 12 | 13 | # rst add pointset start 14 | stlmesh = mesh.Mesh.from_file("esp_airfoil.stl") 15 | # create a pointset. pointsets are of shape npts by 3 (the second dim is xyz coordinate) 16 | # already have the airfoil mesh as a triangulated surface (stl file) 17 | # each vertex set is its own pointset, so we actually add three pointsets 18 | DVGeo.addPointSet(stlmesh.v0, "mesh_v0") 19 | DVGeo.addPointSet(stlmesh.v1, "mesh_v1") 20 | DVGeo.addPointSet(stlmesh.v2, "mesh_v2") 21 | # rst add pointset end 22 | 23 | # rst add DV start 24 | # Now that we have pointsets added, we should parameterize the geometry. 25 | # Adding geometric design variable to make modifications to the airfoil geometry directly 26 | DVGeo.addVariable("camber_dv") 27 | DVGeo.addVariable("maxloc_dv") 28 | DVGeo.addVariable("thickness_dv") 29 | # rst add DV end 30 | 31 | # rst perturb DV start 32 | # Perturb some local variables and observe the effect on the surface 33 | dvdict = DVGeo.getValues() 34 | dvdict["camber_dv"] = 0.09 35 | dvdict["maxloc_dv"] = 0.8 36 | dvdict["thickness_dv"] = 0.2 37 | DVGeo.setDesignVars(dvdict) 38 | 39 | # write out the new ESP model 40 | DVGeo.writeCSMFile("modified.csm") 41 | # rst perturb DV end 42 | -------------------------------------------------------------------------------- /examples/esp_airfoil/esp_airfoil.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/esp_airfoil/esp_airfoil.stl -------------------------------------------------------------------------------- /examples/esp_airfoil/images/new_airfoil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/esp_airfoil/images/new_airfoil.png -------------------------------------------------------------------------------- /examples/esp_airfoil/images/orig_airfoil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/esp_airfoil/images/orig_airfoil.png -------------------------------------------------------------------------------- /examples/esp_airfoil/modified_example.csm: -------------------------------------------------------------------------------- 1 | # modified.csm written by ocsmSave (v1.20) 2 | 3 | # Constant, Design, and Output Parameters: 4 | despmtr camber_dv 0.09000 5 | despmtr maxloc_dv 0.80000 6 | despmtr thickness_dv 0.20000 7 | 8 | # Global Attributes: 9 | 10 | # Branches: 11 | udprim naca camber camber_dv maxloc maxloc_dv thickness thickness_dv 12 | translate 0 0 -0.2 13 | udprim naca camber camber_dv maxloc maxloc_dv thickness thickness_dv 14 | translate 0 0 1.2 15 | blend 0 0 0 0 0 16 | 17 | end 18 | -------------------------------------------------------------------------------- /examples/esp_airfoil/naca0012.csm: -------------------------------------------------------------------------------- 1 | DESPMTR camber_dv 0.0 2 | DESPMTR maxloc_dv 0.4 3 | DESPMTR thickness_dv 0.12 4 | # rst initialize DV 5 | UDPRIM naca camber camber_dv maxloc maxloc_dv thickness thickness_dv 6 | # rst airfoil 1 7 | TRANSLATE 0 0 -0.2 8 | UDPRIM naca camber camber_dv maxloc maxloc_dv thickness thickness_dv 9 | TRANSLATE 0 0 1.2 10 | BLEND 11 | # rst airfoil 2 12 | END -------------------------------------------------------------------------------- /examples/ffd/generateBWBFFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # rst Dimensions 5 | # Bounding box for root airfoil 6 | x_root_range = [0.00, 30.01] 7 | y_root_range = [-3.1, 3.0] 8 | z_root = -23.5 9 | 10 | # Bounding box for tip airfoil 11 | x_tip_range = [0.00, 30.01] 12 | y_tip_range = [-3.1, 3.0] 13 | z_tip = 23.5 14 | 15 | # Number of FFD control points per dimension 16 | nX = 4 # streamwise 17 | nY = 2 # perpendicular to wing planform 18 | nZ = 4 # spanwise 19 | # rst Compute 20 | # Compute grid points 21 | span_dist = np.linspace(0, 1, nZ) ** 0.8 22 | z_sections = span_dist * (z_tip - z_root) + z_root 23 | 24 | x_te = span_dist * (x_tip_range[0] - x_root_range[0]) + x_root_range[0] 25 | x_le = span_dist * (x_tip_range[1] - x_root_range[1]) + x_root_range[1] 26 | y_coords = np.vstack( 27 | ( 28 | span_dist * (y_tip_range[0] - y_root_range[0]) + y_root_range[0], 29 | span_dist * (y_tip_range[1] - y_root_range[1]) + y_root_range[1], 30 | ) 31 | ) 32 | 33 | X = np.zeros((nY * nZ, nX)) 34 | Y = np.zeros((nY * nZ, nX)) 35 | Z = np.zeros((nY * nZ, nX)) 36 | row = 0 37 | for k in range(nZ): 38 | for j in range(nY): 39 | X[row, :] = np.linspace(x_te[k], x_le[k], nX) 40 | Y[row, :] = np.ones(nX) * y_coords[j, k] 41 | Z[row, :] = np.ones(nX) * z_sections[k] 42 | row += 1 43 | # rst Write 44 | # Write FFD to file 45 | filename = "bwb.xyz" 46 | f = open(filename, "w") 47 | f.write("\t\t1\n") 48 | f.write("\t\t%d\t\t%d\t\t%d\n" % (nX, nY, nZ)) 49 | for i in [X, Y, Z]: 50 | for row in i: 51 | vals = tuple(row) 52 | f.write("\t%3.8f\t%3.8f\t%3.8f\t%3.8f\n" % vals) 53 | f.close() 54 | -------------------------------------------------------------------------------- /examples/ffd/generateFFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | 5 | def writeFFDFile(fileName, nBlocks, nx, ny, nz, points): 6 | """ 7 | Take in a set of points and write the plot 3dFile 8 | """ 9 | 10 | f = open(fileName, "w") 11 | 12 | f.write("%d\n" % nBlocks) 13 | for i in range(nBlocks): 14 | f.write("%d %d %d " % (nx[i], ny[i], nz[i])) 15 | # end 16 | f.write("\n") 17 | for block in range(nBlocks): 18 | for k in range(nz[block]): 19 | for j in range(ny[block]): 20 | for i in range(nx[block]): 21 | f.write("%f " % points[block][i, j, k, 0]) 22 | # end 23 | # end 24 | # end 25 | f.write("\n") 26 | 27 | for k in range(nz[block]): 28 | for j in range(ny[block]): 29 | for i in range(nx[block]): 30 | f.write("%f " % points[block][i, j, k, 1]) 31 | # end 32 | # end 33 | # end 34 | f.write("\n") 35 | 36 | for k in range(nz[block]): 37 | for j in range(ny[block]): 38 | for i in range(nx[block]): 39 | f.write("%f " % points[block][i, j, k, 2]) 40 | # end 41 | # end 42 | # end 43 | # end 44 | f.close() 45 | 46 | 47 | def returnBlockPoints(corners, nx, ny, nz): 48 | """ 49 | corners needs to be 8 x 3 50 | """ 51 | points = np.zeros([nx, ny, nz, 3]) 52 | 53 | # points 1 - 4 are the iMin face 54 | # points 5 - 8 are the iMax face 55 | 56 | for idim in range(3): 57 | edge1 = np.linspace(corners[0][idim], corners[4][idim], nx) 58 | edge2 = np.linspace(corners[1][idim], corners[5][idim], nx) 59 | edge3 = np.linspace(corners[2][idim], corners[6][idim], nx) 60 | edge4 = np.linspace(corners[3][idim], corners[7][idim], nx) 61 | 62 | for i in range(nx): 63 | edge5 = np.linspace(edge1[i], edge3[i], ny) 64 | edge6 = np.linspace(edge2[i], edge4[i], ny) 65 | for j in range(ny): 66 | edge7 = np.linspace(edge5[j], edge6[j], nz) 67 | points[i, j, :, idim] = edge7 68 | # end 69 | # end 70 | # end 71 | 72 | return points 73 | 74 | 75 | nBlocks = 2 76 | 77 | nx = [2, 2] # 4 78 | ny = [2, 2] 79 | nz = [2, 2] 80 | 81 | corners = np.zeros([nBlocks, 8, 3]) 82 | 83 | corners[0, 0, :] = [-1.0, -1.0, -1.0] 84 | corners[0, 1, :] = [-1.0, -1.0, 1.0] 85 | corners[0, 2, :] = [-1.0, 1.0, -1.0] 86 | corners[0, 3, :] = [-1.0, 1.0, 1] 87 | corners[0, 4, :] = [1.0, -1.0, -1.0] 88 | corners[0, 5, :] = [1.0, -1.0, 1.0] 89 | corners[0, 6, :] = [1.0, 1.0, -1.0] 90 | corners[0, 7, :] = [1.0, 1.0, 1.0] 91 | 92 | corners[1, 0, :] = [1.0, -1.0, -1.0] 93 | corners[1, 1, :] = [1.0, -1.0, 1.0] 94 | corners[1, 2, :] = [1.0, 1.0, -1.0] 95 | corners[1, 3, :] = [1.0, 1.0, 1.0] 96 | corners[1, 4, :] = [2.0, -1.0, -1.0] 97 | corners[1, 5, :] = [2.0, -1.0, 1.0] 98 | corners[1, 6, :] = [2.0, 1.0, -1.0] 99 | corners[1, 7, :] = [2.0, 1.0, 1.0] 100 | 101 | points = [] 102 | 103 | for i in range(nBlocks): 104 | points.append(returnBlockPoints(corners[i], nx[i], ny[i], nz[i])) 105 | # end 106 | 107 | fileName = "outerBoxFFD.xyz" 108 | writeFFDFile(fileName, nBlocks, nx, ny, nz, points) 109 | 110 | 111 | nBlocks = 1 # 3 112 | 113 | nx = [5] 114 | ny = [5] 115 | nz = [5] 116 | 117 | corners = np.zeros([nBlocks, 8, 3]) 118 | 119 | corners[0, 0, :] = [-0.5, -0.5, -0.5] 120 | corners[0, 1, :] = [-0.5, 0.5, -0.5] 121 | corners[0, 2, :] = [-0.5, -0.5, 0.5] 122 | corners[0, 3, :] = [-0.5, 0.5, 0.5] 123 | corners[0, 4, :] = [0.5, -0.5, -0.5] 124 | corners[0, 5, :] = [0.5, 0.5, -0.5] 125 | corners[0, 6, :] = [0.5, -0.5, 0.5] 126 | corners[0, 7, :] = [0.5, 0.5, 0.5] 127 | 128 | points = [] 129 | for block in range(nBlocks): 130 | points.append(returnBlockPoints(corners[block], nx[block], ny[block], nz[block])) 131 | 132 | 133 | fileName = "innerFFD.xyz" 134 | writeFFDFile(fileName, nBlocks, nx, ny, nz, points) 135 | nBlocks = 1 # 3 136 | 137 | nx = [2] 138 | ny = [2] 139 | nz = [2] 140 | 141 | corners = np.zeros([nBlocks, 8, 3]) 142 | 143 | corners[0, 0, :] = [-0.5, -0.5, -0.5] 144 | corners[0, 1, :] = [-0.5, 0.5, -0.5] 145 | corners[0, 2, :] = [-0.5, -0.5, 0.5] 146 | corners[0, 3, :] = [-0.5, 0.5, 0.5] 147 | corners[0, 4, :] = [0.5, -0.5, -0.5] 148 | corners[0, 5, :] = [0.5, 0.5, -0.5] 149 | corners[0, 6, :] = [0.5, -0.5, 0.5] 150 | corners[0, 7, :] = [0.5, 0.5, 0.5] 151 | 152 | points = [] 153 | for block in range(nBlocks): 154 | points.append(returnBlockPoints(corners[block], nx[block], ny[block], nz[block])) 155 | 156 | 157 | fileName = "simpleInnerFFD.xyz" 158 | writeFFDFile(fileName, nBlocks, nx, ny, nz, points) 159 | -------------------------------------------------------------------------------- /examples/ffd/generatec172FFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # rst Dimensions 5 | # Bounding box for root airfoil 6 | x_root_range = [-0.05, 1.7] 7 | y_root_range = [-0.25, 0.25] 8 | z_root = -0.01 9 | 10 | # Bounding box for tip airfoil 11 | x_tip_range = [-0.05, 1.7] 12 | y_tip_range = [-0.25, 0.25] 13 | z_tip = 11 / 2 14 | 15 | # Number of FFD control points per dimension 16 | nX = 4 # streamwise 17 | nY = 2 # perpendicular to wing planform 18 | nZ = 4 # spanwise 19 | # rst Compute 20 | # Compute grid points 21 | span_dist = np.linspace(0, 1, nZ) ** 0.8 22 | z_sections = span_dist * (z_tip - z_root) + z_root 23 | 24 | x_te = span_dist * (x_tip_range[0] - x_root_range[0]) + x_root_range[0] 25 | x_le = span_dist * (x_tip_range[1] - x_root_range[1]) + x_root_range[1] 26 | y_coords = np.vstack( 27 | ( 28 | span_dist * (y_tip_range[0] - y_root_range[0]) + y_root_range[0], 29 | span_dist * (y_tip_range[1] - y_root_range[1]) + y_root_range[1], 30 | ) 31 | ) 32 | 33 | X = np.zeros((nY * nZ, nX)) 34 | Y = np.zeros((nY * nZ, nX)) 35 | Z = np.zeros((nY * nZ, nX)) 36 | row = 0 37 | for k in range(nZ): 38 | for j in range(nY): 39 | X[row, :] = np.linspace(x_te[k], x_le[k], nX) 40 | Y[row, :] = np.ones(nX) * y_coords[j, k] 41 | Z[row, :] = np.ones(nX) * z_sections[k] 42 | row += 1 43 | # rst Write 44 | # Write FFD to file 45 | filename = "c172.xyz" 46 | f = open(filename, "w") 47 | f.write("\t\t1\n") 48 | f.write("\t\t%d\t\t%d\t\t%d\n" % (nX, nY, nZ)) 49 | for i in [X, Y, Z]: 50 | for row in i: 51 | vals = tuple(row) 52 | f.write("\t%3.8f\t%3.8f\t%3.8f\t%3.8f\n" % vals) 53 | f.close() 54 | -------------------------------------------------------------------------------- /examples/ffd/generaterectFFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # rst Dimensions 5 | # Bounding box for root airfoil 6 | x_root_range = [-1.0, 1.0] 7 | y_root_range = [-0.5, 0.5] 8 | z_root = 0.0 9 | 10 | # Bounding box for tip airfoil 11 | x_tip_range = [-1.0, 1.0] 12 | y_tip_range = [-0.5, 0.5] 13 | z_tip = 8.0 14 | 15 | # Number of FFD control points per dimension 16 | nX = 4 # streamwise 17 | nY = 2 # perpendicular to wing planform 18 | nZ = 4 # spanwise 19 | # rst Compute 20 | # Compute grid points 21 | span_dist = np.linspace(0, 1, nZ) ** 0.8 22 | z_sections = span_dist * (z_tip - z_root) + z_root 23 | 24 | x_te = span_dist * (x_tip_range[0] - x_root_range[0]) + x_root_range[0] 25 | x_le = span_dist * (x_tip_range[1] - x_root_range[1]) + x_root_range[1] 26 | y_coords = np.vstack( 27 | ( 28 | span_dist * (y_tip_range[0] - y_root_range[0]) + y_root_range[0], 29 | span_dist * (y_tip_range[1] - y_root_range[1]) + y_root_range[1], 30 | ) 31 | ) 32 | 33 | X = np.zeros((nY * nZ, nX)) 34 | Y = np.zeros((nY * nZ, nX)) 35 | Z = np.zeros((nY * nZ, nX)) 36 | row = 0 37 | for k in range(nZ): 38 | for j in range(nY): 39 | X[row, :] = np.linspace(x_te[k], x_le[k], nX) 40 | Y[row, :] = np.ones(nX) * y_coords[j, k] 41 | Z[row, :] = np.ones(nX) * z_sections[k] 42 | row += 1 43 | # rst Write 44 | # Write FFD to file 45 | filename = "2x1x8_rectangle.xyz" 46 | f = open(filename, "w") 47 | f.write("\t\t1\n") 48 | f.write("\t\t%d\t\t%d\t\t%d\n" % (nX, nY, nZ)) 49 | for i in [X, Y, Z]: 50 | for row in i: 51 | vals = tuple(row) 52 | f.write("\t%3.8f\t%3.8f\t%3.8f\t%3.8f\n" % vals) 53 | f.close() 54 | -------------------------------------------------------------------------------- /examples/ffd_cylinder/cylinder.csm: -------------------------------------------------------------------------------- 1 | # cylinder.csm written by ocsmSave (v1.19) 2 | 3 | # Constant, Design, and Output Parameters: 4 | 5 | # Global Attributes: 6 | 7 | # Branches: 8 | cylinder 0.5 0.0 0.001 0.5 0.0 0.999 0.4999 9 | 10 | end 11 | -------------------------------------------------------------------------------- /examples/ffd_cylinder/genFFD.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | nffd = 10 5 | FFDbox = np.zeros((nffd, 2, 2, 3)) 6 | xslice = np.zeros(nffd) 7 | yupper = np.zeros(nffd) 8 | ylower = np.zeros(nffd) 9 | 10 | xmargin = 0.001 11 | ymargin = 0.02 12 | yu = 0.5 13 | yl = -0.5 14 | 15 | # construct the i-j (x-y) plane grid of control points 10 x 2 16 | # we'll copy this along the k (z) axis later to make a cube 17 | for i in range(nffd): 18 | xtemp = i * 1.0 / (nffd - 1.0) 19 | xslice[i] = -1.0 * xmargin + (1 + 2.0 * xmargin) * xtemp 20 | yupper[i] = yu + ymargin 21 | ylower[i] = yl - ymargin 22 | 23 | # create the FFD box topology 24 | # 1st dim = plot3d i dimension 25 | # 2nd = plot3d j dimension 26 | # 3rd = plot3d k dimension 27 | # 4th = xyz coordinate dimension 28 | # the result is a three-axis tensor of points in R3 29 | FFDbox[:, 0, 0, 0] = xslice[:].copy() 30 | FFDbox[:, 1, 0, 0] = xslice[:].copy() 31 | # Y 32 | # lower 33 | FFDbox[:, 0, 0, 1] = ylower[:].copy() 34 | # upper 35 | FFDbox[:, 1, 0, 1] = yupper[:].copy() 36 | # copy 37 | FFDbox[:, :, 1, :] = FFDbox[:, :, 0, :].copy() 38 | # Z 39 | FFDbox[:, :, 0, 2] = 0.0 40 | # Z 41 | FFDbox[:, :, 1, 2] = 1.0 42 | 43 | # write the result to disk in plot3d format 44 | # the i dimension is on the rows 45 | # j and k are on newlines 46 | # k changes slower than j 47 | # xyz changes slowest of all 48 | f = open("ffdbox.xyz", "w") 49 | f.write("1\n") 50 | # header row with block topology n x 2 x 2 51 | f.write(str(nffd) + " 2 2\n") 52 | for ell in range(3): 53 | for k in range(2): 54 | for j in range(2): 55 | for i in range(nffd): 56 | f.write("%.15f " % (FFDbox[i, j, k, ell])) 57 | f.write("\n") 58 | f.close() 59 | -------------------------------------------------------------------------------- /examples/ffd_cylinder/images/cyl_embedded_deformed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/ffd_cylinder/images/cyl_embedded_deformed.png -------------------------------------------------------------------------------- /examples/ffd_cylinder/images/cyl_embedded_undeformed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/ffd_cylinder/images/cyl_embedded_undeformed.png -------------------------------------------------------------------------------- /examples/ffd_cylinder/images/cylinder_ffd_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/ffd_cylinder/images/cylinder_ffd_3d.png -------------------------------------------------------------------------------- /examples/ffd_cylinder/images/cylinder_only_3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/ffd_cylinder/images/cylinder_only_3d.png -------------------------------------------------------------------------------- /examples/ffd_cylinder/images/deformed_cylinder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/examples/ffd_cylinder/images/deformed_cylinder.png -------------------------------------------------------------------------------- /examples/ffd_cylinder/tecplot_layouts/README.md: -------------------------------------------------------------------------------- 1 | These Tecplot layouts were used to generate the visualizations in the documentation. 2 | Copy them into the parent folder containing "runFFDExample.py" and run that script, which will generate the required geometry files. -------------------------------------------------------------------------------- /input_files/e63.dat: -------------------------------------------------------------------------------- 1 | 1.000000 0.000100 2 | 0.997190 0.001210 3 | 0.989380 0.004730 4 | 0.977510 0.009860 5 | 0.961730 0.015530 6 | 0.941640 0.021260 7 | 0.917170 0.027090 8 | 0.888610 0.033010 9 | 0.856240 0.038850 10 | 0.820390 0.044510 11 | 0.781410 0.049850 12 | 0.739680 0.054800 13 | 0.695620 0.059210 14 | 0.649670 0.063040 15 | 0.602290 0.066170 16 | 0.553940 0.068570 17 | 0.505090 0.070160 18 | 0.456240 0.070940 19 | 0.407860 0.070840 20 | 0.360430 0.069900 21 | 0.314410 0.068090 22 | 0.270260 0.065450 23 | 0.228400 0.061980 24 | 0.189200 0.057750 25 | 0.153040 0.052800 26 | 0.120230 0.047230 27 | 0.091030 0.041110 28 | 0.065680 0.034570 29 | 0.044350 0.027750 30 | 0.027140 0.020830 31 | 0.014160 0.014040 32 | 0.005360 0.007660 33 | 0.000760 0.002180 34 | 0.000550 -0.001410 35 | 0.005570 -0.003060 36 | 0.016510 -0.003300 37 | 0.033160 -0.002270 38 | 0.055500 -0.000040 39 | 0.083420 0.003150 40 | 0.116710 0.007080 41 | 0.155040 0.011510 42 | 0.198000 0.016200 43 | 0.245090 0.020930 44 | 0.295740 0.025460 45 | 0.349310 0.029620 46 | 0.405130 0.033190 47 | 0.462470 0.036050 48 | 0.520560 0.038030 49 | 0.578590 0.039070 50 | 0.635760 0.039070 51 | 0.691250 0.038060 52 | 0.744300 0.036040 53 | 0.794140 0.033100 54 | 0.840040 0.029300 55 | 0.881320 0.024820 56 | 0.917350 0.019790 57 | 0.947560 0.014390 58 | 0.971150 0.008870 59 | 0.987540 0.004100 60 | 0.996950 0.001020 61 | 1.000000 -0.000100 62 | -------------------------------------------------------------------------------- /input_files/get-input-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this file will download the input files for pyGeo regression tests 3 | # and examples and extract them to the right place. 4 | 5 | DIR=$(dirname $0) 6 | wget -O $DIR/input_files.tar.gz https://websites.umich.edu/~mdolaboratory/repo_files/pyGeo/pygeo_input_files.tar.gz 7 | tar -xzf $DIR/input_files.tar.gz -C $DIR/../ 8 | -------------------------------------------------------------------------------- /input_files/naca0012_sharp.dat: -------------------------------------------------------------------------------- 1 | 1.000000 0.0000000E-02 2 | 0.9556484 0.7317189E-02 3 | 0.9402486 0.9345839E-02 4 | 0.9242123 0.1141940E-01 5 | 0.9078338 0.1349729E-01 6 | 0.8912799 0.1555747E-01 7 | 0.8746367 0.1758921E-01 8 | 0.8579464 0.1958767E-01 9 | 0.8412299 0.2155079E-01 10 | 0.8244974 0.2347775E-01 11 | 0.8077546 0.2536826E-01 12 | 0.7910046 0.2722215E-01 13 | 0.7742495 0.2903929E-01 14 | 0.7574910 0.3081946E-01 15 | 0.7407304 0.3256231E-01 16 | 0.7239692 0.3426741E-01 17 | 0.7072086 0.3593415E-01 18 | 0.6904498 0.3756179E-01 19 | 0.6736942 0.3914944E-01 20 | 0.6569430 0.4069607E-01 21 | 0.6401976 0.4220048E-01 22 | 0.6234593 0.4366132E-01 23 | 0.6067296 0.4507707E-01 24 | 0.5900098 0.4644604E-01 25 | 0.5733015 0.4776639E-01 26 | 0.5566061 0.4903607E-01 27 | 0.5399254 0.5025287E-01 28 | 0.5232609 0.5141440E-01 29 | 0.5066145 0.5251805E-01 30 | 0.4899881 0.5356103E-01 31 | 0.4733836 0.5454034E-01 32 | 0.4568033 0.5545275E-01 33 | 0.4402494 0.5629481E-01 34 | 0.4237245 0.5706282E-01 35 | 0.4072312 0.5775284E-01 36 | 0.3907728 0.5836065E-01 37 | 0.3743523 0.5888176E-01 38 | 0.3579737 0.5931135E-01 39 | 0.3416411 0.5964430E-01 40 | 0.3253592 0.5987512E-01 41 | 0.3091334 0.5999796E-01 42 | 0.2929700 0.6000654E-01 43 | 0.2768762 0.5989415E-01 44 | 0.2608607 0.5965360E-01 45 | 0.2449337 0.5927721E-01 46 | 0.2291078 0.5875675E-01 47 | 0.2133984 0.5808348E-01 48 | 0.1978253 0.5724818E-01 49 | 0.1824140 0.5624130E-01 50 | 0.1671985 0.5505334E-01 51 | 0.1522253 0.5367551E-01 52 | 0.1375585 0.5210105E-01 53 | 0.1232868 0.5032750E-01 54 | 0.1095300 0.4836006E-01 55 | 0.9644040E-01 0.4621587E-01 56 | 0.8419250E-01 0.4392743E-01 57 | 0.7295464E-01 0.4154215E-01 58 | 0.6284856E-01 0.3911578E-01 59 | 0.5391754E-01 0.3670115E-01 60 | 0.4612202E-01 0.3433829E-01 61 | 0.3936201E-01 0.3205037E-01 62 | 0.3350894E-01 0.2984552E-01 63 | 0.2843159E-01 0.2772130E-01 64 | 0.2401083E-01 0.2566907E-01 65 | 0.2014561E-01 0.2367712E-01 66 | 0.1675353E-01 0.2173266E-01 67 | 0.1376927E-01 0.1982291E-01 68 | 0.1114227E-01 0.1793562E-01 69 | 0.8834520E-02 0.1605954E-01 70 | 0.6818652E-02 0.1418471E-01 71 | 0.5076404E-02 0.1230310E-01 72 | 0.3597085E-02 0.1040943E-01 73 | 0.2375947E-02 0.8502398E-02 74 | 0.1411548E-02 0.6585675E-02 75 | 0.7030324E-03 0.4670019E-02 76 | 0.2448913E-03 0.2768967E-02 77 | 0.2616688E-04 0.9084721E-03 78 | 0.2616688E-04 -0.9084721E-03 79 | 0.2448913E-03 -0.2768967E-02 80 | 0.7030324E-03 -0.4670019E-02 81 | 0.1411548E-02 -0.6585675E-02 82 | 0.2375947E-02 -0.8502398E-02 83 | 0.3597085E-02 -0.1040943E-01 84 | 0.5076404E-02 -0.1230310E-01 85 | 0.6818652E-02 -0.1418471E-01 86 | 0.8834520E-02 -0.1605954E-01 87 | 0.1114227E-01 -0.1793562E-01 88 | 0.1376927E-01 -0.1982291E-01 89 | 0.1675353E-01 -0.2173266E-01 90 | 0.2014561E-01 -0.2367712E-01 91 | 0.2401083E-01 -0.2566907E-01 92 | 0.2843159E-01 -0.2772130E-01 93 | 0.3350894E-01 -0.2984552E-01 94 | 0.3936201E-01 -0.3205037E-01 95 | 0.4612202E-01 -0.3433829E-01 96 | 0.5391754E-01 -0.3670115E-01 97 | 0.6284856E-01 -0.3911578E-01 98 | 0.7295464E-01 -0.4154215E-01 99 | 0.8419250E-01 -0.4392743E-01 100 | 0.9644040E-01 -0.4621587E-01 101 | 0.1095300 -0.4836006E-01 102 | 0.1232868 -0.5032750E-01 103 | 0.1375585 -0.5210105E-01 104 | 0.1522253 -0.5367551E-01 105 | 0.1671985 -0.5505334E-01 106 | 0.1824140 -0.5624130E-01 107 | 0.1978253 -0.5724818E-01 108 | 0.2133984 -0.5808348E-01 109 | 0.2291078 -0.5875675E-01 110 | 0.2449337 -0.5927721E-01 111 | 0.2608607 -0.5965360E-01 112 | 0.2768762 -0.5989415E-01 113 | 0.2929700 -0.6000654E-01 114 | 0.3091334 -0.5999796E-01 115 | 0.3253592 -0.5987512E-01 116 | 0.3416411 -0.5964430E-01 117 | 0.3579737 -0.5931135E-01 118 | 0.3743523 -0.5888176E-01 119 | 0.3907728 -0.5836065E-01 120 | 0.4072312 -0.5775284E-01 121 | 0.4237245 -0.5706282E-01 122 | 0.4402494 -0.5629481E-01 123 | 0.4568033 -0.5545275E-01 124 | 0.4733836 -0.5454034E-01 125 | 0.4899881 -0.5356103E-01 126 | 0.5066145 -0.5251805E-01 127 | 0.5232609 -0.5141440E-01 128 | 0.5399254 -0.5025287E-01 129 | 0.5566061 -0.4903607E-01 130 | 0.5733015 -0.4776639E-01 131 | 0.5900098 -0.4644604E-01 132 | 0.6067296 -0.4507707E-01 133 | 0.6234593 -0.4366132E-01 134 | 0.6401976 -0.4220048E-01 135 | 0.6569430 -0.4069607E-01 136 | 0.6736942 -0.3914944E-01 137 | 0.6904498 -0.3756179E-01 138 | 0.7072086 -0.3593415E-01 139 | 0.7239692 -0.3426741E-01 140 | 0.7407304 -0.3256231E-01 141 | 0.7574910 -0.3081946E-01 142 | 0.7742495 -0.2903929E-01 143 | 0.7910046 -0.2722215E-01 144 | 0.8077546 -0.2536826E-01 145 | 0.8244974 -0.2347775E-01 146 | 0.8412299 -0.2155079E-01 147 | 0.8579464 -0.1958767E-01 148 | 0.8746367 -0.1758921E-01 149 | 0.8912799 -0.1555747E-01 150 | 0.9078338 -0.1349729E-01 151 | 0.9242123 -0.1141940E-01 152 | 0.9402486 -0.9345839E-02 153 | 0.9556484 -0.7317189E-02 154 | -------------------------------------------------------------------------------- /paper/Global-FFD-DV-Demo.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import copy 3 | 4 | # External modules 5 | import numpy as np 6 | 7 | # First party modules 8 | from pygeo import DVGeometry 9 | 10 | FFDFile = "ffd.xyz" 11 | DVGeo = DVGeometry(FFDFile) 12 | 13 | nRefAxPts = DVGeo.addRefAxis("wing", xFraction=0.25, alignIndex="k") 14 | 15 | 16 | def dihedral(val, geo): 17 | C = geo.extractCoef("wing") 18 | for i in range(1, nRefAxPts): 19 | C[i, 1] += val[i - 1] 20 | geo.restoreCoef(C, "wing") 21 | 22 | 23 | # rst Twist 24 | def twist(val, geo): 25 | for i in range(1, nRefAxPts): 26 | geo.rot_z["wing"].coef[i] = val[i - 1] 27 | 28 | 29 | # rst Taper 30 | def taper(val, geo): 31 | s = geo.extractS("wing") 32 | slope = (val[1] - val[0]) / (s[-1] - s[0]) 33 | for i in range(nRefAxPts): 34 | geo.scale_x["wing"].coef[i] = slope * (s[i] - s[0]) + val[0] 35 | 36 | 37 | nTwist = nRefAxPts - 1 38 | DVGeo.addGlobalDV(dvName="dihedral", value=[0] * nTwist, func=dihedral, lower=-10, upper=10, scale=1) 39 | DVGeo.addGlobalDV(dvName="twist", value=[0] * nTwist, func=twist, lower=-10, upper=10, scale=1) 40 | DVGeo.addGlobalDV(dvName="taper", value=[1] * 2, func=taper, lower=0.5, upper=1.5, scale=1) 41 | 42 | # Comment out one or the other 43 | DVGeo.addLocalDV("local", lower=-0.5, upper=0.5, axis="y", scale=1) 44 | 45 | dvDict = DVGeo.getValues() 46 | dvDictCopy = copy.deepcopy(dvDict) 47 | dvDictCopy["twist"] = np.linspace(0, 50, nRefAxPts)[1:] 48 | DVGeo.setDesignVars(dvDictCopy) 49 | DVGeo.writePlot3d("ffd_deformed-twist.xyz") 50 | 51 | dvDictCopy = copy.deepcopy(dvDict) 52 | dvDictCopy["dihedral"] = np.linspace(0, 3, nRefAxPts)[1:] 53 | DVGeo.setDesignVars(dvDictCopy) 54 | DVGeo.writePlot3d("ffd_deformed-dihedral.xyz") 55 | 56 | dvDictCopy = copy.deepcopy(dvDict) 57 | dvDictCopy["taper"] = np.array([1.2, 0.5]) 58 | DVGeo.setDesignVars(dvDictCopy) 59 | DVGeo.writePlot3d("ffd_deformed-taper.xyz") 60 | -------------------------------------------------------------------------------- /paper/child_ffd.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/child_ffd.pdf -------------------------------------------------------------------------------- /paper/constraints_3d.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/constraints_3d.pdf -------------------------------------------------------------------------------- /paper/cst_example.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/cst_example.pdf -------------------------------------------------------------------------------- /paper/cst_example.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | 4 | # External modules 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | 8 | # First party modules 9 | from pygeo.parameterization.DVGeoCST import DVGeometryCST 10 | 11 | cur_dir = os.path.dirname(os.path.abspath(__file__)) 12 | out_file = os.path.join(cur_dir, "cst_example.pdf") 13 | 14 | # Airfoil shape inputs 15 | x = np.linspace(0, 1, 10000) 16 | N1 = 0.5 17 | N2 = 1.0 18 | yte = 0.0 19 | num_cst = 3 20 | coeff = [ 21 | {"upper": np.full(num_cst, 0.5), "lower": np.full(num_cst, 0.5)}, 22 | {"upper": np.array([1.5, 0.5, 0.5]), "lower": np.full(num_cst, 0.5)}, 23 | ] 24 | 25 | # Line styles 26 | colors = ["#0CAAEF", "#003268"] 27 | poly_linewidth = 0.8 28 | alpha = 0.5 29 | 30 | fig, axs = plt.subplots(1, 2, figsize=[5.5, 2]) 31 | 32 | for i_foil, c in enumerate(coeff): 33 | for surf, color in zip(["upper", "lower"], colors): 34 | for i in range(num_cst): 35 | w = np.zeros(num_cst) 36 | w[i] = c[surf][i] 37 | y = DVGeometryCST.computeCSTCoordinates(x, N1, N2, w, yte) 38 | y = -y if surf == "lower" else y 39 | axs[i_foil].plot(x, y, color=color, linewidth=poly_linewidth, alpha=alpha) 40 | 41 | y = DVGeometryCST.computeCSTCoordinates(x, N1, N2, c[surf], yte) 42 | y = -y if surf == "lower" else y 43 | axs[i_foil].plot(x, y, color=color) 44 | 45 | axs[i_foil].set_aspect("equal") 46 | axs[i_foil].axis("off") 47 | axs[i_foil].set_ylim([-0.2, 0.45]) 48 | 49 | plt.subplots_adjust(left=0, right=1, bottom=0, top=1, wspace=0.0) 50 | 51 | fig.savefig(out_file) 52 | -------------------------------------------------------------------------------- /paper/esp_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/esp_example.png -------------------------------------------------------------------------------- /paper/ffd.xyz: -------------------------------------------------------------------------------- 1 | 1 2 | 6 2 8 3 | -0.10000000 0.94000000 1.98000000 3.02000000 4.06000000 5.10000000 4 | -0.10000000 0.94000000 1.98000000 3.02000000 4.06000000 5.10000000 5 | 1.48118553 2.37782471 3.27446389 4.17110307 5.06774224 5.96438142 6 | 1.48118553 2.37782471 3.27446389 4.17110307 5.06774224 5.96438142 7 | 2.65300391 3.44339822 4.23379253 5.02418685 5.81458116 6.60497547 8 | 2.65300391 3.44339822 4.23379253 5.02418685 5.81458116 6.60497547 9 | 3.70785003 4.40260496 5.09735989 5.79211482 6.48686975 7.18162468 10 | 3.70785003 4.40260496 5.09735989 5.79211482 6.48686975 7.18162468 11 | 4.69325821 5.29866946 5.90408072 6.50949197 7.11490323 7.72031449 12 | 4.69325821 5.29866946 5.90408072 6.50949197 7.11490323 7.72031449 13 | 5.63005558 6.15053054 6.67100550 7.19148047 7.71195543 8.23243039 14 | 5.63005558 6.15053054 6.67100550 7.19148047 7.71195543 8.23243039 15 | 6.52985197 6.96874539 7.40763881 7.84653223 8.28542566 8.72431908 16 | 6.52985197 6.96874539 7.40763881 7.84653223 8.28542566 8.72431908 17 | 7.40000000 7.76000000 8.12000000 8.48000000 8.84000000 9.20000000 18 | 7.40000000 7.76000000 8.12000000 8.48000000 8.84000000 9.20000000 19 | -0.50000000 -0.50000000 -0.50000000 -0.50000000 -0.50000000 -0.50000000 20 | 0.50000000 0.50000000 0.50000000 0.50000000 0.50000000 0.50000000 21 | -0.44729382 -0.44729382 -0.44729382 -0.44729382 -0.44729382 -0.44729382 22 | 0.44729382 0.44729382 0.44729382 0.44729382 0.44729382 0.44729382 23 | -0.40823320 -0.40823320 -0.40823320 -0.40823320 -0.40823320 -0.40823320 24 | 0.40823320 0.40823320 0.40823320 0.40823320 0.40823320 0.40823320 25 | -0.37307167 -0.37307167 -0.37307167 -0.37307167 -0.37307167 -0.37307167 26 | 0.37307167 0.37307167 0.37307167 0.37307167 0.37307167 0.37307167 27 | -0.34022473 -0.34022473 -0.34022473 -0.34022473 -0.34022473 -0.34022473 28 | 0.34022473 0.34022473 0.34022473 0.34022473 0.34022473 0.34022473 29 | -0.30899815 -0.30899815 -0.30899815 -0.30899815 -0.30899815 -0.30899815 30 | 0.30899815 0.30899815 0.30899815 0.30899815 0.30899815 0.30899815 31 | -0.27900493 -0.27900493 -0.27900493 -0.27900493 -0.27900493 -0.27900493 32 | 0.27900493 0.27900493 0.27900493 0.27900493 0.27900493 0.27900493 33 | -0.25000000 -0.25000000 -0.25000000 -0.25000000 -0.25000000 -0.25000000 34 | 0.25000000 0.25000000 0.25000000 0.25000000 0.25000000 0.25000000 35 | -0.01000000 -0.01000000 -0.01000000 -0.01000000 -0.01000000 -0.01000000 36 | -0.01000000 -0.01000000 -0.01000000 -0.01000000 -0.01000000 -0.01000000 37 | 2.99636075 2.99636075 2.99636075 2.99636075 2.99636075 2.99636075 38 | 2.99636075 2.99636075 2.99636075 2.99636075 2.99636075 2.99636075 39 | 5.22437810 5.22437810 5.22437810 5.22437810 5.22437810 5.22437810 40 | 5.22437810 5.22437810 5.22437810 5.22437810 5.22437810 5.22437810 41 | 7.22999218 7.22999218 7.22999218 7.22999218 7.22999218 7.22999218 42 | 7.22999218 7.22999218 7.22999218 7.22999218 7.22999218 7.22999218 43 | 9.10358160 9.10358160 9.10358160 9.10358160 9.10358160 9.10358160 44 | 9.10358160 9.10358160 9.10358160 9.10358160 9.10358160 9.10358160 45 | 10.88474568 10.88474568 10.88474568 10.88474568 10.88474568 10.88474568 46 | 10.88474568 10.88474568 10.88474568 10.88474568 10.88474568 10.88474568 47 | 12.59555855 12.59555855 12.59555855 12.59555855 12.59555855 12.59555855 48 | 12.59555855 12.59555855 12.59555855 12.59555855 12.59555855 12.59555855 49 | 14.25000000 14.25000000 14.25000000 14.25000000 14.25000000 14.25000000 50 | 14.25000000 14.25000000 14.25000000 14.25000000 14.25000000 14.25000000 51 | -------------------------------------------------------------------------------- /paper/ffd_dvs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/ffd_dvs.pdf -------------------------------------------------------------------------------- /paper/ffd_multi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/ffd_multi.png -------------------------------------------------------------------------------- /paper/trisurfcon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/trisurfcon.pdf -------------------------------------------------------------------------------- /paper/vsp_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdolab/pygeo/e2cdcbd567d4ebe4f22f64281a23da4dc0fa7a3c/paper/vsp_example.png -------------------------------------------------------------------------------- /pygeo/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.16.0" 2 | 3 | from .pyNetwork import pyNetwork 4 | from .pyGeo import pyGeo 5 | from .pyBlock import pyBlock 6 | from .constraints import DVConstraints 7 | from .parameterization import DVGeometry 8 | from .parameterization import DVGeometryAxi 9 | from .parameterization import DVGeometryCST 10 | from .parameterization import DVGeometryVSP 11 | from .parameterization import DVGeometryESP 12 | from .parameterization import DVGeometryMulti 13 | -------------------------------------------------------------------------------- /pygeo/constraints/__init__.py: -------------------------------------------------------------------------------- 1 | from .DVCon import DVConstraints 2 | -------------------------------------------------------------------------------- /pygeo/constraints/gearPostConstraint.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # Local modules 5 | from .. import geo_utils 6 | from .baseConstraint import GeometricConstraint 7 | 8 | 9 | class GearPostConstraint(GeometricConstraint): 10 | """ 11 | This class is used to represet a single volume constraint. The 12 | parameter list is explained in the addVolumeConstaint() of 13 | the DVConstraints class 14 | """ 15 | 16 | def __init__( 17 | self, 18 | name, 19 | wimpressCalc, 20 | up, 21 | down, 22 | thickLower, 23 | thickUpper, 24 | thickScaled, 25 | MACFracLower, 26 | MACFracUpper, 27 | DVGeo, 28 | addToPyOpt, 29 | compNames, 30 | ): 31 | super().__init__(name, None, None, None, None, DVGeo, addToPyOpt) 32 | 33 | self.wimpress = wimpressCalc 34 | self.thickLower = thickLower 35 | self.thickUpper = thickUpper 36 | self.thickScaled = thickScaled 37 | self.MACFracLower = MACFracLower 38 | self.MACFracUpper = MACFracUpper 39 | self.coords = np.array([up, down]) 40 | 41 | # First thing we can do is embed the coordinates into DVGeo 42 | # with the name provided: 43 | self.DVGeo.addPointSet(self.coords, self.name, compNames=compNames) 44 | 45 | # Compute the reference length 46 | self.D0 = np.linalg.norm(self.coords[0] - self.coords[1]) 47 | 48 | def evalFunctions(self, funcs, config): 49 | # Update the gear post locations 50 | self.coords = self.DVGeo.update(self.name, config=config) 51 | 52 | # Compute the thickness constraint 53 | D = np.linalg.norm(self.coords[0] - self.coords[1]) 54 | if self.thickScaled: 55 | D = D / self.D0 56 | 57 | # Compute the values we need from the wimpress calc 58 | wfuncs = {} 59 | self.wimpress.evalFunctions(wfuncs) 60 | 61 | # Now the constraint value is 62 | postLoc = 0.5 * (self.coords[0, 0] + self.coords[1, 0]) 63 | locCon = (postLoc - wfuncs["xLEMAC"]) / wfuncs["MAC"] 64 | 65 | # Final set of two constrains 66 | funcs[self.name + "_thick"] = D 67 | funcs[self.name + "_MAC"] = locCon 68 | 69 | def evalFunctionsSens(self, funcsSens, config): 70 | """ 71 | Evaluate the sensitivity of the functions this object has and 72 | place in the funcsSens dictionary 73 | 74 | Parameters 75 | ---------- 76 | funcsSens : dict 77 | Dictionary to place function values 78 | """ 79 | nDV = self.DVGeo.getNDV() 80 | if nDV > 0: 81 | wfuncs = {} 82 | self.wimpress.evalFunctions(wfuncs) 83 | 84 | wSens = {} 85 | self.wimpress.evalFunctionsSens(wSens) 86 | 87 | # Accumulate the derivative into p1b and p2b 88 | p1b, p2b = geo_utils.eDist_b(self.coords[0, :], self.coords[1, :]) 89 | if self.thickScaled: 90 | p1b /= self.D0 91 | p2b /= self.D0 92 | 93 | funcsSens[self.name + "_thick"] = self.DVGeo.totalSensitivity( 94 | np.array([[p1b, p2b]]), self.name, config=config 95 | ) 96 | 97 | # And now we need the sensitivity of the conLoc calc 98 | p1b[:] = 0 99 | p2b[:] = 0 100 | p1b[0] += 0.5 / wfuncs["MAC"] 101 | p2b[0] += 0.5 / wfuncs["MAC"] 102 | 103 | tmpSens = self.DVGeo.totalSensitivity(np.array([[p1b, p2b]]), self.name, config=config) 104 | 105 | # And we need the sensitivity of conLoc wrt 'xLEMAC' and 'MAC' 106 | postLoc = 0.5 * (self.coords[0, 0] + self.coords[1, 0]) 107 | for key in wSens["xLEMAC"]: 108 | tmpSens[key] -= wSens["xLEMAC"][key] / wfuncs["MAC"] 109 | tmpSens[key] += wfuncs["xLEMAC"] / wfuncs["MAC"] ** 2 * wSens["MAC"][key] 110 | tmpSens[key] -= postLoc / wfuncs["MAC"] ** 2 * wSens["MAC"][key] 111 | funcsSens[self.name + "_MAC"] = tmpSens 112 | 113 | def addConstraintsPyOpt(self, optProb): 114 | """ 115 | Add the constraints to pyOpt, if the flag is set 116 | """ 117 | if self.addToPyOpt: 118 | optProb.addCon( 119 | self.name + "_thick", lower=self.thickLower, upper=self.thickUpper, wrt=self.DVGeo.getVarNames() 120 | ) 121 | 122 | optProb.addCon( 123 | self.name + "_MAC", lower=self.MACFracLower, upper=self.MACFracUpper, wrt=self.DVGeo.getVarNames() 124 | ) 125 | 126 | def writeTecplot(self, handle): 127 | raise NotImplementedError() 128 | -------------------------------------------------------------------------------- /pygeo/constraints/locationConstraint.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # Local modules 5 | from .baseConstraint import GeometricConstraint 6 | 7 | 8 | class LocationConstraint(GeometricConstraint): 9 | """ 10 | DVConstraints representation of a set of location 11 | constraints. One of these objects is created each time a 12 | addLocationConstraints1D call is 13 | made. The user should not have to deal with this class directly. 14 | """ 15 | 16 | def __init__(self, name, coords, lower, upper, scaled, scale, DVGeo, addToPyOpt, compNames): 17 | super().__init__(name, len(coords.flatten()), lower, upper, scale, DVGeo, addToPyOpt) 18 | self.coords = coords 19 | self.scaled = scaled 20 | 21 | # First thing we can do is embed the coordinates into DVGeo 22 | # with the name provided: 23 | self.DVGeo.addPointSet(self.coords, self.name, compNames=compNames) 24 | 25 | # Now get the reference lengths 26 | self.X0 = np.zeros(self.nCon) 27 | X = self.coords.flatten() 28 | for i in range(self.nCon): 29 | self.X0[i] = X[i] 30 | 31 | def evalFunctions(self, funcs, config): 32 | """ 33 | Evaluate the functions this object has and place in the funcs dictionary 34 | 35 | Parameters 36 | ---------- 37 | funcs : dict 38 | Dictionary to place function values 39 | """ 40 | # Pull out the most recent set of coordinates: 41 | self.coords = self.DVGeo.update(self.name, config=config) 42 | X = self.coords.flatten() 43 | if self.scaled: 44 | for i in range(self.nCon): 45 | X[i] /= self.X0[i] 46 | 47 | funcs[self.name] = X 48 | 49 | def evalFunctionsSens(self, funcsSens, config): 50 | """ 51 | Evaluate the sensitivity of the functions this object has and 52 | place in the funcsSens dictionary 53 | 54 | Parameters 55 | ---------- 56 | funcsSens : dict 57 | Dictionary to place function values 58 | """ 59 | 60 | nDV = self.DVGeo.getNDV() 61 | if nDV > 0: 62 | dTdPt = np.zeros((self.nCon, self.coords.shape[0], self.coords.shape[1])) 63 | counter = 0 64 | for i in range(self.coords.shape[0]): 65 | for j in range(self.coords.shape[1]): 66 | dTdPt[counter][i][j] = 1.0 67 | if self.scaled: 68 | dTdPt[counter][i][j] /= self.X0[i] 69 | counter += 1 70 | 71 | funcsSens[self.name] = self.DVGeo.totalSensitivity(dTdPt, self.name, config=config) 72 | 73 | def writeTecplot(self, handle): 74 | """ 75 | Write the visualization of this set of thickness constraints 76 | to the open file handle 77 | """ 78 | 79 | handle.write("Zone T=%s\n" % self.name) 80 | handle.write("Nodes = %d, Elements = %d ZONETYPE=FELINESEG\n" % (len(self.coords), len(self.coords) - 1)) 81 | handle.write("DATAPACKING=POINT\n") 82 | for i in range(len(self.coords)): 83 | handle.write(f"{self.coords[i, 0]:f} {self.coords[i, 1]:f} {self.coords[i, 2]:f}\n") 84 | 85 | for i in range(len(self.coords) - 1): 86 | handle.write("%d %d\n" % (i + 1, i + 2)) 87 | -------------------------------------------------------------------------------- /pygeo/geo_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # Utility Functions for Use in pyNetwork, pyGeo, pyBlock, DVGeometry, 3 | # and pyLayout 4 | # ============================================================================= 5 | 6 | # This __init__ file imports every methods in pygeo/geo_utils 7 | from .bilinear_map import * # noqa: F401, F403 8 | from .dcel import * # noqa: F401, F403 9 | from .file_io import * # noqa: F401, F403 10 | from .ffd_generation import * # noqa: F401, F403 11 | from .index_position import * # noqa: F401, F403 12 | from .knotvector import * # noqa: F401, F403 13 | from .misc import * # noqa: F401, F403 14 | from .node_edge_face import * # noqa: F401, F403 15 | from .norm import * # noqa: F401, F403 16 | from .orientation import * # noqa: F401, F403 17 | from .pointselect import * # noqa: F401, F403 18 | from .polygon import * # noqa: F401, F403 19 | from .projection import * # noqa: F401, F403 20 | from .remove_duplicates import * # noqa: F401, F403 21 | from .rotation import * # noqa: F401, F403 22 | from .split_quad import * # noqa: F401, F403 23 | 24 | 25 | # Set a (MUCH) larger recursion limit. For meshes with extremely large 26 | # numbers of blocs (> 5000) the recursive edge propagation may hit a 27 | # recursion limit. 28 | import sys 29 | 30 | sys.setrecursionlimit(10000) 31 | -------------------------------------------------------------------------------- /pygeo/geo_utils/bilinear_map.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | 5 | def getBiLinearMap(edge0, edge1, edge2, edge3): 6 | """Get the UV coordinates on a square defined from spacing on the edges""" 7 | 8 | if len(edge0) != len(edge1): 9 | raise ValueError("getBiLinearMap: The len of edge0 and edge1 are not the same") 10 | if len(edge2) != len(edge3): 11 | raise ValueError("getBiLinearMap: The len of edge2 and edge3 are no the same") 12 | 13 | N = len(edge0) 14 | M = len(edge2) 15 | 16 | UV = np.zeros((N, M, 2)) 17 | 18 | UV[:, 0, 0] = edge0 19 | UV[:, 0, 1] = 0.0 20 | 21 | UV[:, -1, 0] = edge1 22 | UV[:, -1, 1] = 1.0 23 | 24 | UV[0, :, 0] = 0.0 25 | UV[0, :, 1] = edge2 26 | 27 | UV[-1, :, 0] = 1.0 28 | UV[-1, :, 1] = edge3 29 | 30 | for i in range(1, N - 1): 31 | x1 = edge0[i] 32 | y1 = 0.0 33 | 34 | x2 = edge1[i] 35 | y2 = 1.0 36 | 37 | for j in range(1, M - 1): 38 | x3 = 0 39 | y3 = edge2[j] 40 | x4 = 1.0 41 | y4 = edge3[j] 42 | UV[i, j] = calcIntersection(x1, y1, x2, y2, x3, y3, x4, y4) 43 | 44 | return UV 45 | 46 | 47 | def calcIntersection(x1, y1, x2, y2, x3, y3, x4, y4): 48 | # Calc the intersection between two line segments defined by 49 | # (x1,y1) to (x2,y2) and (x3,y3) to (x4,y4) 50 | 51 | denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) 52 | ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom 53 | xi = x1 + ua * (x2 - x1) 54 | yi = y1 + ua * (y2 - y1) 55 | 56 | return xi, yi 57 | -------------------------------------------------------------------------------- /pygeo/geo_utils/index_position.py: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------- 2 | # Index position functions 3 | # -------------------------------------------------------------- 4 | 5 | 6 | def indexPosition1D(i, N): 7 | """This function is a generic function which determines if index 8 | over a list of length N is an interior point or node 0 or node 1. 9 | """ 10 | if 0 < i < N - 1: # Interior 11 | return 0, None 12 | elif i == 0: # Node 0 13 | return 1, 0 14 | elif i == N - 1: # Node 1 15 | return 1, 1 16 | 17 | 18 | def indexPosition2D(i, j, N, M): 19 | """This function is a generic function which determines if for a grid 20 | of data NxM with index i going 0->N-1 and j going 0->M-1, it 21 | determines if i,j is on the interior, on an edge or on a corner 22 | 23 | The function return four values: 24 | type: this is 0 for interior, 1 for on an edge and 2 for on a corner 25 | edge: this is the edge number if type==1 26 | node: this is the node number if type==2 27 | index: this is the value index along the edge of interest -- 28 | only defined for edges""" 29 | 30 | if 0 < i < N - 1 and 0 < j < M - 1: # Interior 31 | return 0, None, None, None 32 | elif 0 < i < N - 1 and j == 0: # Edge 0 33 | return 1, 0, None, i 34 | elif 0 < i < N - 1 and j == M - 1: # Edge 1 35 | return 1, 1, None, i 36 | elif i == 0 and 0 < j < M - 1: # Edge 2 37 | return 1, 2, None, j 38 | elif i == N - 1 and 0 < j < M - 1: # Edge 3 39 | return 1, 3, None, j 40 | elif i == 0 and j == 0: # Node 0 41 | return 2, None, 0, None 42 | elif i == N - 1 and j == 0: # Node 1 43 | return 2, None, 1, None 44 | elif i == 0 and j == M - 1: # Node 2 45 | return 2, None, 2, None 46 | elif i == N - 1 and j == M - 1: # Node 3 47 | return 2, None, 3, None 48 | 49 | 50 | def indexPosition3D(i, j, k, N, M, L): 51 | """This function is a generic function which determines if for a 52 | 3D grid of data NxMXL with index i going 0->N-1 and j going 0->M-1 53 | k going 0->L-1, it determines if i,j,k is on the interior, on a 54 | face, on an edge or on a corner 55 | 56 | Returns 57 | ------- 58 | type : int 59 | this is 0 for interior, 1 for on an face, 3 for an edge and 4 for on a corner 60 | number : int 61 | this is the face number if type==1, 62 | this is the edge number if type==2, 63 | this is the node number if type==3 64 | 65 | index1 : int 66 | this is the value index along 0th dir the face of interest OR edge of interest 67 | index2 : int 68 | this is the value index along 1st dir the face of interest 69 | """ 70 | 71 | # Note to interior->Faces->Edges->Nodes to minimize number of if checks 72 | 73 | # Interior: 74 | if 0 < i < N - 1 and 0 < j < M - 1 and 0 < k < L - 1: 75 | return 0, None, None, None 76 | 77 | elif 0 < i < N - 1 and 0 < j < M - 1 and k == 0: # Face 0 78 | return 1, 0, i, j 79 | elif 0 < i < N - 1 and 0 < j < M - 1 and k == L - 1: # Face 1 80 | return 1, 1, i, j 81 | elif i == 0 and 0 < j < M - 1 and 0 < k < L - 1: # Face 2 82 | return 1, 2, j, k 83 | elif i == N - 1 and 0 < j < M - 1 and 0 < k < L - 1: # Face 3 84 | return 1, 3, j, k 85 | elif 0 < i < N - 1 and j == 0 and 0 < k < L - 1: # Face 4 86 | return 1, 4, i, k 87 | elif 0 < i < N - 1 and j == M - 1 and 0 < k < L - 1: # Face 5 88 | return 1, 5, i, k 89 | 90 | elif 0 < i < N - 1 and j == 0 and k == 0: # Edge 0 91 | return 2, 0, i, None 92 | elif 0 < i < N - 1 and j == M - 1 and k == 0: # Edge 1 93 | return 2, 1, i, None 94 | elif i == 0 and 0 < j < M - 1 and k == 0: # Edge 2 95 | return 2, 2, j, None 96 | elif i == N - 1 and 0 < j < M - 1 and k == 0: # Edge 3 97 | return 2, 3, j, None 98 | elif 0 < i < N - 1 and j == 0 and k == L - 1: # Edge 4 99 | return 2, 4, i, None 100 | elif 0 < i < N - 1 and j == M - 1 and k == L - 1: # Edge 5 101 | return 2, 5, i, None 102 | elif i == 0 and 0 < j < M - 1 and k == L - 1: # Edge 6 103 | return 2, 6, j, None 104 | elif i == N - 1 and 0 < j < M - 1 and k == L - 1: # Edge 7 105 | return 2, 7, j, None 106 | elif i == 0 and j == 0 and 0 < k < L - 1: # Edge 8 107 | return 2, 8, k, None 108 | elif i == N - 1 and j == 0 and 0 < k < L - 1: # Edge 9 109 | return 2, 9, k, None 110 | elif i == 0 and j == M - 1 and 0 < k < L - 1: # Edge 10 111 | return 2, 10, k, None 112 | elif i == N - 1 and j == M - 1 and 0 < k < L - 1: # Edge 11 113 | return 2, 11, k, None 114 | 115 | elif i == 0 and j == 0 and k == 0: # Node 0 116 | return 3, 0, None, None 117 | elif i == N - 1 and j == 0 and k == 0: # Node 1 118 | return 3, 1, None, None 119 | elif i == 0 and j == M - 1 and k == 0: # Node 2 120 | return 3, 2, None, None 121 | elif i == N - 1 and j == M - 1 and k == 0: # Node 3 122 | return 3, 3, None, None 123 | elif i == 0 and j == 0 and k == L - 1: # Node 4 124 | return 3, 4, None, None 125 | elif i == N - 1 and j == 0 and k == L - 1: # Node 5 126 | return 3, 5, None, None 127 | elif i == 0 and j == M - 1 and k == L - 1: # Node 6 128 | return 3, 6, None, None 129 | elif i == N - 1 and j == M - 1 and k == L - 1: # Node 7 130 | return 3, 7, None, None 131 | -------------------------------------------------------------------------------- /pygeo/geo_utils/knotvector.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # -------------------------------------------------------------- 5 | # Knot Vector Manipulation Functions 6 | # -------------------------------------------------------------- 7 | 8 | 9 | def blendKnotVectors(knotVectors, sym): 10 | """Take in a list of knot vectors and average them""" 11 | 12 | nVec = len(knotVectors) 13 | 14 | if sym: # Symmetrize each knot vector first 15 | for i in range(nVec): 16 | curKnotVec = knotVectors[i].copy() 17 | if np.mod(len(curKnotVec), 2) == 1: # its odd 18 | mid = (len(curKnotVec) - 1) // 2 19 | beg1 = curKnotVec[0:mid] 20 | beg2 = (1 - curKnotVec[mid + 1 :])[::-1] 21 | # Average 22 | beg = 0.5 * (beg1 + beg2) 23 | curKnotVec[0:mid] = beg 24 | curKnotVec[mid + 1 :] = (1 - beg)[::-1] 25 | curKnotVec[mid] = 0.5 26 | else: # its even 27 | mid = len(curKnotVec) // 2 28 | beg1 = curKnotVec[0:mid] 29 | beg2 = (1 - curKnotVec[mid:])[::-1] 30 | beg = 0.5 * (beg1 + beg2) 31 | curKnotVec[0:mid] = beg 32 | curKnotVec[mid:] = (1 - beg)[::-1] 33 | 34 | knotVectors[i] = curKnotVec 35 | 36 | # Now average them all 37 | newKnotVec = np.zeros(len(knotVectors[0])) 38 | for i in range(nVec): 39 | newKnotVec += knotVectors[i] 40 | 41 | newKnotVec = newKnotVec / nVec 42 | return newKnotVec 43 | -------------------------------------------------------------------------------- /pygeo/geo_utils/misc.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # -------------------------------------------------------------- 5 | # Truly Miscellaneous Functions 6 | # -------------------------------------------------------------- 7 | 8 | 9 | def area2(hedge, point): 10 | """Determines the area of the triangle formed by a hedge and 11 | an external point""" 12 | 13 | pa = hedge.twin.origin 14 | pb = hedge.origin 15 | pc = point 16 | return (pb.x - pa.x) * (pc[1] - pa.y) - (pc[0] - pa.x) * (pb.y - pa.y) 17 | 18 | 19 | def isLeft(P0, P1, P2): 20 | return (P1[0] - P0[0]) * (P2[1] - P0[1]) - (P2[0] - P0[0]) * (P1[1] - P0[1]) 21 | 22 | 23 | def lefton(hedge, point): 24 | """Determines if a point is to the left of a hedge""" 25 | 26 | return area2(hedge, point) >= 0 27 | 28 | 29 | def hangle(dx, dy): 30 | """Determines the angle with respect to the x axis of a segment 31 | of coordinates dx and dy 32 | """ 33 | length = np.sqrt(dx * dx + dy * dy) 34 | 35 | if dy > 0: 36 | return np.arccos(dx / length) 37 | else: 38 | return 2 * np.pi - np.arccos(dx / length) 39 | 40 | 41 | def fillKnots(t, k, level): 42 | t = t[k - 1 : -k + 1] # Strip out the np.zeros 43 | newT = np.zeros(len(t) + (len(t) - 1) * level) 44 | start = 0 45 | for i in range(len(t) - 1): 46 | tmp = np.linspace(t[i], t[i + 1], level + 2) 47 | for j in range(level + 2): 48 | newT[start + j] = tmp[j] 49 | 50 | start += level + 1 51 | 52 | return newT 53 | 54 | 55 | def convertTo1D(value, dim1): 56 | """ 57 | Generic function to process 'value'. In the end, it must be 58 | array of size dim1. value is already that shape, excellent, 59 | otherwise, a scalar will be 'upcast' to that size 60 | """ 61 | 62 | if np.isscalar(value): 63 | return value * np.ones(dim1) 64 | else: 65 | temp = np.atleast_1d(value) 66 | if temp.shape[0] == dim1: 67 | return value 68 | else: 69 | raise ValueError( 70 | "The size of the 1D array was the incorrect shape! " + f"Expected {dim1} but got {temp.size}" 71 | ) 72 | 73 | 74 | def convertTo2D(value, dim1, dim2): 75 | """ 76 | Generic function to process 'value'. In the end, it must be dim1 77 | by dim2. value is already that shape, excellent, otherwise, a 78 | scalar will be 'upcast' to that size 79 | """ 80 | 81 | if np.isscalar(value): 82 | return value * np.ones((dim1, dim2)) 83 | else: 84 | temp = np.atleast_2d(value) 85 | if temp.shape[0] == dim1 and temp.shape[1] == dim2: 86 | return value 87 | else: 88 | raise ValueError("The size of the 2D array was the incorrect shape") 89 | -------------------------------------------------------------------------------- /pygeo/geo_utils/norm.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # ------------------------------------------------------------- 5 | # Norm Functions 6 | # ------------------------------------------------------------- 7 | 8 | 9 | def euclideanNorm(inVec): 10 | """ 11 | perform the euclidean 2 norm of the vector inVec 12 | required because linalg.norm() provides incorrect results for 13 | CS derivatives. 14 | """ 15 | inVec = np.array(inVec) 16 | return np.sqrt(inVec.dot(inVec)) 17 | 18 | 19 | def cross_b(a, b, crossb): 20 | """ 21 | Do the reverse accumulation through a cross product. 22 | """ 23 | ab = np.zeros_like(a) 24 | bb = np.zeros_like(b) 25 | 26 | ab[0] = ab[0] + b[1] * crossb[2] 27 | bb[1] = bb[1] + a[0] * crossb[2] 28 | ab[1] = ab[1] - b[0] * crossb[2] 29 | bb[0] = bb[0] - a[1] * crossb[2] 30 | 31 | ab[2] = ab[2] + b[0] * crossb[1] 32 | bb[0] = bb[0] + a[2] * crossb[1] 33 | ab[0] = ab[0] - b[2] * crossb[1] 34 | bb[2] = bb[2] - a[0] * crossb[1] 35 | 36 | ab[1] = ab[1] + b[2] * crossb[0] 37 | bb[2] = bb[2] + a[1] * crossb[0] 38 | ab[2] = ab[2] - b[1] * crossb[0] 39 | bb[1] = bb[1] - a[2] * crossb[0] 40 | 41 | return ab, bb 42 | 43 | 44 | def dot_b(a, b, dotb): 45 | """ 46 | Do the reverse accumulation through a dot product. 47 | """ 48 | ab = np.zeros_like(a) 49 | bb = np.zeros_like(b) 50 | 51 | ab = b * dotb 52 | bb = a * dotb 53 | 54 | return ab, bb 55 | 56 | 57 | def calculateCentroid(p0, v1, v2): 58 | """ 59 | take in a triangulated surface and calculate the centroid 60 | """ 61 | p0 = np.array(p0) 62 | p1 = np.array(v1) + p0 63 | p2 = np.array(v2) + p0 64 | 65 | # compute the areas 66 | areaVec = np.cross(v1, v2) / 2.0 67 | area = np.linalg.norm(areaVec, axis=1) 68 | 69 | # compute the cell centroids 70 | cellCenter = (p0 + p1 + p2) / 3.0 71 | 72 | centerSum = area.dot(cellCenter) 73 | areaSum = np.sum(area) 74 | 75 | centroid = centerSum / areaSum 76 | 77 | return centroid 78 | 79 | 80 | def calculateAverageNormal(p0, v1, v2): 81 | """ 82 | take in a triangulated surface and calculate the centroid 83 | """ 84 | p0 = np.array(p0) 85 | 86 | # compute the normal of each triangle 87 | normal = np.cross(v1, v2) 88 | sumNormal = np.sum(normal, axis=0) 89 | lengthNorm = np.linalg.norm(sumNormal) 90 | 91 | unitNormal = sumNormal / lengthNorm 92 | 93 | return unitNormal 94 | 95 | 96 | def calculateRadii(centroid, p0, v1, v2): 97 | """ 98 | take the centroid and compute inner and outer radii of surface 99 | """ 100 | p0 = np.array(p0) 101 | p1 = np.array(v1) + p0 102 | p2 = np.array(v2) + p0 103 | 104 | # take the difference between the points and the centroid 105 | d0 = p0 - centroid 106 | d1 = p1 - centroid 107 | d2 = p2 - centroid 108 | 109 | radO = np.zeros(3) 110 | radI = np.zeros(3) 111 | d0 = np.linalg.norm(d0, axis=1) 112 | radO[0] = np.max(d0) 113 | radI[0] = np.min(d0) 114 | d1 = np.linalg.norm(d1, axis=1) 115 | radO[1] = np.max(d1) 116 | radI[1] = np.min(d1) 117 | d2 = np.linalg.norm(d2, axis=1) 118 | radO[2] = np.max(d2) 119 | radI[2] = np.min(d2) 120 | 121 | outerRadius = np.max(radO) 122 | innerRadius = np.min(radI) 123 | 124 | return innerRadius, outerRadius 125 | 126 | 127 | def computeDistToAxis(origin, coords, axis, dtype="d"): 128 | """ 129 | compute the distance of coords from the defined axis. 130 | """ 131 | # Compute the direction from each point to the origin 132 | dirVec = origin - coords 133 | 134 | # compute the cross product with the desired axis. Cross product 135 | # will be zero if the direction vector is the same as the axis 136 | resultDir = np.cross(axis, dirVec) 137 | 138 | X = np.zeros(len(coords), dtype) 139 | for i in range(len(resultDir)): 140 | X[i] = euclideanNorm(resultDir[i, :]) 141 | 142 | return X 143 | 144 | 145 | # -------------------------------------------------------------- 146 | # Edge distance Function 147 | # -------------------------------------------------------------- 148 | 149 | 150 | def eDist(x1, x2): 151 | """Get the eculidean distance between two points""" 152 | return euclideanNorm(x1 - x2) # np.linalg.norm(x1-x2) 153 | 154 | 155 | def eDist2D(x1, x2): 156 | """Get the eculidean distance between two points""" 157 | return np.sqrt((x1[0] - x2[0]) ** 2 + (x1[1] - x2[1]) ** 2) 158 | 159 | 160 | def eDist_b(x1, x2): 161 | x1b = 0.0 162 | x2b = 0.0 163 | db = 1.0 164 | x1b = np.zeros(3) 165 | x2b = np.zeros(3) 166 | if (x1[0] - x2[0]) ** 2 + (x1[1] - x2[1]) ** 2 + (x1[2] - x2[2]) ** 2 == 0.0: 167 | tempb = 0.0 168 | else: 169 | tempb = db / (2.0 * np.sqrt((x1[0] - x2[0]) ** 2 + (x1[1] - x2[1]) ** 2 + (x1[2] - x2[2]) ** 2)) 170 | 171 | tempb0 = 2 * (x1[0] - x2[0]) * tempb 172 | tempb1 = 2 * (x1[1] - x2[1]) * tempb 173 | tempb2 = 2 * (x1[2] - x2[2]) * tempb 174 | x1b[0] = tempb0 175 | x2b[0] = -tempb0 176 | x1b[1] = tempb1 177 | x2b[1] = -tempb1 178 | x1b[2] = tempb2 179 | x2b[2] = -tempb2 180 | 181 | return x1b, x2b 182 | -------------------------------------------------------------------------------- /pygeo/geo_utils/orientation.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import sys 3 | 4 | # External modules 5 | import numpy as np 6 | 7 | # Local modules 8 | from .norm import eDist 9 | 10 | # -------------------------------------------------------------- 11 | # Orientation Functions 12 | # -------------------------------------------------------------- 13 | 14 | 15 | def edgeOrientation(e1, e2): 16 | """Compare two edge orientations. Basically if the two nodes are 17 | in the same order return 1 if they are in opposite order, return 18 | 1""" 19 | 20 | if [e1[0], e1[1]] == [e2[0], e2[1]]: 21 | return 1 22 | elif [e1[1], e1[0]] == [e2[0], e2[1]]: 23 | return -1 24 | else: 25 | print("Error with edgeOrientation: Not possible.") 26 | print("Orientation 1 [%d %d]" % (e1[0], e1[1])) 27 | print("Orientation 2 [%d %d]" % (e2[0], e2[1])) 28 | sys.exit(0) 29 | 30 | 31 | def faceOrientation(f1, f2): 32 | """Compare two face orientations f1 and f2 and return the 33 | transform to get f1 back to f2""" 34 | 35 | if [f1[0], f1[1], f1[2], f1[3]] == [f2[0], f2[1], f2[2], f2[3]]: 36 | return 0 37 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[1], f2[0], f2[3], f2[2]]: 38 | return 1 39 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[2], f2[3], f2[0], f2[1]]: 40 | return 2 41 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[3], f2[2], f2[1], f2[0]]: 42 | return 3 43 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[0], f2[2], f2[1], f2[3]]: 44 | return 4 45 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[2], f2[0], f2[3], f2[1]]: 46 | return 5 47 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[1], f2[3], f2[0], f2[2]]: 48 | return 6 49 | elif [f1[0], f1[1], f1[2], f1[3]] == [f2[3], f2[1], f2[2], f2[0]]: 50 | return 7 51 | else: 52 | print("Error with faceOrientation: Not possible.") 53 | print("Orientation 1 [%d %d %d %d]" % (f1[0], f1[1], f1[2], f1[3])) 54 | print("Orientation 2 [%d %d %d %d]" % (f2[0], f2[1], f2[2], f2[3])) 55 | sys.exit(0) 56 | 57 | 58 | def quadOrientation(pt1, pt2): 59 | """Given two sets of 4 points in ndim space, pt1 and pt2, 60 | determine the orientation of pt2 wrt pt1 61 | This works for both exact quads and "loosely" oriented quads 62 | .""" 63 | dist = np.zeros((4, 4)) 64 | for i in range(4): 65 | for j in range(4): 66 | dist[i, j] = eDist(pt1[i], pt2[j]) 67 | 68 | # Now compute the 8 distances for the 8 possible orientation 69 | sumDist = np.zeros(8) 70 | # corners = [0, 1, 2, 3] 71 | sumDist[0] = dist[0, 0] + dist[1, 1] + dist[2, 2] + dist[3, 3] 72 | # corners = [1, 0, 3, 2] 73 | sumDist[1] = dist[0, 1] + dist[1, 0] + dist[2, 3] + dist[3, 2] 74 | # corners = [2, 3, 0, 1] 75 | sumDist[2] = dist[0, 2] + dist[1, 3] + dist[2, 0] + dist[3, 1] 76 | # corners = [3, 2, 1, 0] 77 | sumDist[3] = dist[0, 3] + dist[1, 2] + dist[2, 1] + dist[3, 0] 78 | # corners = [0, 2, 1, 3] 79 | sumDist[4] = dist[0, 0] + dist[1, 2] + dist[2, 1] + dist[3, 3] 80 | # corners = [2, 0, 3, 1] 81 | sumDist[5] = dist[0, 2] + dist[1, 0] + dist[2, 3] + dist[3, 1] 82 | # corners = [1, 3, 0, 2] 83 | sumDist[6] = dist[0, 1] + dist[1, 3] + dist[2, 0] + dist[3, 2] 84 | # corners = [3, 1, 2, 0] 85 | sumDist[7] = dist[0, 3] + dist[1, 1] + dist[2, 2] + dist[3, 0] 86 | 87 | index = sumDist.argmin() 88 | 89 | return index 90 | 91 | 92 | def directionAlongSurface(surface, line): 93 | """Determine the dominate (u or v) direction of line along surface""" 94 | # Now Do two tests: Take N points in u and test N groups 95 | # against dn and take N points in v and test the N groups 96 | # again 97 | 98 | N = 3 99 | sn = np.linspace(0, 1, N) 100 | dn = np.zeros((N, 3)) 101 | s = np.linspace(0, 1, N) 102 | for i in range(N): 103 | dn[i, :] = line.getDerivative(sn[i]) 104 | 105 | uDotTot = 0 106 | for i in range(N): 107 | for n in range(N): 108 | du, dv = surface.getDerivative(s[i], s[n]) 109 | uDotTot += np.dot(du, dn[n, :]) 110 | 111 | vDotTot = 0 112 | for j in range(N): 113 | for n in range(N): 114 | du, dv = surface.getDerivative(s[n], s[j]) 115 | vDotTot += np.dot(dv, dn[n, :]) 116 | 117 | if abs(uDotTot) > abs(vDotTot): 118 | # Its along u now get 119 | if uDotTot >= 0: 120 | return 0 # U same direction 121 | else: 122 | return 1 # U opposite direction 123 | else: 124 | if vDotTot >= 0: 125 | return 2 # V same direction 126 | else: 127 | return 3 # V opposite direction 128 | 129 | 130 | def curveDirection(curve1, curve2): 131 | """Determine if the direction of curve 1 is basically in the same 132 | direction as curve2. Return 1 for same direction, -1 for opposite 133 | direction""" 134 | 135 | N = 4 136 | s = np.linspace(0, 1, N) 137 | tot = 0 138 | dForward = 0 139 | dBackward = 0 140 | for i in range(N): 141 | tot += np.dot(curve1.getDerivative(s[i]), curve2.getDerivative(s[i])) 142 | dForward += eDist(curve1.getValue(s[i]), curve2.getValue(s[i])) 143 | dBackward += eDist(curve1.getValue(s[i]), curve2.getValue(s[N - i - 1])) 144 | 145 | if tot > 0: 146 | return tot, dForward 147 | else: 148 | return tot, dBackward 149 | -------------------------------------------------------------------------------- /pygeo/geo_utils/rotation.py: -------------------------------------------------------------------------------- 1 | # External modules 2 | import numpy as np 3 | 4 | # -------------------------------------------------------------- 5 | # Rotation Functions 6 | # -------------------------------------------------------------- 7 | 8 | 9 | def rotxM(theta): 10 | """Return x rotation matrix""" 11 | theta = theta * np.pi / 180 12 | M = [[1, 0, 0], [0, np.cos(theta), -np.sin(theta)], [0, np.sin(theta), np.cos(theta)]] 13 | return M 14 | 15 | 16 | def rotyM(theta): 17 | """Return y rotation matrix""" 18 | theta = theta * np.pi / 180 19 | M = [[np.cos(theta), 0, np.sin(theta)], [0, 1, 0], [-np.sin(theta), 0, np.cos(theta)]] 20 | return M 21 | 22 | 23 | def rotzM(theta): 24 | """Return z rotation matrix""" 25 | theta = theta * np.pi / 180 26 | M = [[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]] 27 | return M 28 | 29 | 30 | def rotxV(x, theta): 31 | """Rotate a coordinate in the local x frame""" 32 | M = [[1, 0, 0], [0, np.cos(theta), -np.sin(theta)], [0, np.sin(theta), np.cos(theta)]] 33 | return np.dot(M, x) 34 | 35 | 36 | def rotyV(x, theta): 37 | """Rotate a coordinate in the local y frame""" 38 | M = [[np.cos(theta), 0, np.sin(theta)], [0, 1, 0], [-np.sin(theta), 0, np.cos(theta)]] 39 | return np.dot(M, x) 40 | 41 | 42 | def rotzV(x, theta): 43 | """Rotate a coordinate in the local z frame""" 44 | M = [[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]] 45 | return np.dot(M, x) 46 | 47 | 48 | def rotVbyW(V, W, theta): 49 | """Rotate a vector V, about an axis W by angle theta""" 50 | ux = W[0] 51 | uy = W[1] 52 | uz = W[2] 53 | 54 | c = np.cos(theta) 55 | s = np.sin(theta) 56 | if ( 57 | np.array(theta).dtype == np.dtype("D") 58 | or np.array(W).dtype == np.dtype("D") 59 | or np.array(V).dtype == np.dtype("D") 60 | ): 61 | dtype = "D" 62 | else: 63 | dtype = "d" 64 | 65 | R = np.zeros((3, 3), dtype) 66 | 67 | R[0, 0] = ux**2 + (1 - ux**2) * c 68 | R[0, 1] = ux * uy * (1 - c) - uz * s 69 | R[0, 2] = ux * uz * (1 - c) + uy * s 70 | 71 | R[1, 0] = ux * uy * (1 - c) + uz * s 72 | R[1, 1] = uy**2 + (1 - uy**2) * c 73 | R[1, 2] = uy * uz * (1 - c) - ux * s 74 | 75 | R[2, 0] = ux * uz * (1 - c) - uy * s 76 | R[2, 1] = uy * uz * (1 - c) + ux * s 77 | R[2, 2] = uz**2 + (1 - uz**2) * c 78 | 79 | return np.dot(R, V) 80 | 81 | 82 | # -------------------------------------------------------------- 83 | # Array Rotation and Flipping Functions 84 | # -------------------------------------------------------------- 85 | 86 | 87 | def rotateCCW(inArray): 88 | """Rotate the inArray array 90 degrees CCW""" 89 | rows = inArray.shape[0] 90 | cols = inArray.shape[1] 91 | output = np.empty([cols, rows], inArray.dtype) 92 | 93 | for row in range(rows): 94 | for col in range(cols): 95 | output[cols - col - 1][row] = inArray[row][col] 96 | 97 | return output 98 | 99 | 100 | def rotateCW(inArray): 101 | """Rotate the inArray array 90 degrees CW""" 102 | rows = inArray.shape[0] 103 | cols = inArray.shape[1] 104 | output = np.empty([cols, rows], inArray.dtype) 105 | 106 | for row in range(rows): 107 | for col in range(cols): 108 | output[col][rows - row - 1] = inArray[row][col] 109 | 110 | return output 111 | 112 | 113 | def reverseRows(inArray): 114 | """Flip Rows (horizontally)""" 115 | rows = inArray.shape[0] 116 | cols = inArray.shape[1] 117 | output = np.empty([rows, cols], inArray.dtype) 118 | for row in range(rows): 119 | output[row] = inArray[row][::-1].copy() 120 | 121 | return output 122 | 123 | 124 | def reverseCols(inArray): 125 | """Flip Cols (vertically)""" 126 | rows = inArray.shape[0] 127 | cols = inArray.shape[1] 128 | output = np.empty([rows, cols], inArray.dtype) 129 | for col in range(cols): 130 | output[:, col] = inArray[:, col][::-1].copy() 131 | 132 | return output 133 | 134 | 135 | def orientArray(index, inArray): 136 | """Take an input array inArray, and rotate/flip according to the index 137 | output from quadOrientation (in orientation.py)""" 138 | 139 | if index == 0: 140 | outArray = inArray.copy() 141 | elif index == 1: 142 | outArray = rotateCCW(inArray) 143 | outArray = rotateCCW(outArray) 144 | outArray = reverseRows(outArray) 145 | elif index == 2: 146 | outArray = reverseRows(inArray) 147 | elif index == 3: 148 | outArray = rotateCCW(inArray) # Verified working 149 | outArray = rotateCCW(outArray) 150 | elif index == 4: 151 | outArray = rotateCW(inArray) 152 | outArray = reverseRows(outArray) 153 | elif index == 5: 154 | outArray = rotateCCW(inArray) 155 | elif index == 6: 156 | outArray = rotateCW(inArray) 157 | elif index == 7: 158 | outArray = rotateCCW(inArray) 159 | outArray = reverseRows(outArray) 160 | 161 | return outArray 162 | -------------------------------------------------------------------------------- /pygeo/mphys/__init__.py: -------------------------------------------------------------------------------- 1 | from .mphys_dvgeo import OM_DVGEOCOMP 2 | 3 | __all__ = ["OM_DVGEOCOMP"] 4 | -------------------------------------------------------------------------------- /pygeo/parameterization/__init__.py: -------------------------------------------------------------------------------- 1 | from .DVGeo import DVGeometry 2 | from .DVGeoAxi import DVGeometryAxi 3 | from .DVGeoCST import DVGeometryCST 4 | from .DVGeoVSP import DVGeometryVSP 5 | from .DVGeoESP import DVGeometryESP 6 | from .DVGeoMulti import DVGeometryMulti 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import re 3 | import os 4 | 5 | __version__ = re.findall( 6 | r"""__version__ = ["']+([0-9\.]*)["']+""", 7 | open("pygeo/__init__.py").read(), 8 | )[0] 9 | 10 | this_directory = os.path.abspath(os.path.dirname(__file__)) 11 | with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f: 12 | long_description = f.read() 13 | 14 | setup( 15 | name="pygeo", 16 | version=__version__, 17 | description="pyGeo is an object oriented geometry manipulation framework for multidisciplinary design optimization", 18 | long_description=long_description, 19 | long_description_content_type="text/markdown", 20 | keywords="geometry FFD optimization", 21 | author="", 22 | author_email="", 23 | url="https://github.com/mdolab/pygeo", 24 | license="Apache License Version 2.0", 25 | packages=find_packages(include=["pygeo*"]), 26 | install_requires=["numpy>=1.21", "pyspline>=1.1", "scipy>=1.7", "mpi4py>=3.1.5", "mdolab-baseclasses", "packaging"], 27 | extras_require={ 28 | "testing": ["numpy-stl", "parameterized", "testflo", "psutil"], 29 | "mphys": ["openmdao>=3.25"], 30 | "openvsp": ["openvsp>=3.28"], 31 | }, 32 | classifiers=["Operating System :: OS Independent", "Programming Language :: Python"], 33 | ) 34 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | - testflo 3 | - pyspline 4 | - numpy-stl 5 | - parameterized 6 | 7 | # Instructions and examples 8 | - To train all regression tests with specific function signature: `testflo reg_tests -m "train_*"` 9 | - To train all regression tests in specific testfile either works: `cd reg_tests; testflo test_DVGeometry.py -m "train_*"` or `testflo reg_tests/test_DVGeometry.py -m "train_*"` 10 | - To train specific regression test in specific testfile either works: `cd reg_tests; testflo test_DVGeometry.py -m "test_1` or `testflo reg_tests/test_DVGeometry.py -m "train_1"` 11 | 12 | - To test all regression tests: `testflo reg_tests` 13 | - To test all regression tests with specific function signature: `testflo reg_tests -m "test_*"` 14 | - To test all regression tests in specific testfile either works: `cd reg_tests; testflo test_DVGeometry.py -m "test_*"` or `testflo reg_tests/test_DVGeometry.py -m "test_*"` 15 | - To test specific regression test in a specific testfile either works: `cd reg_tests; testflo test_DVGeometry.py -m "test_1` or `testflo reg_tests/test_DVGeometry.py -m "test_1"` 16 | 17 | - To enable screen output: use `-s` argument 18 | - To change number of cores to 1 for running tests: use `-n 1` 19 | -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_Blocks_01.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "rotate_y_small": { 4 | "__ndarray__": [ 5 | [ 6 | 0.0 7 | ], 8 | [ 9 | 0.0 10 | ], 11 | [ 12 | 0.012341341837363018 13 | ], 14 | [ 15 | -0.004363322814171511 16 | ], 17 | [ 18 | 0.0 19 | ], 20 | [ 21 | -0.007978018357057692 22 | ], 23 | [ 24 | -2.220446049250313e-10 25 | ], 26 | [ 27 | 0.0 28 | ], 29 | [ 30 | -0.012341340949184598 31 | ], 32 | [ 33 | 0.0 34 | ], 35 | [ 36 | 0.0 37 | ], 38 | [ 39 | 0.0 40 | ] 41 | ], 42 | "dtype": "float64", 43 | "shape": [ 44 | 12, 45 | 1 46 | ] 47 | }, 48 | "rotate_y_tiny": { 49 | "__ndarray__": [ 50 | [ 51 | 0.0 52 | ], 53 | [ 54 | 0.0 55 | ], 56 | [ 57 | 0.0 58 | ], 59 | [ 60 | -0.004363323036216116 61 | ], 62 | [ 63 | 0.0 64 | ], 65 | [ 66 | 0.004363322925193813 67 | ], 68 | [ 69 | -2.220446049250313e-10 70 | ], 71 | [ 72 | 0.0 73 | ], 74 | [ 75 | 0.0 76 | ], 77 | [ 78 | 0.0 79 | ], 80 | [ 81 | 0.0 82 | ], 83 | [ 84 | 0.0 85 | ] 86 | ], 87 | "dtype": "float64", 88 | "shape": [ 89 | 12, 90 | 1 91 | ] 92 | } 93 | }, 94 | "jacobian": { 95 | "__ndarray__": [ 96 | [ 97 | 4.231650388945577e-19, 98 | 0.0, 99 | 0.01234134149488435, 100 | -0.0043633231299858265, 101 | 0.0, 102 | -0.007978018364898526, 103 | -2.115825194472788e-18, 104 | 0.0, 105 | -0.012341341494884356, 106 | 0.0, 107 | 0.0, 108 | 0.0 109 | ], 110 | [ 111 | 0.0, 112 | 0.0, 113 | 0.0, 114 | -0.004363323129985827, 115 | 0.0, 116 | 0.004363323129985827, 117 | -5.575821688963584e-18, 118 | 0.0, 119 | 4.381002755614244e-18, 120 | 0.0, 121 | 0.0, 122 | 0.0 123 | ] 124 | ], 125 | "dtype": "float64", 126 | "shape": [ 127 | 2, 128 | 12 129 | ] 130 | }, 131 | "pointvals": { 132 | "__ndarray__": [ 133 | [ 134 | 0.2928932188134526, 135 | 0.5, 136 | 0.9999999999999997 137 | ], 138 | [ 139 | 1.4571067811865472, 140 | 1.2499999999999998, 141 | 0.75 142 | ], 143 | [ 144 | 1.7071067811865472, 145 | 1.4999999999999996, 146 | 0.9999999999999997 147 | ], 148 | [ 149 | 2.0, 150 | 2.5, 151 | 0.5 152 | ] 153 | ], 154 | "dtype": "float64", 155 | "shape": [ 156 | 4, 157 | 3 158 | ] 159 | } 160 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_Blocks_05.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "rotate_y_small": { 4 | "__ndarray__": [ 5 | [ 6 | -3.7338091667166853e-19 7 | ], 8 | [ 9 | 0.0 10 | ], 11 | [ 12 | 0.012341341494884351 13 | ], 14 | [ 15 | -0.004363323129985827 16 | ], 17 | [ 18 | 0.0 19 | ], 20 | [ 21 | -0.007978018364898526 22 | ], 23 | [ 24 | -2.5140981722559013e-18 25 | ], 26 | [ 27 | 0.0 28 | ], 29 | [ 30 | -0.012341341494884353 31 | ], 32 | [ 33 | 0.0 34 | ], 35 | [ 36 | 0.0 37 | ], 38 | [ 39 | 0.0 40 | ] 41 | ], 42 | "dtype": "float64", 43 | "shape": [ 44 | 12, 45 | 1 46 | ] 47 | }, 48 | "rotate_y_tiny": { 49 | "__ndarray__": [ 50 | [ 51 | 0.0 52 | ], 53 | [ 54 | 0.0 55 | ], 56 | [ 57 | 0.0 58 | ], 59 | [ 60 | -0.004363323129985827 61 | ], 62 | [ 63 | 0.0 64 | ], 65 | [ 66 | 0.0043633231299858265 67 | ], 68 | [ 69 | -5.77495817785514e-18 70 | ], 71 | [ 72 | 0.0 73 | ], 74 | [ 75 | 3.584456800048018e-18 76 | ], 77 | [ 78 | 0.0 79 | ], 80 | [ 81 | 0.0 82 | ], 83 | [ 84 | 0.0 85 | ] 86 | ], 87 | "dtype": "float64", 88 | "shape": [ 89 | 12, 90 | 1 91 | ] 92 | }, 93 | "translate_big": { 94 | "__ndarray__": [ 95 | [ 96 | 1.0000000000000002, 97 | 0.0, 98 | -1.4419021995714475e-16 99 | ], 100 | [ 101 | 0.0, 102 | 1.0000000000000002, 103 | 0.0 104 | ], 105 | [ 106 | 1.4419021995714475e-16, 107 | 0.0, 108 | 1.0000000000000002 109 | ], 110 | [ 111 | 1.0, 112 | 0.0, 113 | -1.441902199571448e-16 114 | ], 115 | [ 116 | 0.0, 117 | 1.0, 118 | 0.0 119 | ], 120 | [ 121 | 1.441902199571448e-16, 122 | 0.0, 123 | 1.0 124 | ], 125 | [ 126 | 1.0, 127 | 0.0, 128 | -1.441902199571448e-16 129 | ], 130 | [ 131 | 0.0, 132 | 1.0, 133 | 0.0 134 | ], 135 | [ 136 | 1.441902199571448e-16, 137 | 0.0, 138 | 1.0 139 | ], 140 | [ 141 | 1.0, 142 | 0.0, 143 | 0.0 144 | ], 145 | [ 146 | 0.0, 147 | 1.0, 148 | 0.0 149 | ], 150 | [ 151 | 0.0, 152 | 0.0, 153 | 1.0 154 | ] 155 | ], 156 | "dtype": "float64", 157 | "shape": [ 158 | 12, 159 | 3 160 | ] 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_Cylinder_01.ref: -------------------------------------------------------------------------------- 1 | { 2 | "funcs1": { 3 | "DVCon1_leradius_constraints_0": { 4 | "__ndarray__": [ 5 | 0.9999962914850278, 6 | 0.999995760958035, 7 | 0.9999998221258696, 8 | 0.9999993319711176, 9 | 1.000004944755905 10 | ], 11 | "dtype": "float64", 12 | "shape": [ 13 | 5 14 | ] 15 | } 16 | }, 17 | "funcs2": { 18 | "DVCon1_leradius_constraints_0": { 19 | "__ndarray__": [ 20 | 0.4999981457425139, 21 | 0.4999978804790175, 22 | 0.4999999110629348, 23 | 0.4999996659855588, 24 | 0.5000024723779525 25 | ], 26 | "dtype": "float64", 27 | "shape": [ 28 | 5 29 | ] 30 | } 31 | }, 32 | "funcsSens": { 33 | "DVCon1_leradius_constraints_0": { 34 | "scale_circle": { 35 | "__ndarray__": [ 36 | [ 37 | 0.9999962914850281 38 | ], 39 | [ 40 | 0.9999957609580348 41 | ], 42 | [ 43 | 0.9999998221258697 44 | ], 45 | [ 46 | 0.9999993319711181 47 | ], 48 | [ 49 | 1.000004944755905 50 | ] 51 | ], 52 | "dtype": "float64", 53 | "shape": [ 54 | 5, 55 | 1 56 | ] 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_Cylinder_spanwise_dvs.ref: -------------------------------------------------------------------------------- 1 | { 2 | "funcs1": { 3 | "DVCon1_leradius_constraints_0": { 4 | "__ndarray__": [ 5 | 0.9999962914850278, 6 | 0.999995760958035, 7 | 0.9999998221258696, 8 | 0.9999993319711176, 9 | 1.000004944755905 10 | ], 11 | "dtype": "float64", 12 | "shape": [ 13 | 5 14 | ] 15 | } 16 | }, 17 | "funcs2": { 18 | "DVCon1_leradius_constraints_0": { 19 | "__ndarray__": [ 20 | 1.0274767606779722, 21 | 1.0389909669351234, 22 | 1.0523915707811478, 23 | 1.0676870537795249, 24 | 1.0849072587266035 25 | ], 26 | "dtype": "float64", 27 | "shape": [ 28 | 5 29 | ] 30 | } 31 | }, 32 | "funcsSens": { 33 | "DVCon1_leradius_constraints_0": { 34 | "shape": { 35 | "__ndarray__": [ 36 | [ 37 | -0.25686901113785526, 38 | 0.2564858233025937, 39 | -0.2564858233025936, 40 | 0.2568690111378553 41 | ], 42 | [ 43 | -0.32810904434265753, 44 | 0.3261041094447367, 45 | -0.25341375902391505, 46 | 0.25541869392183586 47 | ], 48 | [ 49 | -0.4081754150387783, 50 | 0.40504333499679473, 51 | -0.24239958498968017, 52 | 0.24553166503166368 53 | ], 54 | [ 55 | -0.49722701453849344, 56 | 0.49345926222971825, 57 | -0.22327209583838786, 58 | 0.22703984814716277 59 | ], 60 | [ 61 | -0.5954377625541438, 62 | 0.5915177149773408, 63 | -0.1958658891335127, 64 | 0.1997859367103156 65 | ] 66 | ], 67 | "dtype": "float64", 68 | "shape": [ 69 | 5, 70 | 4 71 | ] 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_monotonic.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_arb_twist": { 3 | "DVCon1_monotonic_constraint_0": { 4 | "twist": { 5 | "__ndarray__": [ 6 | [ 7 | 1.0, 8 | -1.0, 9 | 0.0 10 | ], 11 | [ 12 | 0.0, 13 | 1.0, 14 | -1.0 15 | ] 16 | ], 17 | "dtype": "float64", 18 | "shape": [ 19 | 2, 20 | 3 21 | ] 22 | } 23 | }, 24 | "DVCon1_monotonic_constraint_1": { 25 | "twist": { 26 | "__ndarray__": [ 27 | [ 28 | 0.0, 29 | 1.0, 30 | -1.0 31 | ] 32 | ], 33 | "dtype": "float64", 34 | "shape": [ 35 | 1, 36 | 3 37 | ] 38 | } 39 | } 40 | }, 41 | "derivs_base": { 42 | "DVCon1_monotonic_constraint_0": { 43 | "twist": { 44 | "__ndarray__": [ 45 | [ 46 | 1.0, 47 | -1.0, 48 | 0.0 49 | ], 50 | [ 51 | 0.0, 52 | 1.0, 53 | -1.0 54 | ] 55 | ], 56 | "dtype": "float64", 57 | "shape": [ 58 | 2, 59 | 3 60 | ] 61 | } 62 | }, 63 | "DVCon1_monotonic_constraint_1": { 64 | "twist": { 65 | "__ndarray__": [ 66 | [ 67 | 0.0, 68 | 1.0, 69 | -1.0 70 | ] 71 | ], 72 | "dtype": "float64", 73 | "shape": [ 74 | 1, 75 | 3 76 | ] 77 | } 78 | } 79 | }, 80 | "derivs_twisted": { 81 | "DVCon1_monotonic_constraint_0": { 82 | "twist": { 83 | "__ndarray__": [ 84 | [ 85 | 1.0, 86 | -1.0, 87 | 0.0 88 | ], 89 | [ 90 | 0.0, 91 | 1.0, 92 | -1.0 93 | ] 94 | ], 95 | "dtype": "float64", 96 | "shape": [ 97 | 2, 98 | 3 99 | ] 100 | } 101 | }, 102 | "DVCon1_monotonic_constraint_1": { 103 | "twist": { 104 | "__ndarray__": [ 105 | [ 106 | 0.0, 107 | 1.0, 108 | -1.0 109 | ] 110 | ], 111 | "dtype": "float64", 112 | "shape": [ 113 | 1, 114 | 3 115 | ] 116 | } 117 | } 118 | }, 119 | "funcs_arb_twist": { 120 | "DVCon1_monotonic_constraint_0": { 121 | "__ndarray__": [ 122 | 4.5, 123 | -1.0 124 | ], 125 | "dtype": "float64", 126 | "shape": [ 127 | 2 128 | ] 129 | }, 130 | "DVCon1_monotonic_constraint_1": -1.0 131 | }, 132 | "funcs_base": { 133 | "DVCon1_monotonic_constraint_0": { 134 | "__ndarray__": [ 135 | 0.0, 136 | 0.0 137 | ], 138 | "dtype": "float64", 139 | "shape": [ 140 | 2 141 | ] 142 | }, 143 | "DVCon1_monotonic_constraint_1": 0.0 144 | }, 145 | "funcs_twisted": { 146 | "DVCon1_monotonic_constraint_0": { 147 | "__ndarray__": [ 148 | -5.0, 149 | -5.0 150 | ], 151 | "dtype": "float64", 152 | "shape": [ 153 | 2 154 | ] 155 | }, 156 | "DVCon1_monotonic_constraint_1": -5.0 157 | } 158 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_planarity_box.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_planarity_constraints_0": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -0.9428090415820635, 8 | -1.1785113019775793, 9 | 0.5303300858899105, 10 | 0.5303300858899105, 11 | -1.1785113019775495, 12 | -0.9428090415820393, 13 | 0.0, 14 | 0.0, 15 | 0.0, 16 | 0.0, 17 | 1.5909902576697315, 18 | 1.5909902576697315, 19 | 0.0, 20 | 0.0, 21 | 0.0, 22 | 0.0, 23 | -2.5171126409199746e-28, 24 | -2.983175671499122e-14, 25 | -2.067030588367663e-28, 26 | -2.417942386372973e-14, 27 | 0.0, 28 | 0.0, 29 | 0.0, 30 | 0.0, 31 | 0.0, 32 | 0.0, 33 | 0.0, 34 | 0.0, 35 | 0.0, 36 | 0.0, 37 | 0.0, 38 | 0.0 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | 7.855413721171984e-31, 51 | 9.865181867315213e-17, 52 | 0.004113780498294686 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | } 62 | }, 63 | "funcs_base": { 64 | "DVCon1_planarity_constraints_0": 4.242640687119285 65 | } 66 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_planarity_tri.ref: -------------------------------------------------------------------------------- 1 | { 2 | "funcs_base": { 3 | "DVCon1_planarity_constraints_0": 1.8619006149354548e-16 4 | } 5 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_projectedArea_box.ref: -------------------------------------------------------------------------------- 1 | { 2 | "funcs_base": { 3 | "DVCon1_projectedArea_constraints_0": 15.999999999999886, 4 | "DVCon1_projectedArea_constraints_1": 2.0, 5 | "DVCon1_projectedArea_constraints_2": 7.999999999999943 6 | } 7 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_projectedArea_box_sens.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_projectedArea_constraints_0": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -4.2784323062795407e-44, 8 | -2.907593750582667, 9 | 2.978815592105176, 10 | 0.07122184152250899, 11 | -0.07122184152250717, 12 | -2.9788155921051, 13 | 2.907593750582593, 14 | 0.0, 15 | 0.0, 16 | 0.0, 17 | 0.0, 18 | 0.0, 19 | 0.0, 20 | 0.0, 21 | 0.0, 22 | 0.0, 23 | -1.5211852216018635e-29, 24 | -1.802844525570421e-15, 25 | -6.530806000391957e-28, 26 | -7.639515706439993e-14, 27 | 6.374658022918317e-28, 28 | 7.456859090033516e-14, 29 | 0.0, 30 | 0.0, 31 | 0.0, 32 | 0.0, 33 | 0.0, 34 | 0.0, 35 | 0.0, 36 | 0.0, 37 | 0.0, 38 | 0.0 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | -1.1802419414065871e-29, 51 | -1.3803005736475235e-15, 52 | -0.053808854645455206 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | } 62 | }, 63 | "funcs_base": { 64 | "DVCon1_projectedArea_constraints_0": 16.886300102401385 65 | } 66 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_surfaceArea_box.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_surfaceArea_constraints_0": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -4.999999999999972, 8 | -4.999999999999972, 9 | 4.999999999999972, 10 | 4.999999999999972, 11 | -4.9999999999998455, 12 | -4.999999999999844, 13 | 4.999999999999844, 14 | 4.999999999999844, 15 | 0.0, 16 | 0.0, 17 | 0.0, 18 | 0.0, 19 | 0.0, 20 | 0.0, 21 | 0.0, 22 | 0.0, 23 | -1.0679204504429297e-27, 24 | -1.26565424807265e-13, 25 | -1.0962085094660887e-27, 26 | -1.2823075934420268e-13, 27 | 1.096208509466089e-27, 28 | 1.2823075934420268e-13, 29 | 1.0962085094660887e-27, 30 | 1.2823075934420268e-13, 31 | 0.0, 32 | 0.0, 33 | 0.0, 34 | 0.0, 35 | 0.0, 36 | 0.0, 37 | 0.0, 38 | 0.0 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | -2.715458729239462e-31, 51 | -1.5986063949205333e-17, 52 | 2.7755575615628914e-17 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | } 62 | }, 63 | "funcs_base": { 64 | "DVCon1_surfaceArea_constraints_0": 51.99999999999966 65 | } 66 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_triangulatedSurface_intersected.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_trisurf_constraint_0_KS": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -3.057527043396886e-05, 8 | -0.0008941879814524576, 9 | -2.1053894961190165e-05, 10 | -0.0006399369364370025, 11 | -0.0005367434369910953, 12 | -0.018800846833772235, 13 | -0.00038232868380640075, 14 | -0.013785806486674797, 15 | -0.0001623269524720609, 16 | -0.00042698965231694977, 17 | -0.00023288614108336295, 18 | -0.0006056672146522453, 19 | -0.005252277593508186, 20 | -0.014626084267121569, 21 | -0.007271564983517073, 22 | -0.020086232789955714, 23 | -0.00018699715306737914, 24 | -0.000584287597303101, 25 | -0.0036174377491864085, 26 | -0.011983834419350164, 27 | -0.00026796188863982274, 28 | -0.0008262121494360451, 29 | -0.005021787957041326, 30 | -0.016472967777969937, 31 | -0.0014743546666701403, 32 | -0.004706853219046554, 33 | -0.003961156247969405, 34 | -0.01289514835197009, 35 | -0.0020881877493969304, 36 | -0.006585390073986357, 37 | -0.005551064346504278, 38 | -0.017872121188225183 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | -0.005502725071774599, 51 | -0.018008326343421834, 52 | -0.020513984407938966 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | }, 62 | "DVCon1_trisurf_constraint_0_perim": { 63 | "local": { 64 | "__ndarray__": [ 65 | [ 66 | -0.0012108965054235265, 67 | -0.03576599844146774, 68 | -0.000825066128724888, 69 | -0.02542143921864909, 70 | -0.018456214499037005, 71 | -0.6698450915308741, 72 | -0.012992065300126218, 73 | -0.4866628978621213, 74 | -0.006072810778769617, 75 | -0.015226824756585624, 76 | -0.008812822780531485, 77 | -0.021856109624917047, 78 | -0.2010860091688249, 79 | -0.538239856615703, 80 | -0.2805859446236136, 81 | -0.7455781166885702, 82 | -0.007363266357137905, 83 | -0.023115119460414356, 84 | -0.12479062935005605, 85 | -0.4187796753140749, 86 | -0.010648529022203774, 87 | -0.032945275008524705, 88 | -0.1750732492004869, 89 | -0.5812693306115636, 90 | -0.05562759328741662, 91 | -0.17900283404175182, 92 | -0.14296531358160575, 93 | -0.47042607420874744, 94 | -0.07957646897969974, 95 | -0.25264315622107286, 96 | -0.20244353670441792, 97 | -0.65810725621684 98 | ] 99 | ], 100 | "dtype": "float64", 101 | "shape": [ 102 | 1, 103 | 32 104 | ] 105 | }, 106 | "twist": { 107 | "__ndarray__": [ 108 | [ 109 | -0.19607060711212043, 110 | -0.6485819964041314, 111 | -0.7452222937059239 112 | ] 113 | ], 114 | "dtype": "float64", 115 | "shape": [ 116 | 1, 117 | 3 118 | ] 119 | } 120 | } 121 | }, 122 | "funcs_base": { 123 | "DVCon1_trisurf_constraint_0_KS": 0.5100263228120506, 124 | "DVCon1_trisurf_constraint_0_perim": 2.4799948359126756 125 | } 126 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_triangulatedVolume_box.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_trivolume_constraint_1": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -0.16666666666666669, 8 | -0.33333333333333337, 9 | 0.33333333333333337, 10 | 0.16666666666666669, 11 | -0.33333333333332493, 12 | -0.1666666666666624, 13 | 0.1666666666666624, 14 | 0.3333333333333248, 15 | 0.0, 16 | 0.0, 17 | 0.0, 18 | 0.0, 19 | 0.0, 20 | 0.0, 21 | 0.0, 22 | 0.0, 23 | -7.119469669619572e-29, 24 | -8.43769498715105e-15, 25 | -3.6540283648869846e-29, 26 | -4.274358644806782e-15, 27 | 3.654028364886984e-29, 28 | 4.274358644806781e-15, 29 | 7.308056729773968e-29, 30 | 8.548717289613562e-15, 31 | 0.0, 32 | 0.0, 33 | 0.0, 34 | 0.0, 35 | 0.0, 36 | 0.0, 37 | 0.0, 38 | 0.0 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | 1.565570328357996e-30, 51 | 1.8480858777642647e-16, 52 | 0.007272205216642855 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | }, 62 | "unscaled_vol_con": { 63 | "local": { 64 | "__ndarray__": [ 65 | [ 66 | -2.6666666666666483, 67 | -5.333333333333296, 68 | 5.333333333333296, 69 | 2.666666666666648, 70 | -5.333333333333161, 71 | -2.66666666666658, 72 | 2.6666666666665795, 73 | 5.333333333333159, 74 | 0.0, 75 | 0.0, 76 | 0.0, 77 | 0.0, 78 | 0.0, 79 | 0.0, 80 | 0.0, 81 | 0.0, 82 | -1.1391151471391235e-27, 83 | -1.350031197944158e-13, 84 | -5.846445383819133e-28, 85 | -6.8389738316908e-14, 86 | 5.846445383819132e-28, 87 | 6.8389738316908e-14, 88 | 1.1692890767638265e-27, 89 | 1.36779476633816e-13, 90 | 0.0, 91 | 0.0, 92 | 0.0, 93 | 0.0, 94 | 0.0, 95 | 0.0, 96 | 0.0, 97 | 0.0 98 | ] 99 | ], 100 | "dtype": "float64", 101 | "shape": [ 102 | 1, 103 | 32 104 | ] 105 | }, 106 | "twist": { 107 | "__ndarray__": [ 108 | [ 109 | 2.504912525372775e-29, 110 | 2.9569374044228023e-15, 111 | 0.11635528346628485 112 | ] 113 | ], 114 | "dtype": "float64", 115 | "shape": [ 116 | 1, 117 | 3 118 | ] 119 | } 120 | } 121 | }, 122 | "funcs_base": { 123 | "DVCon1_trivolume_constraint_1": 1.0, 124 | "unscaled_vol_con": 15.999999999999886 125 | } 126 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_triangulatedVolume_bwb.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_trivolume_constraint_1": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | 0.004801047125934009, 8 | 0.002628830714812307, 9 | -0.004801014255246808, 10 | -0.0026288029505695584, 11 | 0.0065299286607317444, 12 | 0.003962813749363914, 13 | -0.006530576911358634, 14 | -0.003963999664718665, 15 | -0.008758085246506363, 16 | -0.010206080323862445, 17 | 0.008758140697656217, 18 | 0.010205766163758519, 19 | -0.004959351761916739, 20 | -0.006020055849019212, 21 | 0.0049593498586602, 22 | 0.006019449063956628, 23 | -0.011135974429007196, 24 | -0.009102546274843581, 25 | -0.0124995715814612, 26 | -0.010504920265786533, 27 | 0.01113595453421521, 28 | 0.009102550153632346, 29 | 0.012500123970111299, 30 | 0.010506335850463566, 31 | -0.019138858375298475, 32 | -0.015790841504792638, 33 | -0.020657462165051738, 34 | -0.017236284670004903, 35 | 0.019138758683853715, 36 | 0.015790932218092427, 37 | 0.020657492426321727, 38 | 0.017236952358013452 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | 2.222465715906452e-07, 51 | 7.056756862189317e-07, 52 | -6.024759085510034e-07 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | }, 62 | "unscaled_vol_con": { 63 | "local": { 64 | "__ndarray__": [ 65 | [ 66 | 5.2982913474996405, 67 | 2.901098586409203, 68 | -5.298255072396898, 69 | -2.9010679466251457, 70 | 7.206233060292718, 71 | 4.373242180145667, 72 | -7.206948450207413, 73 | -4.3745509206979705, 74 | -9.665159717255719, 75 | -11.263123575628846, 76 | 9.66522091148011, 77 | 11.262776877978247, 78 | -5.472991587071917, 79 | -6.6435527458236265, 80 | 5.472989486694985, 81 | 6.642883116059238, 82 | -12.28932676883653, 83 | -10.045296557849738, 84 | -13.794151613253291, 85 | -11.592914356066728, 86 | 12.289304813541893, 87 | 10.045300838364417, 88 | 13.794761212768918, 89 | 11.59447655277944, 90 | -21.121069023279226, 91 | -17.42629820537438, 92 | -22.79695453501685, 93 | -19.021445850182488, 94 | 21.120959006795296, 95 | 17.426398313846732, 96 | 22.79698793044645, 97 | 19.02218269060569 98 | ] 99 | ], 100 | "dtype": "float64", 101 | "shape": [ 102 | 1, 103 | 32 104 | ] 105 | }, 106 | "twist": { 107 | "__ndarray__": [ 108 | [ 109 | 0.00024526464173391105, 110 | 0.0007787624827528816, 111 | -0.0006648743071195895 112 | ] 113 | ], 114 | "dtype": "float64", 115 | "shape": [ 116 | 1, 117 | 3 118 | ] 119 | } 120 | } 121 | }, 122 | "funcs_base": { 123 | "DVCon1_trivolume_constraint_1": 1.0, 124 | "unscaled_vol_con": 1103.569952246393 125 | } 126 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVConstraints_volume_box.ref: -------------------------------------------------------------------------------- 1 | { 2 | "derivs_base": { 3 | "DVCon1_volume_constraint_0": { 4 | "local": { 5 | "__ndarray__": [ 6 | [ 7 | -0.32638888919077697, 8 | -0.32638888919077697, 9 | 0.326388889190777, 10 | 0.3263888891907771, 11 | -0.32638888919076764, 12 | -0.3263888891907678, 13 | 0.3263888891907685, 14 | 0.32638888919076825, 15 | -0.6736111108092158, 16 | -0.6736111108092157, 17 | 0.673611110809216, 18 | 0.673611110809216, 19 | -0.6736111108091964, 20 | -0.6736111108091967, 21 | 0.6736111108091979, 22 | 0.6736111108091978, 23 | -9.223124356277735e-19, 24 | -8.712809288657902e-15, 25 | -1.7322067121175332e-18, 26 | -8.740821860413386e-15, 27 | 4.758632542432742e-19, 28 | 7.85007347909538e-15, 29 | -5.483910992498133e-19, 30 | 8.058497292742751e-15, 31 | -2.1504506166863647e-18, 32 | -1.8016067025196946e-14, 33 | -2.8539707191687805e-18, 34 | -1.8043051612662872e-14, 35 | 6.648385869463538e-20, 36 | 1.628365651446408e-14, 37 | -6.902233299422461e-19, 38 | 1.6435541192278395e-14 39 | ] 40 | ], 41 | "dtype": "float64", 42 | "shape": [ 43 | 1, 44 | 32 45 | ] 46 | }, 47 | "twist": { 48 | "__ndarray__": [ 49 | [ 50 | -3.44311119236244e-20, 51 | 2.260655507968335e-18, 52 | -8.673617379884035e-18 53 | ] 54 | ], 55 | "dtype": "float64", 56 | "shape": [ 57 | 1, 58 | 3 59 | ] 60 | } 61 | } 62 | }, 63 | "funcs_base": { 64 | "DVCon1_volume_constraint_0": 3.999999999999967 65 | } 66 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryCST_e63.ref: -------------------------------------------------------------------------------- 1 | { 2 | "lowerCST": { 3 | "__ndarray__": [ 4 | -0.041052455781363316, 5 | 0.0988276492914589, 6 | -0.029709516208264605, 7 | 0.22876930789750818, 8 | -0.036213269084885584, 9 | 0.2835255955178898, 10 | 0.06942606140458908, 11 | 0.3682053931683023 12 | ], 13 | "dtype": "float64", 14 | "shape": [ 15 | 8 16 | ] 17 | }, 18 | "upperCST": { 19 | "__ndarray__": [ 20 | 0.11430159218390888, 21 | 0.21707988867569167, 22 | 0.07307160384005389, 23 | 0.3391956670398648, 24 | 0.01789477900865324, 25 | 0.40784005800063433, 26 | 0.09973565405701679, 27 | 0.491975692194145 28 | ], 29 | "dtype": "float64", 30 | "shape": [ 31 | 8 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryCST_naca0012.ref: -------------------------------------------------------------------------------- 1 | { 2 | "upperCST": { 3 | "__ndarray__": [ 4 | 0.17346139862235613, 5 | 0.14967142058169658, 6 | 0.17786970157346033, 7 | 0.12162537931229431, 8 | 0.17125918055294803, 9 | 0.12158018412794566, 10 | 0.14839760541138639, 11 | 0.13864883886869403 12 | ], 13 | "dtype": "float64", 14 | "shape": [ 15 | 8 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryCST_naca0012_sharp.ref: -------------------------------------------------------------------------------- 1 | { 2 | "upperCST": { 3 | "__ndarray__": [ 4 | 0.1733353255372565, 5 | 0.1520691142858227, 6 | 0.1722198593787986, 7 | 0.13841157793390244, 8 | 0.14689191790823877, 9 | 0.15727904811194815, 10 | 0.11966039028011803, 11 | 0.17921189097239776 12 | ], 13 | "dtype": "float64", 14 | "shape": [ 15 | 8 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryCST_naca0012_zeroLE.ref: -------------------------------------------------------------------------------- 1 | { 2 | "upperCST": { 3 | "__ndarray__": [ 4 | 0.1730958381222164, 5 | 0.1507233077236812, 6 | 0.17568197294751375, 7 | 0.12476167835810108, 8 | 0.1680704042863872, 9 | 0.12387230428720869, 10 | 0.14725535729854697, 11 | 0.13902121127396566 12 | ], 13 | "dtype": "float64", 14 | "shape": [ 15 | 8 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryCST_naca2412.ref: -------------------------------------------------------------------------------- 1 | { 2 | "lowerCST": { 3 | "__ndarray__": [ 4 | -0.16615767624403266, 5 | -0.08987585351577812, 6 | -0.15582142165311424, 7 | -0.017159899575701378, 8 | -0.17153227428794338, 9 | -0.019655184707028376, 10 | -0.10547303290473756, 11 | -0.06541772277770336 12 | ], 13 | "dtype": "float64", 14 | "shape": [ 15 | 8 16 | ] 17 | }, 18 | "upperCST": { 19 | "__ndarray__": [ 20 | 0.18018474179350882, 21 | 0.2110669552431756, 22 | 0.1966232347728433, 23 | 0.23082903912163238, 24 | 0.16614792941519352, 25 | 0.2269965790418504, 26 | 0.18957112749197577, 27 | 0.21246014723030537 28 | ], 29 | "dtype": "float64", 30 | "shape": [ 31 | 8 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryESP_01.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx_airfoil_p0": { 3 | "cst_l": { 4 | "__ndarray__": [ 5 | [ 6 | 19.063824263798153, 7 | 13.011874363439224, 8 | 9.53331252155413, 9 | 7.571439478404322, 10 | 5.596590913832653, 11 | 3.451727046284834, 12 | 1.7272408953040923, 13 | 0.7136469024808363, 14 | 0.25074404799612815, 15 | 0.07165288073278374, 16 | 0.00830989976254964, 17 | -0.009764335262777049, 18 | -0.008682124317484542 19 | ] 20 | ], 21 | "dtype": "float64", 22 | "shape": [ 23 | 1, 24 | 13 25 | ] 26 | }, 27 | "cst_u": { 28 | "__ndarray__": [ 29 | [ 30 | -1.1380046462991045, 31 | 5.605165749830778, 32 | 7.226975442918927, 33 | 7.253325240584455, 34 | 5.914371156824351, 35 | 3.878564268016957, 36 | 2.110505343655997, 37 | 1.0188227425958265, 38 | 0.4645736757688095, 39 | 0.20085590710672654, 40 | 0.08053288141063071, 41 | 0.03167646367548533, 42 | 0.012292178444587508 43 | ] 44 | ], 45 | "dtype": "float64", 46 | "shape": [ 47 | 1, 48 | 13 49 | ] 50 | } 51 | }, 52 | "dIdx_airfoil_p1": { 53 | "cst_l": { 54 | "__ndarray__": [ 55 | [ 56 | 18.86861949138072, 57 | 11.839701587760269, 58 | 8.431357738203705, 59 | 6.833105287260199, 60 | 5.172037830689348, 61 | 3.240017822409042, 62 | 1.6597080990463837, 63 | 0.7474012709634649, 64 | 0.3379150139959855, 65 | 0.1577813664968781, 66 | 0.060132539931186266, 67 | 0.009214115458988762, 68 | -0.004961622553550813 69 | ] 70 | ], 71 | "dtype": "float64", 72 | "shape": [ 73 | 1, 74 | 13 75 | ] 76 | }, 77 | "cst_u": { 78 | "__ndarray__": [ 79 | [ 80 | -1.09965123414892, 81 | 5.0377003045016515, 82 | 6.491536235580781, 83 | 6.555327639335311, 84 | 5.363044266745283, 85 | 3.5070821630431377, 86 | 1.8733581702674738, 87 | 0.8548755582061015, 88 | 0.34954435455874805, 89 | 0.13391309240716265, 90 | 0.05249072855771583, 91 | 0.023737444382421667, 92 | 0.010687989262101908 93 | ] 94 | ], 95 | "dtype": "float64", 96 | "shape": [ 97 | 1, 98 | 13 99 | ] 100 | } 101 | }, 102 | "dIdx_airfoil_p2": { 103 | "cst_l": { 104 | "__ndarray__": [ 105 | [ 106 | 17.87962054787919, 107 | 10.84743977457232, 108 | 7.867729659741041, 109 | 6.507422554683231, 110 | 4.95153816022192, 111 | 3.1107218083711095, 112 | 1.6276791513590827, 113 | 0.7862873641110544, 114 | 0.4012231894987814, 115 | 0.20972298105431378, 116 | 0.08851272837210905, 117 | 0.019271180742785325, 118 | -0.0030489689782507044 119 | ] 120 | ], 121 | "dtype": "float64", 122 | "shape": [ 123 | 1, 124 | 13 125 | ] 126 | }, 127 | "cst_u": { 128 | "__ndarray__": [ 129 | [ 130 | -1.1667412493098264, 131 | 4.718142195620945, 132 | 6.135625167015937, 133 | 6.2613239615458465, 134 | 5.219155515291938, 135 | 3.513909358857746, 136 | 1.9744628835253664, 137 | 0.9869750839241316, 138 | 0.4612005230402365, 139 | 0.1994868093195788, 140 | 0.07773591398882015, 141 | 0.029013887372442104, 142 | 0.010783684527612475 143 | ] 144 | ], 145 | "dtype": "float64", 146 | "shape": [ 147 | 1, 148 | 13 149 | ] 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryESP_02.ref: -------------------------------------------------------------------------------- 1 | { 2 | "Composite DVs :": { 3 | "__ndarray__": [ 4 | [ 5 | -2.0, 6 | -2.0, 7 | -1.999999 8 | ] 9 | ], 10 | "dtype": "float64", 11 | "shape": [ 12 | 1, 13 | 3 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryMulti.ref: -------------------------------------------------------------------------------- 1 | { 2 | "ptsUpdated": { 3 | "__ndarray__": [ 4 | [ 5 | 0.00015229324522606294, 6 | -0.008724874175625251, 7 | 0.0 8 | ], 9 | [ 10 | 0.49680087946893736, 11 | 0.09994415914341713, 12 | -0.5000000000000001 13 | ], 14 | [ 15 | 0.49999999999999994, 16 | 1.734723475976807e-18, 17 | 1.9999999999999993 18 | ], 19 | [ 20 | 0.3748051686152182, 21 | 0.12479824619498066, 22 | 1.9999999999999996 23 | ], 24 | [ 25 | 2.4999999999999996, 26 | 0.4999999999999999, 27 | 0.0 28 | ], 29 | [ 30 | 0.300010152883015, 31 | -0.000581658278375018, 32 | 0.5000000000000001 33 | ], 34 | [ 35 | 0.7558449426479021, 36 | -0.2439473847057298, 37 | 0.5999997165630315 38 | ], 39 | [ 40 | 0.2560526152942702, 41 | -0.2558449426479023, 42 | 0.5999997165630314 43 | ], 44 | [ 45 | 0.244155058456546, 46 | 0.2439473858494195, 47 | 0.5999999999999999 48 | ], 49 | [ 50 | 0.24972186196458135, 51 | 0.09926785071052377, 52 | 0.5000000000000001 53 | ], 54 | [ 55 | 0.5053277931846962, 56 | -0.2999322798665185, 57 | 0.49999999999999994 58 | ], 59 | [ 60 | 0.24708313975314067, 61 | 0.1136576670970726, 62 | 0.5099996220283098 63 | ], 64 | [ 65 | 0.4991275125824375, 66 | 0.2999847706754774, 67 | 0.5 68 | ], 69 | [ 70 | 0.36910314030476443, 71 | 0.24692177477292812, 72 | 0.6 73 | ], 74 | [ 75 | 0.24928270996440818, 76 | 0.25026018528384725, 77 | 0.5000000000000001 78 | ], 79 | [ 80 | 0.49927001886063926, 81 | 0.250987258131816, 82 | 0.5 83 | ], 84 | [ 85 | 0.5034043514208585, 86 | 0.2501560711472582, 87 | 0.399999734349053 88 | ], 89 | [ 90 | 0.7439510052288243, 91 | 0.25569291087605306, 92 | 0.6000000785150911 93 | ], 94 | [ 95 | 0.4940512221529827, 96 | 0.2498961636964368, 97 | 0.5999999999999999 98 | ], 99 | [ 100 | 0.24999999999999997, 101 | 0.4999999999999999, 102 | 0.6 103 | ], 104 | [ 105 | 0.5058672786729258, 106 | -0.24989810383051725, 107 | 0.5999997144892112 108 | ], 109 | [ 110 | 0.24999999999999997, 111 | -0.49999999999999994, 112 | 0.5999999999999999 113 | ] 114 | ], 115 | "dtype": "float64", 116 | "shape": [ 117 | 22, 118 | 3 119 | ] 120 | } 121 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryVSP_01.ref: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryVSP_02.ref: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometryVSP_03.ref: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometry_01.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "mainX": { 4 | "__ndarray__": [ 5 | [ 6 | 0.49999999999999933 7 | ], 8 | [ 9 | 0.0 10 | ], 11 | [ 12 | 0.0 13 | ], 14 | [ 15 | 0.7000000000000006 16 | ], 17 | [ 18 | 0.0 19 | ], 20 | [ 21 | 0.0 22 | ] 23 | ], 24 | "dtype": "float64", 25 | "shape": [ 26 | 6, 27 | 1 28 | ] 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometry_03.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "mainX": { 4 | "__ndarray__": [ 5 | [ 6 | 0.2999999999999997 7 | ], 8 | [ 9 | 0.0 10 | ], 11 | [ 12 | 0.0 13 | ], 14 | [ 15 | 0.10000000000000175 16 | ], 17 | [ 18 | 0.0 19 | ], 20 | [ 21 | 0.0 22 | ] 23 | ], 24 | "dtype": "float64", 25 | "shape": [ 26 | 6, 27 | 1 28 | ] 29 | }, 30 | "nestedX": { 31 | "__ndarray__": [ 32 | [ 33 | 0.2500000000000002 34 | ], 35 | [ 36 | 0.0 37 | ], 38 | [ 39 | 0.0 40 | ], 41 | [ 42 | 0.7499999999999996 43 | ], 44 | [ 45 | 0.0 46 | ], 47 | [ 48 | 0.0 49 | ] 50 | ], 51 | "dtype": "float64", 52 | "shape": [ 53 | 6, 54 | 1 55 | ] 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometry_21.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "mainAxis": { 4 | "__ndarray__": [ 5 | [ 6 | 0.0 7 | ], 8 | [ 9 | 0.5141811677711328 10 | ], 11 | [ 12 | 0.5141811677766839 13 | ] 14 | ], 15 | "dtype": "float64", 16 | "shape": [ 17 | 3, 18 | 1 19 | ] 20 | }, 21 | "x_axis": { 22 | "__ndarray__": [ 23 | [ 24 | 0.04531743762470341, 25 | 0.04511238028692687, 26 | 0.04511238028692687, 27 | 0.20581764057503446, 28 | 0.044908250818043165, 29 | 0.20488633422077737, 30 | 0.20488633422077737, 31 | 0.2039592419372793 32 | ], 33 | [ 34 | 0.0, 35 | 0.0, 36 | 0.0, 37 | 0.0, 38 | 0.0, 39 | 0.0, 40 | 0.0, 41 | 0.0 42 | ], 43 | [ 44 | 0.0, 45 | 0.0, 46 | 0.0, 47 | 0.0, 48 | 0.0, 49 | 0.0, 50 | 0.0, 51 | 0.0 52 | ] 53 | ], 54 | "dtype": "float64", 55 | "shape": [ 56 | 3, 57 | 8 58 | ] 59 | }, 60 | "y_axis": { 61 | "__ndarray__": [ 62 | [ 63 | 0.0, 64 | 0.0, 65 | 0.0, 66 | 0.0, 67 | 0.0, 68 | 0.0, 69 | 0.0, 70 | 0.0 71 | ], 72 | [ 73 | 0.0, 74 | 0.0, 75 | 0.0, 76 | 0.0, 77 | 0.0, 78 | 0.0, 79 | 0.0, 80 | 0.0 81 | ], 82 | [ 83 | 0.0, 84 | 0.0, 85 | 0.0, 86 | 0.0, 87 | 0.0, 88 | 0.0, 89 | 0.0, 90 | 0.0 91 | ] 92 | ], 93 | "dtype": "float64", 94 | "shape": [ 95 | 3, 96 | 8 97 | ] 98 | }, 99 | "z_axis": { 100 | "__ndarray__": [ 101 | [ 102 | 0.0, 103 | 0.0, 104 | 0.0, 105 | 0.0, 106 | 0.0, 107 | 0.0, 108 | 0.0, 109 | 0.0 110 | ], 111 | [ 112 | 0.03204426745617717, 113 | 0.03189927001989723, 114 | 0.03189927001989723, 115 | 0.14553504935066464, 116 | 0.031754928681015926, 117 | 0.14487651631145404, 118 | 0.14487651631145404, 119 | 0.14422096306643262 120 | ], 121 | [ 122 | 0.03204426746172828, 123 | 0.031899270025448345, 124 | 0.031899270025448345, 125 | 0.14553504935621575, 126 | 0.03175492868656704, 127 | 0.14487651630590292, 128 | 0.14487651630590292, 129 | 0.1442209630608815 130 | ] 131 | ], 132 | "dtype": "float64", 133 | "shape": [ 134 | 3, 135 | 8 136 | ] 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometry_23.ref: -------------------------------------------------------------------------------- 1 | { 2 | "RefAxis_nodes_coord": { 3 | "__ndarray__": [ 4 | [ 5 | -0.4, 6 | -0.09999999999999998, 7 | 0.0 8 | ], 9 | [ 10 | -0.4, 11 | -0.09999999999999998, 12 | 3.32194917 13 | ], 14 | [ 15 | -0.4, 16 | -0.09999999999999998, 17 | 5.78384945 18 | ], 19 | [ 20 | -0.4, 21 | -0.09999999999999998, 22 | 8.0 23 | ] 24 | ], 25 | "dtype": "float64", 26 | "shape": [ 27 | 4, 28 | 3 29 | ] 30 | } 31 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_DVGeometry_25.ref: -------------------------------------------------------------------------------- 1 | { 2 | "Composite DVs :": { 3 | "__ndarray__": [ 4 | 1e-06, 5 | 30.500001, 6 | 32.500001, 7 | 34.000001999999995, 8 | 26.000001 9 | ], 10 | "dtype": "float64", 11 | "shape": [ 12 | 5 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_custom_ray_projections.ref: -------------------------------------------------------------------------------- 1 | { 2 | "links_x": { 3 | "__ndarray__": [ 4 | [ 5 | -1.0, 6 | -0.5, 7 | 0.0 8 | ], 9 | [ 10 | 1.0, 11 | -0.5, 12 | 0.0 13 | ], 14 | [ 15 | -1.0, 16 | 0.5, 17 | -0.5000000000000001 18 | ], 19 | [ 20 | 1.0, 21 | 0.5, 22 | -0.5000000000000001 23 | ], 24 | [ 25 | -1.0, 26 | -0.5, 27 | 0.4999999999999991 28 | ], 29 | [ 30 | 1.0, 31 | -0.5, 32 | 0.4999999999999991 33 | ], 34 | [ 35 | -1.0, 36 | 0.5, 37 | 0.0 38 | ], 39 | [ 40 | 1.0, 41 | 0.5, 42 | 0.0 43 | ], 44 | [ 45 | -0.33333333, 46 | -0.5, 47 | 0.0 48 | ], 49 | [ 50 | 0.33333333, 51 | -0.5, 52 | 0.0 53 | ], 54 | [ 55 | -0.33333333, 56 | 0.5, 57 | -0.49999999999999994 58 | ], 59 | [ 60 | 0.33333333, 61 | 0.5, 62 | -0.5 63 | ], 64 | [ 65 | -0.33333333, 66 | -0.5, 67 | 0.4999999999999991 68 | ], 69 | [ 70 | 0.33333333, 71 | -0.5, 72 | 0.5 73 | ], 74 | [ 75 | -0.33333333, 76 | 0.5, 77 | 0.0 78 | ], 79 | [ 80 | 0.33333333, 81 | 0.5, 82 | 0.0 83 | ], 84 | [ 85 | -1.0, 86 | -0.5, 87 | 0.5 88 | ], 89 | [ 90 | -1.0, 91 | -0.5, 92 | 0.4999999999999991 93 | ], 94 | [ 95 | 1.0, 96 | -0.5, 97 | 0.5 98 | ], 99 | [ 100 | 1.0, 101 | -0.5, 102 | 0.4999999999999991 103 | ], 104 | [ 105 | -1.0, 106 | 0.5, 107 | -0.5 108 | ], 109 | [ 110 | -1.0, 111 | 0.5, 112 | -0.5 113 | ], 114 | [ 115 | 1.0, 116 | 0.5, 117 | -0.5 118 | ], 119 | [ 120 | 1.0, 121 | 0.5, 122 | -0.5 123 | ], 124 | [ 125 | -0.33333333, 126 | -0.5, 127 | 0.5 128 | ], 129 | [ 130 | -0.33333333, 131 | -0.5, 132 | 0.4999999999999991 133 | ], 134 | [ 135 | 0.33333333, 136 | -0.5, 137 | 0.5 138 | ], 139 | [ 140 | 0.33333333, 141 | -0.5, 142 | 0.4999999999999991 143 | ], 144 | [ 145 | -0.33333333, 146 | 0.5, 147 | -0.5 148 | ], 149 | [ 150 | -0.33333333, 151 | 0.5, 152 | -0.5 153 | ], 154 | [ 155 | 0.33333333, 156 | 0.5, 157 | -0.5 158 | ], 159 | [ 160 | 0.33333333, 161 | 0.5, 162 | -0.5 163 | ] 164 | ], 165 | "dtype": "float64", 166 | "shape": [ 167 | 32, 168 | 3 169 | ] 170 | } 171 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_proximity_constraints.ref: -------------------------------------------------------------------------------- 1 | { 2 | "funcs_base": { 3 | "proximity1": 0.10000000000000142, 4 | "proximity2": 1.0000000000000004 5 | }, 6 | "funcs_new": { 7 | "proximity1": 0.15000000000000144, 8 | "proximity2": 0.9500000000000004 9 | }, 10 | "sens_base": { 11 | "proximity1": { 12 | "child1_x_disp": -0.9999999999999999, 13 | "child2_x_disp": 1.0 14 | }, 15 | "proximity2": { 16 | "child1_x_disp": 0.0, 17 | "child2_x_disp": -1.0 18 | } 19 | }, 20 | "sens_new": { 21 | "proximity1": { 22 | "child1_x_disp": -0.9999999999999999, 23 | "child2_x_disp": 1.0 24 | }, 25 | "proximity2": { 26 | "child1_x_disp": 0.0, 27 | "child2_x_disp": -0.9999999999999999 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_pyGeo.ref: -------------------------------------------------------------------------------- 1 | { 2 | "sum of surface data": 339809.4700319107 3 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_shape_function_dv.ref: -------------------------------------------------------------------------------- 1 | { 2 | "dIdx": { 3 | "shape_func": { 4 | "__ndarray__": [ 5 | [ 6 | 0.0, 7 | 0.0 8 | ], 9 | [ 10 | 0.25313918274817326, 11 | -0.028126575860907277 12 | ], 13 | [ 14 | 0.0, 15 | 0.0 16 | ], 17 | [ 18 | 0.0, 19 | 0.0 20 | ], 21 | [ 22 | 0.11838133956666497, 23 | -0.05073485981428538 24 | ], 25 | [ 26 | 0.0, 27 | 0.0 28 | ] 29 | ], 30 | "dtype": "float64", 31 | "shape": [ 32 | 6, 33 | 2 34 | ] 35 | } 36 | }, 37 | "new_pts": { 38 | "__ndarray__": [ 39 | [ 40 | 0.25, 41 | 0.4984430155131783, 42 | 3.9999999999999996 43 | ], 44 | [ 45 | -0.8000000000000002, 46 | 0.20845580996904764, 47 | 7.000000000000001 48 | ] 49 | ], 50 | "dtype": "float64", 51 | "shape": [ 52 | 2, 53 | 3 54 | ] 55 | } 56 | } -------------------------------------------------------------------------------- /tests/reg_tests/ref/test_vol_bounds.ref: -------------------------------------------------------------------------------- 1 | { 2 | "pts0_1": { 3 | "__ndarray__": [ 4 | [ 5 | 0.4999999999999998, 6 | -0.5, 7 | -0.1999999999999998 8 | ], 9 | [ 10 | 0.5, 11 | -0.5, 12 | 0.1999999999999999 13 | ], 14 | [ 15 | 0.0, 16 | 0.0, 17 | -0.5 18 | ] 19 | ], 20 | "dtype": "float64", 21 | "shape": [ 22 | 3, 23 | 3 24 | ] 25 | }, 26 | "pts0_2": { 27 | "__ndarray__": [ 28 | [ 29 | 0.6000000000000001, 30 | -0.5, 31 | -0.19999999999999996 32 | ], 33 | [ 34 | 0.6000000000000001, 35 | -0.5000000000000001, 36 | 0.19999999999999996 37 | ], 38 | [ 39 | 0.6000000000000001, 40 | 2.7755575615628914e-17, 41 | -0.5000000000000001 42 | ] 43 | ], 44 | "dtype": "float64", 45 | "shape": [ 46 | 3, 47 | 3 48 | ] 49 | }, 50 | "pts1_1": { 51 | "__ndarray__": [ 52 | [ 53 | 1.75, 54 | -0.5, 55 | -0.1999999999999998 56 | ], 57 | [ 58 | 1.75, 59 | -0.5, 60 | 0.0 61 | ], 62 | [ 63 | 1.25, 64 | 0.4999999999999999, 65 | -0.5 66 | ] 67 | ], 68 | "dtype": "float64", 69 | "shape": [ 70 | 3, 71 | 3 72 | ] 73 | }, 74 | "pts1_2": { 75 | "__ndarray__": [ 76 | [ 77 | 1.75, 78 | -0.5, 79 | -0.1999999999999998 80 | ], 81 | [ 82 | 1.7499999999999998, 83 | -0.5000000000000001, 84 | 0.1999999999999999 85 | ], 86 | [ 87 | 1.25, 88 | 0.4999999999999999, 89 | -0.5 90 | ] 91 | ], 92 | "dtype": "float64", 93 | "shape": [ 94 | 3, 95 | 3 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /tests/reg_tests/test_examples.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | import subprocess 4 | import unittest 5 | 6 | # External modules 7 | from parameterized import parameterized 8 | 9 | baseDir = os.path.dirname(os.path.abspath(__file__)) 10 | 11 | 12 | class TestExamples(unittest.TestCase): 13 | def setUp(self): 14 | self.cwd = os.getcwd() 15 | self.output_file_list = [] 16 | 17 | def common_test(self, test_dir, run_file, args=None): 18 | """ 19 | This function runs a given Python script, makes sure it does not exit with an error code, 20 | and then checks that the output files are created. 21 | 22 | Parameters 23 | ---------- 24 | test_dir : str 25 | The file directory, relative to where this file is 26 | run_file : str 27 | The name of the python script to run 28 | args : list, optional 29 | The list of command line arguments, by default None 30 | """ 31 | if args is None: 32 | args = [] 33 | full_test_dir = os.path.abspath(os.path.join(baseDir, "../../examples", test_dir)) 34 | os.chdir(full_test_dir) 35 | cmd = ["python", run_file] + args 36 | subprocess.check_call(cmd) 37 | for f in self.output_file_list: 38 | self.assertTrue(os.path.isfile(f)) 39 | 40 | def test_bwb(self): 41 | self.output_file_list = ["bwb.igs"] 42 | self.common_test("bwb", "bwb.py") 43 | 44 | def test_c172(self): 45 | self.output_file_list = [ 46 | "c172_sharp_te_rounded_tip.dat", 47 | "c172_sharp_te_rounded_tip.igs", 48 | "c172_sharp_te_pinched_tip.dat", 49 | "c172_sharp_te_pinched_tip.igs", 50 | "c172_sharp_te_rounded_tip_fitted.dat", 51 | "c172_sharp_te_rounded_tip_fitted.igs", 52 | "c172_blunt_te_rounded_tip.dat", 53 | "c172_blunt_te_rounded_tip.igs", 54 | "c172_rounded_te_rounded_tip.dat", 55 | "c172_rounded_te_rounded_tip.igs", 56 | ] 57 | self.common_test("c172_wing", "c172.py") 58 | 59 | @parameterized.expand(["iges", "plot3d", "liftingsurface"]) 60 | def test_deform(self, input_type): 61 | self.output_file_list = ["wingNew.plt"] 62 | self.common_test("deform_geometry", "runScript.py", args=["--input_type", input_type]) 63 | 64 | def tearDown(self): 65 | for f in self.output_file_list: 66 | os.remove(f) 67 | os.chdir(self.cwd) 68 | -------------------------------------------------------------------------------- /tests/reg_tests/test_ffdGeneration.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | import unittest 4 | 5 | # External modules 6 | import numpy as np 7 | from stl.mesh import Mesh 8 | 9 | # First party modules 10 | from pygeo import DVGeometry 11 | from pygeo.geo_utils import createFittedWingFFD, write_wing_FFD_file 12 | 13 | baseDir = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | 16 | class TestFFDGeneration(unittest.TestCase): 17 | N_PROCS = 1 18 | 19 | def test_box_ffd(self, train=False, refDeriv=False): 20 | # Write duplicate of outerBoxFFD 21 | axes = ["i", "k", "j"] 22 | slices = np.array( 23 | [ 24 | # Slice 1 25 | [[[-1, -1, -1], [-1, 1, -1]], [[-1, -1, 1], [-1, 1, 1]]], 26 | # Slice 2 27 | [[[1, -1, -1], [1, 1, -1]], [[1, -1, 1], [1, 1, 1]]], 28 | # Slice 3 29 | [[[2, -1, -1], [2, 1, -1]], [[2, -1, 1], [2, 1, 1]]], 30 | ] 31 | ) 32 | 33 | N0 = [2, 2] 34 | N1 = [2, 2] 35 | N2 = [2, 2] 36 | 37 | copyName = os.path.join(baseDir, "../../input_files/test1.xyz") 38 | write_wing_FFD_file(copyName, slices, N0, N1, N2, axes=axes) 39 | 40 | # Load original and duplicate 41 | origFFD = DVGeometry(os.path.join(baseDir, "../../input_files/outerBoxFFD.xyz")) 42 | copyFFD = DVGeometry(copyName) 43 | np.testing.assert_allclose(origFFD.FFD.coef, copyFFD.FFD.coef, rtol=1e-7) 44 | 45 | # Delete the duplicate FFD file 46 | os.remove(copyName) 47 | 48 | def test_c172_fitted(self): 49 | # Scale all dimensions from millimeters to meters so that the tolerances match a regular use case 50 | leList = np.array([[0.0, 0.0, 0.1], [10.0, 0.0, 2500.0], [160.0, 0.0, 5280.0]]) * 1e-3 51 | teList = np.array([[1600.0, 0.0, 0.1], [1650.0, 0.0, 2500.0], [1320.0, 0.0, 5280.0]]) * 1e-3 52 | 53 | # Get the surface definition from the STL file 54 | meshFile = os.path.join(baseDir, "../../input_files/c172.stl") 55 | stlMesh = Mesh.from_file(meshFile) 56 | p0 = stlMesh.vectors[:, 0, :] * 1e-3 57 | p1 = stlMesh.vectors[:, 1, :] * 1e-3 58 | p2 = stlMesh.vectors[:, 2, :] * 1e-3 59 | surf = [p0, p1, p2] 60 | surfFormat = "point-point" 61 | 62 | # Set the other FFD generation inputs 63 | outFile = "wing_ffd.xyz" 64 | nSpan = [4, 4] 65 | nChord = 8 66 | relMargins = [0.02, 0.01, 0.01] 67 | absMargins = [0.04, 0.01, 0.02] 68 | liftIndex = 2 69 | 70 | createFittedWingFFD(surf, surfFormat, outFile, leList, teList, nSpan, nChord, absMargins, relMargins, liftIndex) 71 | 72 | # Check that the generated FFD file matches the reference 73 | referenceFFD = DVGeometry(os.path.join(baseDir, "../../input_files/c172_fitted.xyz")) 74 | outputFFD = DVGeometry(outFile) 75 | np.testing.assert_allclose(referenceFFD.FFD.coef, outputFFD.FFD.coef, rtol=1e-13) 76 | 77 | # Check that the embedding works 78 | # This is not an actual test because no errors are raised if the projection does not work 79 | DVGeo = DVGeometry(outFile) 80 | DVGeo.addPointSet(p0, "wing_p0") 81 | DVGeo.addPointSet(p1, "wing_p1") 82 | DVGeo.addPointSet(p2, "wing_p2") 83 | 84 | # Delete the generated FFD file 85 | os.remove(outFile) 86 | 87 | 88 | if __name__ == "__main__": 89 | unittest.main() 90 | -------------------------------------------------------------------------------- /tests/reg_tests/test_pyGeo.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import os 3 | import unittest 4 | 5 | # External modules 6 | from baseclasses import BaseRegTest 7 | import numpy as np 8 | 9 | # First party modules 10 | from pygeo import pyGeo 11 | 12 | baseDir = os.path.dirname(os.path.abspath(__file__)) 13 | 14 | 15 | class TestPyGeo(unittest.TestCase): 16 | def setUp(self): 17 | self.refFile = os.path.join(baseDir, "ref/test_pyGeo.ref") 18 | 19 | def train(self): 20 | with BaseRegTest(self.refFile, train=True) as handler: 21 | self.regTest(handler) 22 | handler.writeRef() 23 | 24 | def test(self): 25 | with BaseRegTest(self.refFile, train=False) as handler: 26 | self.regTest(handler) 27 | 28 | def regTest(self, handler): 29 | dirName = os.path.join(baseDir, "../../input_files") 30 | 31 | # Airfoil file 32 | airfoil_list = [dirName + "/rae2822.dat"] * 2 33 | naf = len(airfoil_list) # number of airfoils 34 | 35 | # Wing definition 36 | # Airfoil leading edge positions 37 | x = [0.0, 7.5] 38 | y = [0.0, 0.0] 39 | z = [0.0, 14.0] 40 | offset = np.zeros((naf, 2)) # x-y offset applied to airfoil position before scaling 41 | 42 | # Airfoil rotations 43 | rot_x = [0.0, 0.0] 44 | rot_y = [0.0, 0.0] 45 | rot_z = [0.0, 0.0] 46 | 47 | # Airfoil scaling 48 | chord = [5.0, 1.5] # chord lengths 49 | 50 | # Run pyGeo 51 | wing = pyGeo( 52 | "liftingSurface", 53 | xsections=airfoil_list, 54 | scale=chord, 55 | offset=offset, 56 | x=x, 57 | y=y, 58 | z=z, 59 | rotX=rot_x, 60 | rotY=rot_y, 61 | rotZ=rot_z, 62 | tip="rounded", 63 | bluntTe=True, 64 | squareTeTip=True, 65 | teHeight=0.25 * 0.0254, 66 | ) 67 | 68 | for isurf in range(wing.nSurf): 69 | wing.surfs[isurf].computeData() 70 | surf = wing.surfs[isurf].data 71 | handler.root_add_val("sum of surface data", sum(surf.flatten()), tol=1e-10) 72 | -------------------------------------------------------------------------------- /tests/reg_tests/warning_childFFD.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import unittest 3 | 4 | # External modules 5 | import numpy as np 6 | from test_Blocks import add_vars 7 | 8 | # First party modules 9 | from pygeo import DVGeometry, geo_utils 10 | 11 | 12 | class RegTestPyGeo(unittest.TestCase): 13 | N_PROCS = 1 14 | 15 | def make_cube_ffd(self, file_name, x0, y0, z0, dx, dy, dz): 16 | # Write cube ffd with i along x-axis, j along y-axis, and k along z-axis 17 | axes = ["k", "j", "i"] 18 | slices = np.array( 19 | # Slice 1 20 | [ 21 | [[[x0, y0, z0], [x0 + dx, y0, z0]], [[x0, y0 + dy, z0], [x0 + dx, y0 + dy, z0]]], 22 | # Slice 2 23 | [[[x0, y0, z0 + dz], [x0 + dx, y0, z0 + dz]], [[x0, y0 + dy, z0 + dz], [x0 + dx, y0 + dy, z0 + dz]]], 24 | ], 25 | dtype="d", 26 | ) 27 | 28 | N0 = [2] 29 | N1 = [2] 30 | N2 = [2] 31 | 32 | geo_utils.write_wing_FFD_file(file_name, slices, N0, N1, N2, axes=axes) 33 | 34 | def test_parent_shape_child_rot(self, train=False, refDeriv=False): 35 | ffd_name = "../../input_files/small_cube.xyz" 36 | self.make_cube_ffd(ffd_name, 0.1, 0.1, 0.1, 0.8, 0.8, 0.8) 37 | small = DVGeometry(ffd_name, child=True) 38 | small.addRefAxis("ref", xFraction=0.5, alignIndex="j") 39 | 40 | x0 = 0.0 41 | y0 = 0.0 42 | z0 = 0.0 43 | dx = 1.0 44 | dy = 1.0 45 | dz = 1.0 46 | 47 | axes = ["k", "j", "i"] 48 | slices = np.array( 49 | # Slice 1 50 | [ 51 | [ 52 | [[x0, y0, z0], [x0 + dx, y0, z0], [x0 + 2 * dx, y0, z0]], 53 | [[x0, y0 + dy, z0], [x0 + dx, y0 + dy, z0], [x0 + 2 * dx, y0 + dy, z0]], 54 | ], 55 | # Slice 2 56 | [ 57 | [[x0, y0, z0 + dz], [x0 + dx, y0, z0 + dz], [x0 + 2 * dx, y0, z0 + dz]], 58 | [[x0, y0 + dy, z0 + dz], [x0 + dx, y0 + dy, z0 + dz], [x0 + 2 * dx, y0 + dy, z0 + dz]], 59 | ], 60 | ], 61 | dtype="d", 62 | ) 63 | 64 | N0 = [2] 65 | N1 = [2] 66 | N2 = [3] 67 | ffd_name = "../../input_files/big_cube.xyz" 68 | 69 | geo_utils.write_wing_FFD_file(ffd_name, slices, N0, N1, N2, axes=axes) 70 | big = DVGeometry(ffd_name) 71 | big.addRefAxis("ref", xFraction=0.5, alignIndex="j") 72 | big.addChild(small) 73 | 74 | # Add point set 75 | points = np.array([[0.5, 0.5, 0.5]]) 76 | big.addPointSet(points, "X") 77 | 78 | # Add only translation variables 79 | add_vars(big, "big", local="z", rotate="y") 80 | add_vars(small, "small", rotate="y") 81 | 82 | ang = 45 83 | ang_r = np.deg2rad(ang) 84 | 85 | # Modify design variables 86 | x = big.getValues() 87 | 88 | # add a local shape change 89 | x["local_z_big"] = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5]) 90 | big.setDesignVars(x) 91 | Xs = big.update("X") 92 | 93 | # add a rotation of the child FFD 94 | x["rotate_y_small"] = ang 95 | big.setDesignVars(x) 96 | Xrot_ffd = big.update("X") 97 | 98 | # the modification caused by the child FFD should be the same as rotating the deformed point of the parent 99 | # (you would think) 100 | rot_mat = np.array([[np.cos(ang_r), 0, np.sin(ang_r)], [0, 1, 0], [-np.sin(ang_r), 0, np.cos(ang_r)]]) 101 | Xrot = np.dot(rot_mat, (Xs - points).T) + points.T 102 | 103 | np.testing.assert_array_almost_equal(Xrot_ffd.T, Xrot) 104 | 105 | 106 | if __name__ == "__main__": 107 | unittest.main() 108 | -------------------------------------------------------------------------------- /tests/unit_tests/test_import_guards.py: -------------------------------------------------------------------------------- 1 | # Standard Python modules 2 | import sys 3 | import unittest 4 | from unittest.mock import patch 5 | import warnings 6 | 7 | 8 | def ignore_warnings(test_func): 9 | def do_test(self, *args, **kwargs): 10 | with warnings.catch_warnings(): 11 | warnings.simplefilter("ignore") 12 | test_func(self, *args, **kwargs) 13 | 14 | return do_test 15 | 16 | 17 | class TestImportGuards(unittest.TestCase): 18 | N_PROCS = 1 19 | 20 | @ignore_warnings 21 | def test_DVGeometryVSP_import_openvsp(self): 22 | with patch.dict(sys.modules, {"openvsp": None}): 23 | # First party modules 24 | from pygeo import DVGeometryVSP 25 | 26 | with self.assertRaises(ImportError) as context: 27 | DVGeometryVSP("wing.vsp3") 28 | 29 | self.assertEqual( 30 | str(context.exception), 31 | "The OpenVSP Python API is required in order to use DVGeometryVSP. " 32 | + "Ensure OpenVSP is installed properly and can be found on your path.", 33 | ) 34 | 35 | @ignore_warnings 36 | def test_DVGeometryVSP_openvsp_out_of_date(self): 37 | class DummyOpenVSPModule: 38 | def __init__(self): 39 | pass 40 | 41 | def GetVSPVersion(self): 42 | return "OpenVSP 0.0.0" 43 | 44 | dummy_module = DummyOpenVSPModule() 45 | with patch.dict(sys.modules, {"openvsp": dummy_module}): 46 | with self.assertRaises(AttributeError) as context: 47 | # First party modules 48 | from pygeo import DVGeometryVSP 49 | 50 | DVGeometryVSP("wing.vsp3") 51 | 52 | self.assertEqual( 53 | str(context.exception), 54 | "Out of date version of OpenVSP detected. " 55 | + "OpenVSP 3.28.0 or greater is required in order to use DVGeometryVSP", 56 | ) 57 | 58 | @ignore_warnings 59 | def test_DVGeometryMulti_import_pysurf(self): 60 | with patch.dict(sys.modules, {"pysurf": None}): 61 | # First party modules 62 | from pygeo import DVGeometryMulti 63 | 64 | with self.assertRaises(ImportError) as context: 65 | DVGeometryMulti() 66 | 67 | self.assertEqual(str(context.exception), "pySurf is not installed and is required to use DVGeometryMulti.") 68 | 69 | @ignore_warnings 70 | def test_DVGeometryCST_import_prefoil(self): 71 | with patch.dict(sys.modules, {"prefoil": None}): 72 | # First party modules 73 | from pygeo import DVGeometryCST 74 | 75 | with self.assertRaises(ImportError) as context: 76 | DVGeometryCST("test.dat") 77 | 78 | self.assertEqual(str(context.exception), "preFoil is not installed and is required to use DVGeometryCST.") 79 | 80 | @ignore_warnings 81 | def test_DVGeometryESP_import_ocsm(self): 82 | with patch.dict(sys.modules, {"pyOCSM": None}): 83 | # First party modules 84 | from pygeo import DVGeometryESP 85 | 86 | with self.assertRaises(ImportError) as context: 87 | DVGeometryESP("test.csm") 88 | 89 | self.assertEqual(str(context.exception), "OCSM and pyOCSM must be installed to use DVGeometryESP.") 90 | 91 | 92 | if __name__ == "__main__": 93 | unittest.main() 94 | --------------------------------------------------------------------------------