├── .gitignore
├── EXTERNALTESTS
├── EXTERNALTESTSreadme.md
├── LICENSE
├── README.md
├── TESTCONFIG
├── TESTS
├── gpkitmodels
├── GP
│ ├── __init__.py
│ ├── aircraft
│ │ ├── __init__.py
│ │ ├── engine
│ │ │ ├── DF70
│ │ │ │ ├── DF35_maxPvh.csv
│ │ │ │ ├── Dataset_BSFC_kgKwh.csv
│ │ │ │ ├── Dataset_Power_Kw.csv
│ │ │ │ ├── Dataset_Torque_Nm.csv
│ │ │ │ ├── fitDF70.py
│ │ │ │ └── genericbsfcpower.csv
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── df70.py
│ │ │ ├── gas_engine.py
│ │ │ ├── powerBSFCfit.csv
│ │ │ ├── power_lawfit.csv
│ │ │ ├── powertobsfcfit.pdf
│ │ │ ├── powertobsfcfit.png
│ │ │ ├── powervsweightfit.pdf
│ │ │ └── powervsweightfit.png
│ │ ├── fuselage
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── cyl_fuse_drawing.ipe
│ │ │ ├── cyl_fuse_drawing.pdf
│ │ │ ├── cylindrical_fuselage.py
│ │ │ ├── ellipsoid_fuselage.pdf
│ │ │ ├── elliptical_fuselage.py
│ │ │ ├── fuel_tank.py
│ │ │ ├── fuselage.pdf
│ │ │ ├── fuselage_profile_drag
│ │ │ │ ├── fusedrag.csv
│ │ │ │ └── fusedragfit.py
│ │ │ ├── fuselage_skin.py
│ │ │ └── test_fuselage.py
│ │ ├── mission
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── breguet_endurance.py
│ │ │ └── mission.pdf
│ │ ├── motor
│ │ │ ├── __init__.py
│ │ │ ├── motor.py
│ │ │ └── motor_test.py
│ │ ├── prop
│ │ │ ├── __init__.py
│ │ │ ├── arccos_fit.py
│ │ │ ├── prop_test.py
│ │ │ └── propeller.py
│ │ ├── tail
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── empennage.py
│ │ │ ├── horizontal_tail.py
│ │ │ ├── tail_aero.py
│ │ │ ├── tail_boom.py
│ │ │ ├── tail_dragfit.csv
│ │ │ ├── tail_tests.py
│ │ │ ├── taildragpolar.pdf
│ │ │ ├── tailpolars
│ │ │ │ ├── genpolar.sh
│ │ │ │ ├── naca_cl0fits.py
│ │ │ │ └── nacasweep.sh
│ │ │ ├── tube_spar.py
│ │ │ └── vertical_tail.py
│ │ └── wing
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── arctan_fit.csv
│ │ │ ├── arctan_fit.py
│ │ │ ├── boxspar.py
│ │ │ ├── capspar.py
│ │ │ ├── constant_taper_chord.py
│ │ │ ├── gustloaddiagram.pdf
│ │ │ ├── gustloading.py
│ │ │ ├── jho1polarfit1.pdf
│ │ │ ├── jho1polars
│ │ │ ├── genpolar.sh
│ │ │ ├── jho1.dat
│ │ │ ├── jho1_polarfits.py
│ │ │ └── jho1polarsweep.sh
│ │ │ ├── jho_fitdata.csv
│ │ │ ├── sparloading.py
│ │ │ ├── tube_spar.py
│ │ │ ├── wing.py
│ │ │ ├── wing_core.py
│ │ │ ├── wing_skin.py
│ │ │ └── wing_test.py
│ ├── beam
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── beam.pdf
│ │ └── beam.py
│ └── materials
│ │ ├── __init__.py
│ │ ├── composite.py
│ │ └── foam.py
├── SP
│ ├── SimPleAC
│ │ ├── README.md
│ │ ├── SimPleAC.py
│ │ ├── SimPleAC_mission.py
│ │ ├── SimPleAC_multimission.py
│ │ ├── __init__.py
│ │ └── simpleac.pdf
│ ├── __init__.py
│ ├── aircraft
│ │ ├── __init__.py
│ │ ├── prop
│ │ │ ├── __init__.py
│ │ │ ├── dae51_fitdata.csv
│ │ │ ├── dae51polars
│ │ │ │ ├── dae51.dat
│ │ │ │ ├── dae51.txt
│ │ │ │ ├── dae51_polarfits.py
│ │ │ │ ├── dae51polarfit1.eps
│ │ │ │ └── polarstest.pdf
│ │ │ └── propeller.py
│ │ ├── tail
│ │ │ ├── __init__.py
│ │ │ └── tail_boom_flex.py
│ │ └── wing
│ │ │ ├── __init__.py
│ │ │ ├── boxspar.py
│ │ │ └── wing.py
│ └── atmosphere
│ │ ├── __init__.py
│ │ └── atmosphere.py
├── __init__.py
├── misc
│ ├── Economic Order Quantity
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── eoq.pdf
│ │ └── qsweep.pdf
│ ├── Moment of Inertia (cylindrical beam)
│ │ ├── MoICylinder.PNG
│ │ ├── README.md
│ │ ├── moi.pdf
│ │ ├── moi.py
│ │ └── tightness.png
│ ├── Net Present Value
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── npv.pdf
│ │ └── npv.py
│ ├── README.md
│ ├── Raymer Weights
│ │ ├── Blended Wing-Body
│ │ │ ├── MTOW_breakdown.eps
│ │ │ ├── OEW_breakdown.eps
│ │ │ ├── Raymer_Weights_Model.py
│ │ │ └── XFOIL_BWB.ipynb
│ │ └── Raymer_Exact.py
│ ├── default.latex
│ └── make_gpmd
└── tools
│ ├── __init__.py
│ ├── fit_constraintset.py
│ ├── ipynb2module.py
│ ├── summing_constraintset.py
│ └── xfoilWrapper.py
├── researchmodeltests.sh
├── runtests.sh
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # iPython checkpoint folders
2 | .ipynb_checkpoints/
3 |
4 | # MOSEK CLI folder
5 | gpkit_tmp/
6 |
7 | # gpkit markdown generated files
8 | *.generated.tex
9 | *.md.py
10 | *.md.tex.md
11 |
12 | # Byte-compiled / optimized / DLL files
13 | __pycache__/
14 | *.py[cod]
15 |
16 | # C extensions
17 | *.so
18 |
19 | # Distribution / packaging
20 | .Python
21 | env/
22 | bin/
23 | build/
24 | develop-eggs/
25 | dist/
26 | eggs/
27 | lib/
28 | lib64/
29 | parts/
30 | sdist/
31 | var/
32 | *.egg-info/
33 | .installed.cfg
34 | *.egg
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .coverage
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 |
48 | # Translations
49 | *.mo
50 |
51 | # Mr Developer
52 | .mr.developer.cfg
53 | .project
54 | .pydevproject
55 |
56 | # Rope
57 | .ropeproject
58 |
59 | # Django stuff:
60 | *.log
61 | *.pot
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # MATLAB autosave
67 | *.asv
68 |
69 | # CI test xmloutput
70 | test_reports/
71 | test_reports_nounits/
72 |
73 | # MacOSX
74 | *.DS_Store
75 |
76 | # vim
77 | *.swp
78 |
79 | # OSX
80 | .DS_Store
81 |
82 | # latex
83 | *.aux
84 | *.log
85 | *.synctex.gz
86 |
87 | # md files support files
88 | *.generated.tex
89 | *.tex.md
90 | *.md.py
91 |
92 | # xfoil polars
93 | *.pol
94 |
--------------------------------------------------------------------------------
/EXTERNALTESTS:
--------------------------------------------------------------------------------
1 | SPaircraft
2 | robust
3 | shopping
4 | gassolar
5 | jho
6 | turbofan
7 | solar
8 | gplibrary
9 | eVTOL
10 |
--------------------------------------------------------------------------------
/EXTERNALTESTSreadme.md:
--------------------------------------------------------------------------------
1 | Testing of external repositories
2 | ================================
3 |
4 | Directions
5 | ----------
6 |
7 | 2. In your repository's top level, create the file `TESTS`.
8 | Each line should be the local path to a `.py` file containing a `test()` function, like this:
9 |
10 | mission.py
11 | aircraft/wing/wing_spar.py
12 |
13 | 3. (optional) create a `TESTCONFIG` file in the same folder. The following options are supported:
14 | `pip install` specifies packages your repository requires,
15 | `gpkit-models` the branch of gpkit-models you're working on,
16 | and `skipsolvers` the solvers that cannot solve your code.
17 | Format the file like this:
18 |
19 | pip install : pandas
20 | gpkit-models branch : 1682
21 | skipsolvers : cvxopt
22 |
23 | 4. Test your repository locally by running `python -c "from gpkit.tests.from_paths import run; run()"`
24 | (consider saving this line as `runtests.sh` for easy access)
25 |
26 | 1. Submit a PR to add the name of your repository to the `EXTERNALTESTS` file in `gpkit-models`
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Convex Engineering
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gplibrary
2 |
3 | [](https://acdl.mit.edu/csi/job/CE_gplibrary_Push_research_models/)
4 |
5 | This repository contains those GP-/SP-compatible models that we consider well documented and general enough to be useful to multiple projects.
6 |
7 | * **Simple models with in-depth explanations** (good for learning GPkit)
8 | * [SimPleAC](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/SP/SimPleAC/simpleac.pdf): a basic aircraft model that captures the fundamental design tradeoffs
9 | * [Economic Order Quantity](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/misc/Economic%20Order%20Quantity/eoq.pdf): tradeoff between setup and holding costs
10 | * [Cylindrical Beam Moment of Inertia](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/misc/Moment%20of%20Inertia%20(cylindrical%20beam)/moi.pdf): GP approximation of cylindrical beam MOI
11 | * [Net Present Value](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/misc/Net%20Present%20Value/npv.pdf): financial tradeoff between cash and equipment
12 | * [Raymer Weights](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/misc/Raymer%20Weights): rule-of-thumb weight relations for aircraft design
13 | * **GP models**
14 | * Aircraft
15 | * [Wing Structural and Aero Models](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/aircraft/wing)
16 | * [Empennage](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/aircraft/tail): TailBoom, HorizontalTail, and VerticalTail inherit from the Wing model
17 | * [Mission](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/aircraft/mission): models that unify subsystems and flight profiles
18 | * [Fuselage](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/aircraft/fuselage): elliptical and cylindrical fuselage models
19 | * [IC Gas Engine Model](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/aircraft/engine)
20 | * [Bending Beam](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/GP/beam): discretized beam for distributed loads
21 | * **SP models**
22 | * Aircraft
23 | * [Tail Boom Flexibility](https://github.com/convexengineering/gplibrary/tree/master/gpkitmodels/SP/aircraft/tail/tail_boom_flex.py)
24 | * [Wing Spanwise Effectiveness](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/SP/aircraft/wing/wing.py)
25 | * Atmosphere
26 | * [Tony Tao's fits as (efficient) signomial equalities](https://github.com/convexengineering/gplibrary/blob/master/gpkitmodels/SP/atmosphere/atmosphere.py). Valid until 10,000m of altitude.
27 |
28 |
--------------------------------------------------------------------------------
/TESTCONFIG:
--------------------------------------------------------------------------------
1 | pip install : pandas, git+https://github.com/hoburg/gpfit.git
2 |
--------------------------------------------------------------------------------
/TESTS:
--------------------------------------------------------------------------------
1 | gpkitmodels/GP/aircraft/wing/wing_test.py
2 | gpkitmodels/GP/aircraft/tail/tail_tests.py
3 | gpkitmodels/GP/aircraft/fuselage/test_fuselage.py
4 | gpkitmodels/GP/aircraft/prop/prop_test.py
5 | gpkitmodels/GP/aircraft/motor/motor_test.py
6 | gpkitmodels/SP/SimPleAC/SimPleAC.py
7 | gpkitmodels/SP/SimPleAC/SimPleAC_mission.py
8 | gpkitmodels/SP/SimPleAC/SimPleAC_multimission.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/DF35_maxPvh.csv:
--------------------------------------------------------------------------------
1 | ft,kW,,RPM,P,BSFC,Q(nm)
2 | 0,2.25,,4000,0.2,0.62,0.477707006
3 | 3280.839895,1.97,,5000,0.38,0.51,0.72611465
4 | 6561.67979,1.69,,6000,0.64,0.405,1.01910828
5 | 9842.519685,1.44,,7000,1.02,0.34,1.392174704
6 | 13123.35958,1.2,,8000,1.5,0.32,1.791401274
7 | 16404.19948,0.99,,9000,2.13,0.32,2.261146497
8 | 19685.03937,0.79,,,,,
9 | 22965.87927,0.61,,,,,
10 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/Dataset_BSFC_kgKwh.csv:
--------------------------------------------------------------------------------
1 | RPM,BSFC,Power
3000.669344,0.680239521,0.062323205
3081.659973,0.668263473,0.067406877
3192.10174,0.653892216,0.07476995
3302.543507,0.637125749,0.082645353
3405.62249,0.622754491,0.090472716
3493.975904,0.610778443,0.097558981
3619.14324,0.594011976,0.108211161
3714.859438,0.582035928,0.116855572
3810.575636,0.57005988,0.125944049
3898.92905,0.558083832,0.134736888
3994.645248,0.546107784,0.144709665
4060.910308,0.536526946,0.151891842
4171.352075,0.522155689,0.164378363
4267.068273,0.510179641,0.17573295
4399.598394,0.493413174,0.192292545
4495.314592,0.481437126,0.20487178
4591.03079,0.469461078,0.217982687
4664.658635,0.45988024,0.2284365
4775.100402,0.445508982,0.244729568
4841.365462,0.438323353,0.254864062
4937.08166,0.426347305,0.269986208
5040.160643,0.414371257,0.286921518
5157.965194,0.40239521,0.307118096
5275.769746,0.390419162,0.328231705
5364.123159,0.380838323,0.344680602
5452.476573,0.371257485,0.361664775
5570.281124,0.361676647,0.385157371
5688.085676,0.354491018,0.409636063
5769.076305,0.349700599,0.427047404
5879.518072,0.34251497,0.451567804
5989.959839,0.335329341,0.477000251
6070.950469,0.332934132,0.496239732
6210.843373,0.328143713,0.530665943
6306.559572,0.325748503,0.555106662
6387.550201,0.323353293,0.57635854
6468.54083,0.320958084,0.598140821
6593.708166,0.318562874,0.63286237
6696.787149,0.318562874,0.662435732
6851.405622,0.316167665,0.708484326
6954.484605,0.316167665,0.740328118
7109.103079,0.316167665,0.789843487
7248.995984,0.318562874,0.836484757
7396.251673,0.318562874,0.887507917
7506.69344,0.320958084,0.927094736
7617.135207,0.323353293,0.967830251
7720.21419,0.325748503,1.006900515
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/Dataset_Power_Kw.csv:
--------------------------------------------------------------------------------
1 | RPM,P,
2 | 3641.231593,0.431137725,0.421407002
3 | 3678.045515,0.443113772,0.434056247
4 | 3714.859438,0.45508982,0.446953511
5 | 3751.67336,0.467065868,0.460101126
6 | 3788.487282,0.479041916,0.473501425
7 | 3840.026774,0.502994012,0.492690787
8 | 3876.840696,0.51497006,0.506707029
9 | 3913.654618,0.526946108,0.520983871
10 | 3943.105756,0.538922156,0.532594539
11 | 3979.919679,0.550898204,0.54734632
12 | 4009.370817,0.562874251,0.559339951
13 | 4068.273092,0.586826347,0.583844815
14 | 4119.812584,0.610778443,0.605858871
15 | 4178.714859,0.634730539,0.631680379
16 | 4237.617135,0.658682635,0.658217742
17 | 4296.519411,0.682634731,0.68548044
18 | 4348.058902,0.706586826,0.709937751
19 | 4399.598394,0.730538922,0.734963991
20 | 4465.863454,0.754491018,0.76798676
21 | 4524.76573,0.778443114,0.798149542
22 | 4561.579652,0.80239521,0.81739243
23 | 4598.393574,0.826347305,0.836938969
24 | 4642.570281,0.850299401,0.860798875
25 | 4686.746988,0.874251497,0.885103332
26 | 4760.374833,0.922155689,0.926609443
27 | 4826.639893,0.958083832,0.965045388
28 | 4929.718876,1.017964072,1.026901696
29 | 5003.34672,1.065868263,1.072649666
30 | 5091.700134,1.125748503,1.129297655
31 | 5157.965194,1.173652695,1.173054679
32 | 5216.86747,1.209580838,1.212876448
33 | 5275.769746,1.25748503,1.253580097
34 | 5327.309237,1.293413174,1.289926564
35 | 5364.123159,1.317365269,1.316309563
36 | 5437.751004,1.365269461,1.370138695
37 | 5533.467202,1.437125749,1.442261197
38 | 5621.820616,1.508982036,1.511019508
39 | 5688.085676,1.568862275,1.563983472
40 | 5791.164659,1.652694611,1.648783088
41 | 5864.792503,1.71257485,1.711175424
42 | 5953.145917,1.784431138,1.788078291
43 | 6019.410977,1.844311377,1.847227933
44 | 6100.401606,1.928143713,1.921257226
45 | 6188.75502,2.011976048,2.004220115
46 | 6262.382865,2.083832335,2.075134301
47 | 6350.736278,2.167664671,2.162392518
48 | 6431.726908,2.251497006,2.24447551
49 | 6505.354752,2.323353293,2.320857277
50 | 6556.894244,2.383233533,2.37533236
51 | 6608.433735,2.443113772,2.430644504
52 | 6659.973226,2.502994012,2.486799893
53 | 6704.149933,2.550898204,2.535608922
54 | 6748.32664,2.610778443,2.585045909
55 | 6799.866131,2.670658683,2.64352125
56 | 6866.131191,2.74251497,2.71997746
57 | 6925.033467,2.814371257,2.789151189
58 | 7006.024096,2.910179641,2.886146165
59 | 7064.926372,2.982035928,2.958068326
60 | 7138.554217,3.077844311,3.049621286
61 | 7219.544846,3.185628743,3.152467409
62 | 7285.809906,3.281437126,3.238296248
63 | 7366.800535,3.389221557,3.345274326
64 | 7455.153949,3.497005988,3.46460869
65 | 7499.330656,3.556886228,3.525314741
66 | 7565.595716,3.652694611,3.617683272
67 | 7609.772423,3.724550898,3.680140895
68 | 7661.311914,3.796407186,3.75390245
69 | 7705.488621,3.856287425,3.817897622
70 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/Dataset_Torque_Nm.csv:
--------------------------------------------------------------------------------
1 | RPM,Q,
2 | 3000.669344,0.754491018,0.753469916
3 | 3125.83668,0.826347305,0.815971129
4 | 3265.729585,0.898203593,0.888693433
5 | 3412.985274,0.982035928,0.968506744
6 | 3545.515395,1.053892216,1.043194582
7 | 3663.319946,1.125748503,1.111850766
8 | 3759.036145,1.185628743,1.169202456
9 | 3847.389558,1.245508982,1.223388959
10 | 3906.291834,1.281437126,1.260177375
11 | 4024.096386,1.353293413,1.335346188
12 | 4127.175368,1.413173653,1.402858088
13 | 4208.165997,1.461077844,1.457040499
14 | 4289.156627,1.508982036,1.512222713
15 | 4362.784471,1.556886228,1.563255158
16 | 4458.500669,1.604790419,1.630830298
17 | 4546.854083,1.664670659,1.694443235
18 | 4605.756359,1.71257485,1.737510403
19 | 4664.658635,1.760479042,1.781104011
20 | 4708.835341,1.796407186,1.81414449
21 | 4797.188755,1.868263473,1.881112695
22 | 4870.8166,1.928143713,1.937822556
23 | 4929.718876,1.976047904,1.983781064
24 | 5010.709505,2.035928144,2.047830609
25 | 5091.700134,2.107784431,2.112871268
26 | 5216.86747,2.215568862,2.215336273
27 | 5319.946452,2.299401198,2.301493219
28 | 5408.299866,2.371257485,2.376616069
29 | 5518.741633,2.467065868,2.472171883
30 | 5607.095047,2.550898204,2.549937177
31 | 5688.085676,2.622754491,2.622252408
32 | 5754.350736,2.682634731,2.68215198
33 | 5842.70415,2.766467066,2.763042964
34 | 5908.96921,2.826347305,2.82447938
35 | 5975.23427,2.886227545,2.886573821
36 | 6048.862115,2.958083832,2.956338956
37 | 6122.48996,3.02994012,3.026915519
38 | 6232.931727,3.137724551,3.134300717
39 | 6343.373494,3.245508982,3.243508893
40 | 6439.089692,3.341317365,3.339629327
41 | 6549.531459,3.461077844,3.452236136
42 | 6652.610442,3.568862275,3.558976517
43 | 6748.32664,3.676646707,3.659509908
44 | 6836.680054,3.77245509,3.753520482
45 | 6932.396252,3.880239521,3.856675771
46 | 7020.749665,3.976047904,3.95310493
47 | 7101.740295,4.071856287,4.042517181
48 | 7182.730924,4.167664671,4.132903412
49 | 7278.447122,4.275449102,4.24097846
50 | 7381.526104,4.395209581,4.35888642
51 | 7469.879518,4.502994012,4.461203715
52 | 7572.958501,4.622754491,4.582035143
53 | 7639.223561,4.706586826,4.660543001
54 | 7698.125837,4.778443114,4.73087314
55 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/fitDF70.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import pandas as pd
3 | import numpy as np
4 | from numpy import logspace, log, log10
5 | import matplotlib.pyplot as plt
6 | from gpfit.fit import fit
7 | from scipy import interpolate
8 | plt.rcParams.update({'font.size':19})
9 |
10 | np.random.seed(0)
11 |
12 | # Fitting BSFC vs. Power
13 | df = pd.read_csv('Dataset_Power_Kw.csv')
14 | p = df['P']
15 | rpm = df["RPM"]
16 | f = interpolate.interp1d(rpm, p)
17 | df = pd.read_csv('Dataset_BSFC_kgKwh.csv')
18 | df = df[(df["RPM"] > min(rpm)) & (df["RPM"] < max(rpm))]
19 | rpmnew = df["RPM"]
20 | u = f(rpmnew)/max(p)
21 | w = df['BSFC']/min(df["BSFC"])
22 | x = np.array(log(u))
23 | y = np.array(log(w))
24 | Type = 'SMA'
25 | K = 2
26 |
27 | cstrt, rmserror = fit(x, y, K, Type)
28 | print("RMS error = %.4f" % rmserror)
29 | yfit = cstrt.evaluate(x)
30 |
31 | fig, ax = plt.subplots()
32 | ax.plot(u, w*min(df["BSFC"]), "o", mfc="None", ms=7, mew=1.5)
33 | ax.plot(u, np.exp(yfit)*min(df["BSFC"]), linewidth=2)
34 | ax.set_xlabel("Percent Power")
35 | ax.set_ylabel("$BSFC$ [kg/kW/hr]")
36 | ax.legend(["RCV Engine Ltd. Data", "GP approximation"], fontsize=15)
37 | ax.set_xlim([0, 1])
38 | ax.set_ylim([0, 1])
39 | ax.grid()
40 | fig.savefig("powertobsfcfit.pdf", bbox_inches="tight")
41 |
42 | # Fitting BSFC vs. RPM
43 | df = pd.read_csv('Dataset_BSFC_kgKwh.csv')
44 | RPM = df['RPM']
45 | RPMmax = np.amax(RPM)
46 | BSFC = df['BSFC']
47 | BSFCmin = np.amin(BSFC)
48 | x = np.array(log(RPM/RPMmax))
49 | y = np.array(log(BSFC/BSFCmin))
50 | Type = 'SMA'
51 | K = 2
52 |
53 | cstrt, rmserror = fit(x,y,K,Type)
54 | print("RMS error = %.4f" % rmserror)
55 | yfit = cstrt.evaluate(x)
56 |
57 | fig, ax = plt.subplots()
58 | ax.plot(RPM, BSFC, "o", markerfacecolor="None")
59 | ax.plot(RPM, np.exp(yfit)*BSFCmin)
60 | ax.set_xlabel("$RPM$")
61 | ax.set_ylabel("$BSFC$ [lb/hp/hr]")
62 | ax.legend(["Manufacture Data", "GP approximation"])
63 | ax.grid()
64 | fig.savefig("rpmtobsfcfit.pdf", bbox_inches="tight")
65 |
66 | # Fitting Power vs. RPM
67 | df = pd.read_csv('Dataset_Power_Kw.csv')
68 | RPM = df['RPM']
69 | RPM_max = np.amax(RPM)
70 | P = df['P']
71 | P_max = np.amax(P)
72 | y = np.array(log(P/P_max))
73 | x = np.array(log(RPM/RPM_max))
74 | Type = 'SMA'
75 | K = 1
76 | cstrt, rmserror = fit(x,y,K,Type)
77 | print("RMS error = %.4f" % rmserror)
78 | yfit = cstrt.evaluate(x)
79 |
80 | fig, ax = plt.subplots()
81 | ax.plot(RPM, P, "o", markerfacecolor="None")
82 | ax.plot(RPM, np.exp(yfit)*P_max)
83 | ax.set_xlabel("$RPM$")
84 | ax.set_ylabel("Shaft Power [kW]")
85 | ax.legend(["Manufacture Data", "GP approximation"])
86 | ax.grid()
87 | fig.savefig("rpmtopowerfit.pdf", bbox_inches="tight")
88 |
89 | # Fitting Torque vs. RPM
90 | # df = pd.read_csv('Dataset_Torque_Nm.csv')
91 | # RPM = df['RPM']
92 | # RPM_max = np.amax(RPM)
93 | # Q = df['Q']
94 | # Q_max = np.amax(Q)
95 | # logRPM = log(RPM/RPM_max)
96 | # logQ = log(Q/Q_max)
97 | # Type = 'SMA'
98 | # K = 1
99 | # cstrt, rms_error = fit(logRPM,logQ,K,Type)
100 |
101 | # fit lapse rate
102 | df = pd.read_csv("DF35_maxPvh.csv")
103 | u = df["ft"]
104 | w = df["kW"]/max(df["kW"])
105 | x = np.array(u)
106 | y = np.array(w)
107 |
108 | A = np.vstack([x, np.ones(len(x))]).T
109 | m, c = np.linalg.lstsq(A, y)[0]
110 | print("Equation: y = %.4gx + %.4f" % (m, c))
111 | fig, ax = plt.subplots()
112 | ax.plot(x, y, 'o', label='RCV Engine Data', markerfacecolor="None")
113 | ax.plot(x, m*x + c, label='Fitted Line')
114 | ax.set_ylabel("Engine Lapse Rate")
115 | ax.set_xlabel("Altitude [ft]")
116 | ax.legend()
117 | ax.grid()
118 | fig.savefig("lapseline.pdf", bbox_inches="tight")
119 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/DF70/genericbsfcpower.csv:
--------------------------------------------------------------------------------
1 | 0.9997540049876337, 0.1938783468970966
2 | 0.9011534159132191, 0.19713908929506052
3 | 0.7998372348395438, 0.20585359345655307
4 | 0.6985355240607136, 0.20910386798165048
5 | 0.5999252881230693, 0.21600743013721113
6 | 0.49995484446998684, 0.22654857811393547
7 | 0.3999699305220594, 0.2425539557270553
8 | 0.29995607598444185, 0.26948779261296585
9 | 0.19986986997259884, 0.323742777680853
10 | 0.10085446577929211, 0.48364476965548375
11 | 0.07902843772128779, 0.5655244712184808
12 | 0.058413090998655604, 0.7002302932030664
13 | 0.040137108609312355, 0.9715523239704025
14 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/README.md:
--------------------------------------------------------------------------------
1 | # IC Gas Engine Model
2 |
3 | The models in the this folder aim to capture both engine weight and performance for small IC engines. The DF70 from RCV engines is also modeled with the correct weight and specified BSFC, RPM and Power values and ranges. The general IC engine model is `gas_engine.py` and the DF70 specific model is `df70.py`.
4 |
5 | ## Weight
6 |
7 | The weight is modeled by using a power law derived from data given by the [University of North Dakota](http://media.aero.und.edu/uasresearch.org/documents/195-197 Reference-Section Engines.pdf). Using the fitting techniques in [gpfit](https://github.com/hoburg/gpfit) an equation was derived mapping weight to maximum power and is shown below:
8 |
9 | 
10 |
11 | # Performance
12 |
13 | One common way predict engine performance is to assumed a constant $BSFC$ value, or that power is directly proportional to fuel burn. This model accounts for changes in $BSFC$ due to throttle setting. The lower the throttle setting the higher the $BSFC$. Using the DF70 engine, a $BSFC$ to power setting mapping is found using gpfit.
14 |
15 | 
16 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/engine/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/df70.py:
--------------------------------------------------------------------------------
1 | " engine_model.py "
2 | from builtins import zip
3 | from gpkit import Model, Variable, units
4 |
5 | class DF70(Model):
6 | "engine model"
7 | def setup(self):
8 |
9 | W = Variable("W", "lbf", "Installed/Total engine weight")
10 | mfac = Variable("m_{fac}", 1.0, "-", "Engine weight margin factor")
11 | bsfc_min = Variable("BSFC_{min}", 0.3162, "kg/kW/hr", "minimum BSFC")
12 | Wdf70 = Variable("W_{DF70}", 7.76, "lbf",
13 | "Installed/Total DF70 engine weight")
14 | Pslmax = Variable("P_{sl-max}", 5.17, "hp",
15 | "Max shaft power at sea level")
16 | h = Variable("h", 12, "in", "engine height")
17 |
18 | constraints = [W/mfac >= Wdf70,
19 | Pslmax == Pslmax]
20 |
21 | return constraints
22 |
23 | def flight_model(self, state):
24 | return DF70Perf(self, state)
25 |
26 | class DF70Perf(Model):
27 | "engine performance model"
28 | def setup(self, static, state):
29 |
30 | Pshaft = Variable("P_{shaft}", "hp", "Shaft power")
31 | bsfc = Variable("BSFC", "kg/kW/hr", "Brake specific fuel consumption")
32 | Pavn = Variable("P_{avn}", 40, "watts", "Avionics power")
33 | Ptotal = Variable("P_{total}", "hp", "Total power, avionics included")
34 | eta_alternator = Variable("\\eta_{alternator}", 0.8, "-",
35 | "alternator efficiency")
36 | href = Variable("h_{ref}", 1000, "ft", "reference altitude")
37 | h_vals = state.substitutions("h")
38 | if len(href) == 1:
39 | h_vals = [h_vals]
40 | lfac = [-0.035*(v/hr.value) + 1.0
41 | for v, hr in zip(h_vals, href)]
42 | Leng = Variable("L_{eng}", lfac, "-", "shaft power loss factor")
43 | Pshaftmax = Variable("P_{shaft-max}",
44 | "hp", "Max shaft power at altitude")
45 | mfac = Variable("m_{fac}", 1.0, "-", "BSFC margin factor")
46 | rpm = Variable("RPM", "rpm", "Engine operating RPM")
47 | rpm_max = Variable("RPM_{max}", 7698, "rpm", "Maximum RPM")
48 |
49 | constraints = [
50 | (bsfc/mfac/static["BSFC_{min}"])**36.2209 >= (
51 | 2.31541*(rpm/rpm_max)**8.06517
52 | + 0.00103364*(rpm/rpm_max)**-38.8545),
53 | (Ptotal/Pshaftmax)**0.1 == 0.999495*(rpm/rpm_max)**0.294421,
54 | rpm <= rpm_max,
55 | Pshaftmax/static["P_{sl-max}"] == Leng,
56 | Pshaftmax >= Ptotal,
57 | Ptotal >= Pshaft + Pavn/eta_alternator
58 | ]
59 |
60 | return constraints
61 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/gas_engine.py:
--------------------------------------------------------------------------------
1 | " engine_model.py "
2 | from builtins import zip
3 | from gpkit import Model, Variable, units
4 | import os
5 | import pandas as pd
6 | # from gpkitmodels.tools.fit_constraintset import FitCS
7 | from gpfit.fit_constraintset import FitCS
8 |
9 |
10 | class Engine(Model):
11 | "engine model"
12 | def setup(self, DF70=False):
13 |
14 | self.DF70 = DF70
15 |
16 | W = Variable("W", "lbf", "Installed/Total engine weight")
17 | mfac = Variable("m_{fac}", 1.0, "-", "Engine weight margin factor")
18 | bsfc_min = Variable("BSFC_{min}", 0.3162, "kg/kW/hr", "minimum BSFC")
19 | Pref = Variable("P_{ref}", 10.0, "hp", "Reference shaft power")
20 | Wengref = Variable("W_{eng-ref}", 10.0, "lbf",
21 | "Reference engine weight")
22 | Weng = Variable("W_{eng}", "lbf", "engine weight")
23 | Pslmax = Variable("P_{sl-max}", "hp",
24 | "Max shaft power at sea level")
25 |
26 | path = os.path.dirname(__file__)
27 | df = pd.read_csv(path + os.sep + "power_lawfit.csv").to_dict(
28 | orient="records")[0]
29 |
30 | constraints = [
31 | FitCS(df, Weng/Wengref, [Pslmax/Pref]),
32 | W/mfac >= 2.572*Weng**0.922*units("lbf")**0.078]
33 |
34 | return constraints
35 |
36 | def flight_model(self, state):
37 | return EnginePerf(self, state)
38 |
39 |
40 | class EnginePerf(Model):
41 | "engine performance model"
42 | def setup(self, static, state):
43 |
44 | Pshaft = Variable("P_{shaft}", "hp", "Shaft power")
45 | bsfc = Variable("BSFC", "kg/kW/hr", "Brake specific fuel consumption")
46 | Pavn = Variable("P_{avn}", 40, "watts", "Avionics power")
47 | Ptotal = Variable("P_{total}", "hp", "Total power, avionics included")
48 | eta_alternator = Variable("\\eta_{alternator}", 0.8, "-",
49 | "alternator efficiency")
50 | href = Variable("h_{ref}", 1000, "ft", "reference altitude")
51 | h_vals = state.substitutions("h")
52 | if len(href) == 1:
53 | h_vals = [h_vals]
54 | lfac = [-0.035*(v/hr.value) + 1.0
55 | for v, hr in zip(h_vals, href)]
56 | Leng = Variable("L_{eng}", lfac, "-", "shaft power loss factor")
57 | Pshaftmax = Variable("P_{shaft-max}",
58 | "hp", "Max shaft power at altitude")
59 | mfac = Variable("m_{fac}", 1.0, "-", "BSFC margin factor")
60 |
61 | path = os.path.dirname(__file__)
62 | df = pd.read_csv(path + os.sep + "powerBSFCfit.csv").to_dict(
63 | orient="records")[0]
64 |
65 | constraints = [
66 | FitCS(df, bsfc/mfac/static["BSFC_{min}"], [Ptotal/Pshaftmax]),
67 | Pshaftmax/static["P_{sl-max}"] == Leng,
68 | Pshaftmax >= Ptotal,
69 | Ptotal >= Pshaft + Pavn/eta_alternator
70 | ]
71 |
72 | return constraints
73 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/powerBSFCfit.csv:
--------------------------------------------------------------------------------
1 | lb0,ub0,d,e10,K,e00,a1,ftype,max_err,c1,c0,rms_err
2 | 0.11801242227166193,0.96850044350687503,1,1.1292113161811266,2,-7.7018739569394059,18.556942322046762,SMA,0.020769498868956959,1.3862822190984452,0.0086632097847674488,0.0069998230620234763
3 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/power_lawfit.csv:
--------------------------------------------------------------------------------
1 | lb0,ub0,d,K,e00,a1,ftype,max_err,c0,rms_err
2 | 0.29552034789999998,33.742248679999996,1,1,0.7723915754684576,1,MA,1.0718652137941009,1.2784683664089935,0.34279129123926194
3 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/powertobsfcfit.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/engine/powertobsfcfit.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/powertobsfcfit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/engine/powertobsfcfit.png
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/powervsweightfit.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/engine/powervsweightfit.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/engine/powervsweightfit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/engine/powervsweightfit.png
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/README.md:
--------------------------------------------------------------------------------
1 | # Fuselage
2 |
3 | Included in this folder are two separate fuselage optimization models. One is an ellipsoid shaped fuselage in `elliptical_fuselage.py` the other is a cylindrical shaped fusealge with an ellipctical nose cone and an "o-jive" profile for the rear bulkhead.
4 |
5 | ## Elliptical Fuselage
6 |
7 | The elliptical fuselage assumes a constant skin thickness. Drag model is based off of skin friction drag. No loading cases are assumed. A detailed write up can be found in `./ellipsoid_fuselage.pdf`.
8 |
9 | ## Cylindrical Fusealge
10 |
11 | The cylindrical fuselage has 3 sections, nose, body and bulkhead, whose shapes and surface area are described in the diagram below:
12 |
13 | 
14 |
15 | It is assumed that all of the aircraft fuel goes into the cylindrical, middle section. This middle section must have the volume capacity then to store all of the fuel
16 |
17 | $$ V_{body} \geq V_{fuel} $$
18 |
19 | The fuselage weight is comprised of the skin weight. The fuselage skin is a separate model created when the fuselage model is created.
20 |
21 | The aerodynamic model of the fuselage is based off of a fit to CFD runs in Solidworks for various length to radius ratios of the 3 parts: nose, body, and bulkhead. The model is called FuselageAero.
22 |
23 | ### Fuselage Skin
24 |
25 | The fuselage skin assumes that the body section of fuselage takes all of the tosional and bending loads. The thickness is determined by the loads or the minimum gague thickness. A constant thickness is assumed for all 3 sections
26 |
27 | \begin{align*}
28 | S \geq S_{nose} + S_{body} + S_{bulk} \\
29 | W >= S*rho_{Kevlar}*t*g\\
30 | \end{align*}
31 |
32 | The skin is subjected to 2 loads. The first is a pull up load constraining the thickness in either stress or deflection. The second is a landing load.
33 |
34 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/fuselage/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/cyl_fuse_drawing.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/fuselage/cyl_fuse_drawing.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/cylindrical_fuselage.py:
--------------------------------------------------------------------------------
1 | " cylindrical fuselage.py "
2 | import numpy as np
3 | from gpkit import Variable, Model
4 | from .fuel_tank import FuelTank
5 | from .fuselage_skin import FuselageSkin
6 |
7 | class Fuselage(Model):
8 | "The thing that carries the fuel, engine, and payload"
9 | def setup(self, Wfueltot):
10 |
11 | R = Variable("R", "ft", "fuselage radius")
12 | l = Variable("l", "ft", "fuselage length")
13 | S = Variable("S", "ft^2", "fuselage cross sectional area")
14 | W = Variable("W", "lbf", "Fuselage weight")
15 | mfac = Variable("m_{fac}", 2.1, "-", "Fuselage weight margin factor")
16 | lbody = Variable("l_{body}", "ft", "center body length")
17 | kbody = Variable("k_{body}", "-",
18 | "fuselage body length to radius ratio")
19 | knose = Variable("k_{nose}", "-",
20 | "fuselage nose length to radius ratio")
21 | kbulk = Variable("k_{bulk}", "-",
22 | "fuselage bulk length to radius ratio")
23 | Swet = Variable("S_{wet}", "ft**2", "fuselage wetted area")
24 | Sbody = Variable("S_{body}", "ft**2", "wetted surface area of body")
25 | Snose = Variable("S_{nose}", "ft**2", "wetted surface area of nose")
26 | Sbulk = Variable("S_{bulk}", "ft**2", "wetted surface area of bulk")
27 | Volbody = Variable("\\mathcal{V}_{body}", "ft**3", "volume of body")
28 |
29 | self.fueltank = FuelTank(Wfueltot)
30 | self.skin = FuselageSkin(Swet, R, lbody)
31 | self.components = [self.fueltank, self.skin]
32 |
33 | constraints = [
34 | kbody == lbody/R,
35 | Swet >= Sbody + Snose + Sbulk,
36 | Sbody >= 2*np.pi*R*lbody,
37 | Snose**(8./5.) >= (
38 | (2*np.pi*R**2)**(8./5.)*(1./3. + 2./3.*(knose)**(8./5.))),
39 | Sbulk >= R**2*(0.012322*kbulk**2 + 1.524925*kbulk + 0.502498),
40 | Volbody <= np.pi*R**2*lbody,
41 | l <= 3*R*(kbody*knose*kbulk)**(1./3),
42 | S >= np.pi*R**2,
43 | Volbody >= self.fueltank["\\mathcal{V}"],
44 | W/mfac >= self.fueltank["W"] + self.skin["W"],
45 | ]
46 |
47 | return self.components, constraints
48 |
49 | def loading(self, Wcent):
50 | return FuselageLoading(self, Wcent)
51 |
52 | def flight_model(self, state):
53 | return FuselageAero(self, state)
54 |
55 | class FuselageLoading(Model):
56 | "fuselage loading cases"
57 | def setup(self, fuselage, Wcent):
58 |
59 | loading = [fuselage.skin.loading(Wcent)]
60 | loading.append(fuselage.skin.landing(Wcent))
61 |
62 | return loading
63 |
64 | class FuselageAero(Model):
65 | "fuselage drag model"
66 | def setup(self, static, state):
67 |
68 | Cf = Variable("C_f", "-", "fuselage skin friction coefficient")
69 | Re = Variable("Re", "-", "fuselage reynolds number")
70 | Reref = Variable("Re_{ref}", 1e6, "-", "reference Reynolds number")
71 | Cfref = Variable("C_{r_{ref}}", "-",
72 | "reference skin friction coefficient")
73 | Cd = Variable("C_d", "-", "fuselage drag coefficient")
74 |
75 | constraints = [
76 | Re == state["V"]*state["\\rho"]*static["l"]/state["\\mu"],
77 | Cf >= 0.455/Re**0.3,
78 | Cfref == 0.455/Reref**0.3,
79 | Cd**0.996232 >= Cf/Cfref*(
80 | 0.00243049*static["k_{body}"]**0.033607
81 | * static["k_{nose}"]**1.21682 * static["k_{bulk}"]**0.306251
82 | + 0.00255095*static["k_{body}"]**-0.0316887
83 | * static["k_{nose}"]**-0.585489 * static["k_{bulk}"]**1.15394
84 | + 0.0436011 * static["k_{body}"]**0.0545722
85 | * static["k_{nose}"]**0.258228 * static["k_{bulk}"]**-1.42664
86 | + 0.00970479 * static["k_{body}"]**0.8661
87 | * static["k_{nose}"]**-0.209136 * static["k_{bulk}"]**-0.156166)
88 | ]
89 |
90 | return constraints
91 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/ellipsoid_fuselage.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/fuselage/ellipsoid_fuselage.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/elliptical_fuselage.py:
--------------------------------------------------------------------------------
1 | " elliptical fuselage.py "
2 | import numpy as np
3 | from gpkit import Variable, Model, parse_variables
4 | from gpkitmodels.GP.materials import cfrpfabric
5 | from gpkitmodels import g
6 |
7 | class FuselageAero(Model):
8 | """ Fuselage Aerodyanmic Model
9 |
10 | Variables
11 | ---------
12 | Cf [-] fuselage skin friction coefficient
13 | Re [-] fuselage reynolds number
14 | Cd [-] fuselage drag coefficient
15 | mfac 1.0 [-] fuselage drag margin
16 |
17 | """
18 | @parse_variables(__doc__, globals())
19 | def setup(self, static, state):
20 | V = state.V
21 | rho = state.rho
22 | l = static.l
23 | mu = state.mu
24 | k = static.k
25 |
26 | constraints = [
27 | Re == V*rho*l/mu,
28 | Cf >= 0.455/Re**0.3,
29 | Cd/mfac >= Cf*k
30 | ]
31 |
32 | return constraints
33 |
34 | class Fuselage(Model):
35 | """ Fuselage Model
36 |
37 | Variables
38 | ---------
39 | R [ft] fuselage radius
40 | l [ft] fuselage length
41 | S [ft^2] wetted fuselage area
42 | W [lbf] fuselage weight
43 | mfac 2.0 [-] fuselage weight margin factor
44 | f [-] fineness ratio of lenth to diameter
45 | k [-] fuselage form factor
46 | Vol [ft^3] fuselae volume
47 | rhofuel 6.01 [lbf/gallon] density of 100LL
48 | rhocfrp 1.6 [g/cm^3] density of CFRP
49 | t [in] fuselage skin thickness
50 | nply 2 [-] number of plys
51 |
52 | """
53 | material = cfrpfabric
54 | flight_model = FuselageAero
55 |
56 | @parse_variables(__doc__, globals())
57 | def setup(self):
58 | rhocfrp = self.material.rho
59 | tmin = self.material.tmin
60 |
61 | constraints = [
62 | f == l/R/2,
63 | k >= 1 + 60/f**3 + f/400,
64 | 3*(S/np.pi)**1.6075 >= 2*(l*R*2)**1.6075 + (2*R)**(2*1.6075),
65 | Vol <= 4*np.pi/3*(l/2)*R**2,
66 | W/mfac >= S*rhocfrp*t*g,
67 | t >= nply*tmin,
68 | ]
69 |
70 | return constraints
71 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/fuel_tank.py:
--------------------------------------------------------------------------------
1 | " fuel tank "
2 | from gpkit import Model, Variable
3 |
4 | class FuelTank(Model):
5 | """
6 | Returns the weight of the fuel tank. Assumes a cylinder shape with some
7 | fineness ratio
8 | """
9 | def setup(self, Wfueltot):
10 |
11 | W = Variable("W", "lbf", "fuel tank weight")
12 | f = Variable("f", 0.03, "-", "fraction fuel tank weight to fuel weight")
13 | mfac = Variable("m_{fac}", 1.1, "-", "fuel volume margin factor")
14 | rhofuel = Variable("\\rho_{fuel}", 6.01, "lbf/gallon",
15 | "density of 100LL")
16 | Vol = Variable("\\mathcal{V}", "ft^3", "fuel tank volume")
17 |
18 | constraints = [W >= f*Wfueltot,
19 | Vol/mfac >= Wfueltot/rhofuel,
20 | ]
21 |
22 | return constraints
23 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/fuselage.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/fuselage/fuselage.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/fuselage_profile_drag/fusedrag.csv:
--------------------------------------------------------------------------------
1 | tubelr,noselr,taillr,drag,cd_front
1,1,1,1035.305,0.054924636
1,1,2,628.942,0.033366409
1,1,3,484.06,0.025680181
1,1,4,523.018,0.027746967
1,1,5,546.125,0.028972831
1,1,6,615.724,0.032665173
1,1,8,912.273,0.048397586
1,1,10,993.842,0.052724956
1,1,15,1253.929,0.066523
1,2,1,1354.212,0.071843178
1,2,2,698.697,0.037067027
1,2,3,570.088,0.030244108
1,2,4,562.26,0.029828819
1,2,5,576.009,0.030558227
1,2,6,616.17,0.032688834
1,2,8,753.805,0.039990597
1,2,10,803.671,0.042636071
1,2,15,1016.955,0.053951138
1,3,1,1349.822,0.071610281
1,3,2,882.647,0.046825878
1,3,3,681.326,0.036145467
1,3,4,657.46,0.034879336
1,3,5,661.495,0.0350934
1,3,6,722.119,0.038309603
1,3,8,835.784,0.044339718
1,3,10,889.588,0.047194109
1,3,15,1101.411,0.058431668
1,5,1,1751.288,0.092908714
1,5,2,1043.149,0.055340773
1,5,3,873.062,0.046317378
1,5,4,862.476,0.045755773
1,5,5,867.8,0.04603822
1,5,6,918.192,0.048711599
1,5,8,965.876,0.051241313
1,5,10,1039.941,0.055170584
1,5,15,1245.72,0.066087499
3,1,1,1412.136,0.074916142
3,1,2,937.053,0.049712206
3,1,3,761.712,0.040410077
3,1,4,717.539,0.038066626
3,1,5,774.43,0.041084788
3,1,6,1009.313,0.053545718
3,1,8,1073.001,0.056924471
3,1,10,1138.328,0.060390176
3,1,15,1427.87,0.075750856
3,2,1,1677.009,0.088968091
3,2,2,971.614,0.051545724
3,2,3,745.06,0.039526661
3,2,4,724.946,0.03845958
3,2,5,838.682,0.044493462
3,2,6,840.405,0.04458487
3,2,8,881.551,0.046767733
3,2,10,964.773,0.051182797
3,2,15,1184.484,0.062838828
3,3,1,1689.709,0.089641847
3,3,2,1061.085,0.056292308
3,3,3,876.277,0.046487939
3,3,4,874.08,0.046371384
3,3,5,860.193,0.045634656
3,3,6,893.371,0.047394804
3,3,8,963.363,0.051107994
3,3,10,1038.846,0.055112492
3,3,15,1241.806,0.065879854
3,5,1,1861.434,0.098752141
3,5,2,1344.49,0.07132741
3,5,3,1080.079,0.057299971
3,5,4,1028.981,0.054589137
3,5,5,990.455,0.05254527
3,5,6,1028.758,0.054577307
3,5,8,1112.913,0.059041868
3,5,10,1169.812,0.062060454
3,5,15,1386.483,0.073555208
5,1,1,1788.728,0.094894968
5,1,2,1163.367,0.061718536
5,1,3,996.039,0.05284151
5,1,4,1090.061,0.057829532
5,1,5,1125.31,0.05969955
5,1,6,1169.837,0.06206178
5,1,8,1207.535,0.064061721
5,1,10,1326.058,0.070349562
5,1,15,1557.048,0.082603962
5,2,1,1788.143,0.094863932
5,2,2,1161.068,0.06159657
5,2,3,1034.13,0.0548623
5,2,4,946.827,0.050230732
5,2,5,949.72,0.050384211
5,2,6,970.72,0.051498295
5,2,8,1036.778,0.055002781
5,2,10,1089.987,0.057825606
5,2,15,1307.506,0.069365348
5,3,1,1888.519,0.100189045
5,3,2,1328.993,0.070505268
5,3,3,1075.567,0.057060602
5,3,4,1028.461,0.054561551
5,3,5,1005.752,0.053356801
5,3,6,1023.171,0.054280907
5,3,8,1112.382,0.059013698
5,3,10,1165.087,0.061809785
5,3,15,1388.393,0.073656536
5,5,1,2164.614,0.114836339
5,5,2,1531.067,0.081225627
5,5,3,1222.183,0.064838822
5,5,4,1137.749,0.060359459
5,5,5,1194.024,0.063344941
5,5,6,1195.987,0.063449081
5,5,8,1241.198,0.065847599
5,5,10,1329.098,0.070510839
5,5,15,1522.546,0.080773574
10,1,1,2574.053,0.136557753
10,1,2,1856.29,0.098479243
10,1,3,1579.383,0.08378887
10,1,4,1488.625,0.078974009
10,1,5,1548.464,0.082148567
10,1,6,1552.098,0.082341356
10,1,8,1637.829,0.086889527
10,1,10,1694.605,0.089901587
10,1,15,1927.926,0.102279651
10,2,1,2553.915,0.135489399
10,2,2,1732.495,0.091911714
10,2,3,1403.615,0.074464088
10,2,4,1343.743,0.07128778
10,2,5,1264.998,0.067110228
10,2,6,1317.244,0.069881965
10,2,8,1400.165,0.07428106
10,2,10,1450.112,0.076930831
10,2,15,1662.582,0.088202715
10,3,1,2539.403,0.134719513
10,3,2,1828.237,0.096990985
10,3,3,1478.233,0.078422696
10,3,4,1345.505,0.071381257
10,3,5,1342.065,0.07119876
10,3,6,1391.456,0.073819033
10,3,8,1454.841,0.077181712
10,3,10,1528.788,0.081104722
10,3,15,1754.559,0.093082246
10,5,1,2581.016,0.136927152
10,5,2,1974.068,0.10472756
10,5,3,1622.004,0.086049985
10,5,4,1524.516,0.080878086
10,5,5,1547.127,0.082077637
10,5,6,1535.669,0.081469771
10,5,8,1598.099,0.084781785
10,5,10,1660.352,0.088084409
10,5,15,1871.798,0.099301968
20,1,1,3373.827,0.178987081
20,1,2,2810.082,0.14907948
20,1,3,2403.816,0.1275264
20,1,4,2340.528,0.124168867
20,1,5,2306.57,0.122367339
20,1,6,2281.424,0.121033302
20,1,8,2355.985,0.124988886
20,1,10,2411.924,0.127956542
20,1,15,2632.213,0.139643237
20,2,1,3122.014,0.165627987
20,2,2,2585.664,0.137173736
20,2,3,2166.146,0.114917614
20,2,4,1975.77,0.104817854
20,2,5,2068.303,0.109726882
20,2,6,2043.235,0.108396983
20,2,8,2082.744,0.110493001
20,2,10,2151.441,0.11413749
20,2,15,2353.945,0.124880661
20,3,1,3167.898,0.168062209
20,3,2,2639.043,0.140005579
20,3,3,2253.647,0.119559687
20,3,4,2058.463,0.109204854
20,3,5,2154.367,0.114292719
20,3,6,2086.292,0.110681228
20,3,8,2140.522,0.113558219
20,3,10,2200.414,0.116735588
20,3,15,2405.294,0.12760481
20,5,1,3422.578,0.181573402
20,5,2,2793.204,0.148184075
20,5,3,2394.765,0.127046229
20,5,4,2233.341,0.11848242
20,5,5,2157.161,0.114440945
20,5,6,2245.555,0.119130393
20,5,8,2287.977,0.12138095
20,5,10,2343.273,0.124314494
20,5,15,2545.617,0.135049176
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/fuselage_profile_drag/fusedragfit.py:
--------------------------------------------------------------------------------
1 | " fuselage drag fits "
2 | from builtins import zip
3 | import pandas as pd
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 |
7 | def fit_setup(filename):
8 | "set up fitting variables"
9 |
10 | df = pd.read_csv(filename)
11 |
12 | u1 = np.array(df["tubelr"])
13 | u2 = np.array(df["noselr"])
14 | u3 = np.array(df["taillr"])
15 |
16 | w = np.array(df["cd_front"])
17 |
18 | u1 = u1.astype(np.float)
19 | u2 = u2.astype(np.float)
20 | u3 = u3.astype(np.float)
21 | w = w.astype(np.float)
22 |
23 | u = [u1, u2, u3]
24 | x = np.log(u)
25 | y = np.log(w)
26 |
27 | return x, y
28 |
29 | def return_fit(u_1, u_2, u_3):
30 | "fit using SMA, K = 4, RMS = 0.0479"
31 | w = (
32 | 0.00243049 * (u_1)**0.033607 * (u_2)**1.21682 * (u_3)**0.306251
33 | + 0.00255095 * (u_1)**-0.0316887 * (u_2)**-0.585489 * (u_3)**1.15394
34 | + 0.0436011 * (u_1)**0.0545722 * (u_2)**0.258228 * (u_3)**-1.42664
35 | + 0.00970479 * (u_1)**0.8661 * (u_2)**-0.209136 * (u_3)**-0.156166) \
36 | ** (1/0.996232)
37 | return w
38 |
39 | def plot_fits(filename):
40 | "plot fit against data"
41 |
42 | df = pd.read_csv(filename)
43 | u1 = np.array(df["tubelr"])
44 | u2 = np.array(df["noselr"])
45 | u3 = np.array(df["taillr"])
46 | body = np.unique(u1)
47 | nose = np.unique(u2)
48 | tail = np.unique(u3)
49 |
50 | figs = []
51 |
52 | colors = ["k", "m", "b", "g", "y"]
53 | assert len(colors) == len(body)
54 |
55 | for n in nose:
56 | fig, ax = plt.subplots()
57 | datan = df[(df["noselr"] == n)]
58 | for b, clr in zip(body, colors):
59 | datab = datan[(datan["tubelr"] == b)]
60 | ax.plot(datab["taillr"], datab["cd_front"], "o", c=clr)
61 | trl = np.array(datab["taillr"])
62 | taillr = np.linspace(trl[0], trl[-1], 20)
63 | cd = return_fit(b, n, taillr)
64 | ax.plot(taillr, cd, c=clr, label="body finess ratio = %d" % b)
65 | ax.legend()
66 | ax.grid()
67 | ax.set_title("nose finess ratio = %d" % n)
68 | ax.set_xlabel("tail finess ratio")
69 | ax.set_ylabel("fuselage $C_{dp}$")
70 | fig.savefig("fuse_drag_nose%d.pdf" % n)
71 | figs.append(fig)
72 |
73 | return figs
74 |
75 | if __name__ == "__main__":
76 | X, Y = fit_setup("fusedrag.csv")
77 | df = pd.read_csv("fusedrag.csv")
78 | F = plot_fits("fusedrag.csv")
79 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/fuselage_skin.py:
--------------------------------------------------------------------------------
1 | " fuselage skin "
2 | import numpy as np
3 | from gpkit import Model, Variable
4 |
5 | class FuselageSkin(Model):
6 | "fuselage skin model"
7 | def setup(self, S, R, l):
8 |
9 | W = Variable("W", "lbf", "fuselage skin weight")
10 | m = Variable("m", "kg", "fuselage skin mass")
11 | g = Variable("g", 9.81, "m/s^2", "Gravitational acceleration")
12 | rhokevlar = Variable("\\rho_{kevlar}", 1.3629, "g/cm**3",
13 | "kevlar density")
14 | t = Variable("t", "in", "skin thickness")
15 | tmin = Variable("t_{min}", 0.03, "in", "minimum skin thickness")
16 | I = Variable("I", "m**4", "wing skin moment of inertia")
17 | Ig = Variable("I_G", "kg*m**2", "mass moment of inertia")
18 | E = Variable("E", 30, "GPa", "Young's Modulus of Kevlar")
19 |
20 | constraints = [m >= S*rhokevlar*t,
21 | W >= m*g,
22 | t >= tmin,
23 | I <= np.pi*R**3*t,
24 | Ig >= m*(4*R**2 + 4*R*t + t**2),
25 | l == l,
26 | E == E]
27 |
28 | return constraints
29 |
30 | def loading(self, Wcent):
31 | return FuselageSkinL(self, Wcent)
32 |
33 | def landing(self, Wcent):
34 | return FuselageLanding(self, Wcent)
35 |
36 | class FuselageSkinL(Model):
37 | "fuselage skin loading"
38 | def setup(self, static, Wcent):
39 |
40 | Mh = Variable("M_h", "N*m", "horizontal axis center fuselage moment")
41 | Nmax = Variable("N_{max}", 5, "-", "max loading")
42 | sigmakevlar = Variable("\\sigma_{Kevlar}", 190, "MPa",
43 | "stress strength of Kevlar")
44 | q = Variable("q", "N/m", "distributed load")
45 | kappa = Variable("\\kappa", 0.05, "-", "maximum tip deflection ratio")
46 |
47 | constraints = [
48 | Mh >= Nmax*Wcent/4*static["l_{body}"],
49 | sigmakevlar >= Mh*static["R"]/static["I"],
50 | q >= Wcent*Nmax/static["l_{body}"],
51 | kappa*static["l_{body}"]/2 >= (
52 | q*(static["l_{body}"]/2)**4/(8*static["E"]*static["I"]))
53 | ]
54 |
55 | return constraints
56 |
57 | class FuselageLanding(Model):
58 | "fuselage loading case"
59 | def setup(self, static, Wcent):
60 |
61 | F = Variable("F", "lbf", "maximum landing force")
62 | Nmax = Variable("N_{max}", 5, "-", "maximum landing load factor")
63 | a = Variable("a", "m/s**2", "landing vertical acceleration")
64 | omegadot = Variable("\\dot{\\omega}", "1/s**2",
65 | "angular acceleration about rear fuselage")
66 | Mg = Variable("M_G", "N*m", "landing moment about center of mass")
67 | sigmakevlar = Variable("\\sigma_{Kevlar}", 190, "MPa",
68 | "stress strength of Kevlar")
69 |
70 | constraints = [F >= Wcent*Nmax,
71 | a >= F/static["m"],
72 | omegadot >= a/(static["l_{body}"]/2),
73 | Mg >= static["I_G"]*omegadot,
74 | sigmakevlar >= Mg*static["R"]/static["I"]
75 | ]
76 |
77 | return constraints
78 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/fuselage/test_fuselage.py:
--------------------------------------------------------------------------------
1 | from gpkit import Model
2 | from gpkitmodels.GP.aircraft.fuselage.elliptical_fuselage import Fuselage
3 | from gpkitmodels.GP.aircraft.wing.wing_test import FlightState
4 |
5 | def test_ellp():
6 | "elliptical fuselage test"
7 | f = Fuselage()
8 | fs = FlightState()
9 | faero = f.flight_model(f, fs)
10 | f.substitutions[f.Vol] = 1.33
11 |
12 | m = Model(f.W*faero.Cd, [f, fs, faero])
13 | m.solve()
14 |
15 | def test():
16 | "tests"
17 | test_ellp()
18 |
19 | if __name__ == "__main__":
20 | test()
21 |
22 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/mission/README.md:
--------------------------------------------------------------------------------
1 | # Mission
2 |
3 | This folder contains various ''mission`` models that can be used for a given aircraft.
4 |
5 | ## BreguetEndurance
6 |
7 | This model predicts how much fuel is needed to fly for a certain time period. It needs as an input a performance model that has the following variables:
8 |
9 | \begin{table}[]
10 | \centering
11 | \begin{tabular}{ll}
12 | Variable & Description \\
13 | \hline
14 | $P_{total}$ & Total power used by the aircraft \\
15 | $BSFC$ & Break specific fuel consumption \\
16 | $W_{end}$ & End of flight segment weight \\
17 | $W_{start}$ & Start of flight segment weight
18 | \end{tabular}
19 | \end{table}
20 |
21 | The following is a derivation of the form of the Breguet Range equation that is used and a description of the Taylor expanison used to make this GP-compatible.
22 |
23 | \begin{equation}
24 | \label{e:breguetendurance}
25 | t = \frac{W_{\text{ave}}}{P_{\text{shaft}}\text{BSFC}g} \ln{\left( \frac{W_{\text{initial}}}{W_{\text{final}}}\right)}.
26 | \end{equation}
27 |
28 | The derivation begins with the differential form of Breguet Range,\cite{br2}
29 |
30 | \begin{equation}
31 | \label{e:breguetdiff}
32 | -\frac{dW}{dt} = g\dot{m}_{\text{fuel}}.
33 | \end{equation}
34 |
35 | Using the definition of $\text{BSFC}$
36 |
37 | \begin{equation}
38 | \label{e:brBSFC}
39 | \text{BSFC} = \frac{\dot{m}_{\text{fuel}}}{P_{\text{shaft}}},
40 | \end{equation}
41 |
42 | Equation~\eqref{e:breguetdiff} can be written as
43 |
44 | \begin{equation}
45 | \label{e:brdiff2}
46 | -dW = g P_{\text{shaft}} \text{BSFC} dt.
47 | \end{equation}
48 |
49 | This version comes from assuming that $\text{BSFC}$ and the power to weight ratio, $(P_{\text{shaft}}/W)$, are constant during the considered flight segment.
50 | One way to obtain a constant power to weight ratio is a constant velocity and constant lift coefficient.\cite{br2}
51 | $W_{\text{ave}}$ is assumed to be the geometric mean, defined as
52 |
53 | Dividing by $W$,
54 |
55 | \begin{equation}
56 | \label{e:brdiff2}
57 | -\frac{dW}{W} = \frac{g P_{\text{shaft}}\text{BSFC} }{W} dt,
58 | \end{equation}
59 |
60 | and integrating, the Breguet Range equation can be expressed as
61 |
62 | \begin{equation}
63 | \label{e:be1}
64 | \ln{\left( \frac{W_{\text{initial}}}{W_{\text{final}}} \right)} = \frac{gP_{\text{shaft}}\text{BSFC}}{W_{\text{ave}}} t,
65 | \end{equation}
66 |
67 | where $W_{\text{ave}}$ is the average weight of the aircraft during the flight segment.
68 |
69 | \begin{equation}
70 | \label{e:gpmean}
71 | W_{\text{ave}} = \sqrt{W_{\text{initial}}W_{\text{final}}}.
72 | \end{equation}
73 |
74 | To make Equation~\eqref{e:breguetendurance} GP compatible, a Taylor expansion is used,\cite{hoburgthesis}
75 |
76 | \begin{align}
77 | \label{e:brzbre}
78 | z_{\text{bre}} &\geq \frac{P_{\text{shaft}}t \text{BSFC} g}{W}\\
79 | \label{e:brtaylor}
80 | \frac{W_{\text{fuel}}}{W_\text{final}} &\geq z_{\text{bre}} + \frac{z_{\text{bre}}^2}{2} + \frac{z_{\text{bre}}^3}{6} + \frac{z_{\text{bre}}^4}{24} + \dots
81 | \end{align}
82 |
83 | Equations~\eqref{e:brzbre} and~\eqref{e:brtaylor} are monomial and posynomial respectively and therefore GP compatible. For long-endurance aircraft, missions can last days, causing the power to weight ratio $(P_{\text{shaft}}/W)$ to vary significantly during the course of the flight.
84 | Equations~\eqref{e:brzbre},~\eqref{e:brtaylor}, and~\eqref{e:slfweight} can be discretized to account for this.
85 |
86 | \begin{align}
87 | \label{e:slfweightd}
88 | \sqrt{W_i W_{i+1}} &= \frac{1}{2} \rho_i V_i^2 C_{L_i} S \\
89 | \label{e:brzbred}
90 | z_{bre_i} &\geq \frac{P_{\text{shaft}_i}t_i \text{BSFC} g}{\sqrt{W_i W_{i+1}}}\\
91 | \label{e:brtaylord}
92 | \frac{W_{\text{fuel}_i}}{W_{i+1}} &\geq z_{bre_i} + \frac{z_{bre_i}^2}{2} + \frac{z_{bre_i}^3}{6} + \frac{z_{bre_i}^3}{24}
93 | \end{align}
94 |
95 | For evaluation of long-endurance, gas-powered aircraft a discretization of $N=5$ was used.
96 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/mission/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/mission/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/mission/breguet_endurance.py:
--------------------------------------------------------------------------------
1 | " breguet_endurance.py "
2 | from gpkit import Model, Variable
3 | from gpkit.tools import te_exp_minus1
4 | from gpkit.constraints.tight import Tight as TCS
5 |
6 | class BreguetEndurance(Model):
7 | "breguet endurance model"
8 | def setup(self, perf):
9 | z_bre = Variable("z_{bre}", "-", "Breguet coefficient")
10 | t = Variable("t", "days", "Time per flight segment")
11 | f_fueloil = Variable("f_{(fuel/oil)}", 0.98, "-", "Fuel-oil fraction")
12 | Wfuel = Variable("W_{fuel}", "lbf", "Segment-fuel weight")
13 | g = Variable("g", 9.81, "m/s^2", "gravitational acceleration")
14 |
15 | constraints = [
16 | TCS([z_bre >= (perf["P_{total}"]*t*perf["BSFC"]*g
17 | / (perf["W_{end}"]*perf["W_{start}"])**0.5)]),
18 | f_fueloil*Wfuel/perf["W_{end}"] >= te_exp_minus1(z_bre, 3),
19 | perf["W_{start}"] >= perf["W_{end}"] + Wfuel
20 | ]
21 |
22 | return constraints
23 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/mission/mission.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/mission/mission.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/motor/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/motor/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/motor/motor.py:
--------------------------------------------------------------------------------
1 | "Electric motor model "
2 | from gpkit import Model, parse_variables
3 | from gpkit.constraints.tight import Tight as TCS
4 | from gpkitmodels.GP.aircraft.prop.propeller import Propeller, ActuatorProp
5 | from gpkitmodels import g
6 |
7 | class MotorPerf(Model):
8 | """ Electric Motor Performance Model
9 |
10 | Note: The last two constraints may not be tight if there is a motor energy
11 | constraint that does not size the motor. TCS removed to prevent
12 | unnecessary warnings - for most normal useage they will be tight.
13 |
14 | Variables
15 | ---------
16 | Pshaft [kW] motor output shaft power
17 | Pelec [kW] motor input shaft power
18 | etam [-] motor efficiency
19 | Q [N*m] torque
20 | omega [rpm] propeller rotation rate
21 | i [amp] current
22 | v [V] woltage
23 | """
24 | @parse_variables(__doc__, globals())
25 | def setup(self, static, state):
26 | Kv = static.Kv
27 | R = static.R
28 | i0 = static.i0
29 | V_max = static.V_max
30 |
31 | return [Pshaft == Q*omega,
32 | Pelec == v*i,
33 | etam == Pshaft/Pelec,
34 | static.Qmax >= Q,
35 | v <= V_max,
36 | i >= Q*Kv+i0,
37 | v >= omega/Kv + i*R]
38 |
39 | class Motor(Model):
40 | """ Electric Motor Model
41 |
42 | Variables
43 | ---------
44 | Qstar .8 [kg/(N*m)] motor specific torque
45 | W [lbf] motor weight
46 | Qmax [N*m] motor max. torque
47 | V_max 300 [V] motor max voltage
48 | Kv_min 1 [rpm/V] min motor voltage constant
49 | Kv_max 1000 [rpm/V] max motor voltage constant
50 | Kv [rpm/V] motor voltage constant
51 | i0 4.5 [amp] zero-load current
52 | R .033 [ohms] internal resistance
53 | """
54 |
55 | flight_model = MotorPerf
56 |
57 | @parse_variables(__doc__, globals())
58 | def setup(self):
59 | constraints = [W >= Qstar*Qmax*g,
60 | Kv >= Kv_min,
61 | Kv <= Kv_max]
62 |
63 | return constraints
64 |
65 | class PropulsorPerf(Model):
66 | "Propulsor Performance Model"
67 |
68 | @parse_variables(__doc__, globals())
69 | def setup(self, static, state):
70 | self.prop = static.prop.flight_model(static.prop, state)
71 | self.motor = static.motor.flight_model(static.motor, state)
72 |
73 | self.components = [self.prop, self.motor]
74 |
75 | constraints = [self.prop.Q == self.motor.Q,
76 | self.prop.omega == self.motor.omega
77 | ]
78 |
79 | return constraints, self.components
80 |
81 | class Propulsor(Model):
82 | """Propulsor model
83 |
84 | Variables
85 | ---------
86 | W [lbf] propulsor weight
87 |
88 | """
89 | flight_model = PropulsorPerf
90 | prop_flight_model = ActuatorProp
91 |
92 | @parse_variables(__doc__, globals())
93 | def setup(self):
94 | Propeller.flight_model = self.prop_flight_model
95 | self.prop = Propeller()
96 | self.motor = Motor()
97 |
98 | components = [self.prop, self.motor]
99 |
100 | return [self.W >= self.prop.W + self.motor.W], components
101 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/motor/motor_test.py:
--------------------------------------------------------------------------------
1 | from gpkit import Model, parse_variables, SignomialsEnabled, SignomialEquality, units
2 | from gpkitmodels.GP.aircraft.motor.motor import Propulsor, Motor, MotorPerf
3 | from gpkitmodels.GP.aircraft.prop.propeller import Propeller, ActuatorProp
4 | from gpkitmodels.GP.aircraft.wing.wing_test import FlightState
5 | from gpkitmodels.SP.aircraft.prop.propeller import BladeElementProp
6 |
7 | class Propulsor_Test(Model):
8 | """Propulsor Test Model
9 | """
10 |
11 | def setup(self):
12 | fs = FlightState()
13 | p = Propulsor()
14 | pp = p.flight_model(p,fs)
15 | pp.substitutions[pp.prop.T] = 100
16 | self.cost = 1./pp.motor.etam + p.W/(1000*units('lbf')) + 1./pp.prop.eta
17 |
18 | return fs,p,pp
19 |
20 | class Actuator_Propulsor_Test(Model):
21 | """Propulsor Test Model w/ Actuator Disk Propeller
22 | """
23 |
24 | def setup(self):
25 | fs = FlightState()
26 | Propulsor.prop_flight_model = ActuatorProp
27 | p = Propulsor()
28 | pp = p.flight_model(p,fs)
29 | pp.substitutions[pp.prop.T] = 100
30 | self.cost = pp.motor.Pelec/(1000*units('W')) + p.W/(1000*units('lbf'))
31 |
32 | return fs,p,pp
33 | class BladeElement_Propulsor_Test(Model):
34 | """Propulsor Test Model w/ Blade Element Propeller
35 | """
36 |
37 | def setup(self):
38 | fs = FlightState()
39 | Propulsor.prop_flight_model = BladeElementProp
40 | p = Propulsor()
41 | pp = p.flight_model(p,fs)
42 | pp.substitutions[pp.prop.T] = 100
43 | self.cost = pp.motor.Pelec/(1000*units('W')) + p.W/(1000*units('lbf'))
44 |
45 | return fs,p,pp
46 |
47 | def actuator_propulsor_test():
48 | test = Actuator_Propulsor_Test()
49 | test.solve()
50 |
51 | def ME_propulsor_test():
52 | test = BladeElement_Propulsor_Test()
53 | sol = test.localsolve(use_leqs=False) # cvxopt gets singular with leqs
54 |
55 | def propulsor_test():
56 | test = Propulsor_Test()
57 | sol = test.solve()
58 |
59 | class Motor_P_Test(Model):
60 | def setup(self):
61 | fs = FlightState()
62 | m = Motor()
63 | mp = MotorPerf(m,fs)
64 | self.mp = mp
65 | mp.substitutions[m.Qmax] = 100
66 | mp.substitutions[mp.Q] = 10
67 | self.cost = 1./mp.etam + m.W/(100.*units('lbf'))
68 | return self.mp, fs, m
69 |
70 | class speed_280_motor(Model):
71 | def setup(self):
72 | fs = FlightState()
73 | m = Motor()
74 | mp = MotorPerf(m,fs)
75 | self.mp = mp
76 | mp.substitutions[m.Qmax] = 100
77 | mp.substitutions[mp.R] = .7
78 | mp.substitutions[mp.i0] = .16
79 | mp.substitutions[mp.Kv] = 3800
80 | mp.substitutions[mp.v] = 6
81 | self.cost = 1./mp.etam
82 | return self.mp, fs
83 | class hacker_q150_45_motor(Model):
84 | def setup(self):
85 | fs = FlightState()
86 | m = Motor()
87 | mp = MotorPerf(m,fs)
88 | self.mp = mp
89 | mp.substitutions[m.Qmax] = 10000
90 | mp.substitutions[mp.R] = .033
91 | mp.substitutions[mp.i0] = 4.5
92 | mp.substitutions[mp.Kv] = 29
93 | self.cost = 1./mp.etam
94 | return self.mp, fs
95 |
96 | def motor_test():
97 | test = Motor_P_Test()
98 | test.solve()
99 |
100 | def test():
101 | motor_test()
102 | actuator_propulsor_test()
103 | propulsor_test()
104 | ME_propulsor_test()
105 |
106 | if __name__ == "__main__":
107 | test()
108 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/prop/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/prop/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/prop/arccos_fit.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import unittest
3 | from numpy import log, exp, log10, vstack
4 | from numpy import arccos,arange
5 | from gpfit.fit import fit
6 | from numpy.random import random_sample
7 | i = arange(0.0001,3,.001)
8 | j = arccos(exp(-i))
9 | x = log(i)
10 | y = log(j)
11 | K = 1
12 |
13 | cstrt, rmsErr = fit(x,y,K,"SMA")
14 | print(rmsErr)
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/prop/prop_test.py:
--------------------------------------------------------------------------------
1 | " propeller tests "
2 | from gpkitmodels.GP.aircraft.prop.propeller import Propeller, ActuatorProp
3 | from gpkitmodels.SP.aircraft.prop.propeller import BladeElementProp
4 |
5 | from gpkitmodels.GP.aircraft.wing.wing_test import FlightState
6 | from gpkit import units, Model
7 |
8 | def simpleprop_test():
9 | " test simple propeller model "
10 | fs = FlightState()
11 | Propeller.flight_model = ActuatorProp
12 | p = Propeller()
13 | pp = p.flight_model(p, fs)
14 | m = Model(1/pp.eta + p.W/(100.*units("lbf"))+ pp.Q/(100.*units("N*m")),
15 | [fs, p, pp])
16 | m.substitutions.update({"rho": 1.225, "V": 50, "T": 100, "omega":1000})
17 | m.solve()
18 |
19 | def ME_eta_test():
20 |
21 | fs = FlightState()
22 | Propeller.flight_model = BladeElementProp
23 | p = Propeller()
24 | pp = p.flight_model(p,fs)
25 | pp.substitutions[pp.T] = 100
26 | pp.cost = 1./pp.eta + pp.Q/(1000.*units("N*m")) + p.T_m/(1000*units('N'))
27 | sol = pp.localsolve(iteration_limit = 400)
28 |
29 |
30 | def test():
31 | "tests"
32 | simpleprop_test()
33 | ME_eta_test()
34 | if __name__ == "__main__":
35 | test()
36 |
37 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/prop/propeller.py:
--------------------------------------------------------------------------------
1 | " propeller model "
2 | from numpy import pi
3 | from gpkit import Model, Variable,Vectorize,parse_variables, SignomialsEnabled, SignomialEquality
4 | from gpkit.constraints.tight import Tight as TCS
5 | from gpfit.fit_constraintset import XfoilFit
6 | import os
7 | import pandas as pd
8 |
9 | class ActuatorProp(Model):
10 | """ Propeller Model
11 |
12 | Variables
13 | ---------
14 | T [lbf] thrust
15 | Tc [-] coefficient of thrust
16 | etaadd .7 [-] swirl and nonuniformity losses
17 | etav .85 [-] viscous losses
18 | etai [-] inviscid losses
19 | eta [-] overall efficiency
20 | z1 self.helper [-] efficiency helper 1
21 | z2 [-] efficiency helper 2
22 | lam [-] advance ratio
23 | CT [-] thrust coefficient
24 | CP [-] power coefficient
25 | Q [N*m] torque
26 | omega [rpm] propeller rotation rate
27 | omega_max 10000 [rpm] max rotation rate
28 | P_shaft [kW] shaft power
29 | M_tip .5 [-] Tip mach number
30 | a 295 [m/s] Speed of sound at altitude
31 | """
32 |
33 | def helper(self, c):
34 | return 2. - 1./c(self.etaadd)
35 |
36 | @parse_variables(__doc__, globals())
37 | def setup(self, static, state):
38 | V = state.V
39 | rho = state.rho
40 | R = static.R
41 |
42 | constraints = [eta <= etav*etai,
43 | Tc >= T/(0.5*rho*V**2*pi*R**2),
44 | z2 >= Tc + 1,
45 | etai*(z1 + z2**0.5/etaadd) <= 2,
46 | lam >= V/(omega*R),
47 | CT >= Tc*lam**2,
48 | CP <= Q*omega/(.5*rho*(omega*R)**3*pi*R**2),
49 | eta >= CT*lam/CP,
50 | omega <= omega_max,
51 | P_shaft == Q*omega,
52 | (M_tip*a)**2 >= (omega*R)**2 + V**2,
53 | static.T_m >= T
54 | ]
55 | return constraints
56 |
57 |
58 | class Propeller(Model):
59 | """ Propeller Model
60 |
61 | Variables
62 | ---------
63 | R [ft] prop radius
64 | W [lbf] prop weight
65 | K 4e-4 [1/ft^2] prop weight scaling factor
66 | T_m [lbf] prop max static thrust
67 |
68 | Variables of length N
69 | ---------------------
70 | c [ft] prop chord
71 | """
72 |
73 | flight_model = ActuatorProp
74 |
75 | @parse_variables(__doc__, globals())
76 | def setup(self, N=5):
77 | self.N = N
78 | return [W >= K*T_m*R**2]
79 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/README.md:
--------------------------------------------------------------------------------
1 | # Aircraft Empennage
2 |
3 | The empennage adds both weight and drag to each aircraft. The Empennage model in `empennage.py` creates 3 models: TailBoom, HorizontalTail, and VerticalTail. Each component has its own separate bending and aerodynamic models.
4 |
5 | ## TailBoom
6 |
7 | The tail boom has a diameter $d$, root wall thickness $t_0$, root moment of inertia $I_0$, modulus $E$, and density $\rho_{\text{cfrp}} = 1.6$ [g/cm$^3$], and length $l_{\text{h}}$.
8 | The total mass and root bending inertia are imposed in the optimization model as
9 | \begin{align}
10 | m &\geq \pi \rho_{\text{cfrp}} t_0 d l_{\text{h}} \left( 1 - \frac{1}{2} k\right) \\
11 | I_0 &\leq \pi t_0 d^3/8
12 | \end{align}
13 |
14 | where the index $k=0$ corresponds to a uniform wall thickness and stiffness, and $k=1$ corresponds to a linear drop-off to zero. For both the solar-electric and gas powered aircraft $k=0.8$ is assumed.
15 | When the tail boom is loaded at the endpoint $x=l_{\text{h}}$, by the horizontal tail lift $L_{\text{h}}$, the end deflection angle follows from standard beam analysis
16 |
17 | \begin{align}
18 | \label{e:boomdefl}
19 | \theta &\geq \frac{L_{\text{h}} l_{\text{h}}^2}{EI_0} \frac{1+k}{2} \\
20 | L_{\text{h}} &= \frac{1}{2} C_{L_{\text{h}}} \rho V^2 S_{\text{h}}.
21 | \end{align}
22 |
23 | ## HorizontalTail
24 |
25 | The horizontal tail can be sized to satisfy a horizontal tail volume coefficient condition, $V_{\text{h}} = 0.45$,
26 |
27 | \begin{equation}
28 | V_{\text{h}} = \frac{S_{\text{h}}l_{\text{h}}}{Sc}
29 | \end{equation}
30 |
31 | ## VerticalTail
32 |
33 | The vertical tail is sized to meet a conservative tail volume coefficient, $V_{\text{v}}= 0.04$,
34 |
35 | \begin{equation}
36 | \label{e:vtv}
37 | V_{\text{v}} = \frac{S_{\text{v}}}{S} \frac{l_{\text{v}}}{b}
38 | \end{equation}
39 |
40 | where $l_{\text{v}}$ is the vertical tail moment arm, assumed to be equal to the horizontal tail moment arm, $l_{\text{v}} = l_{\text{h}}$.
41 |
42 | ## Both Tails
43 |
44 | Both the horizontal and vertical tails are assumed to have a carbon fiber skin and solid foam interior where their respective densities are $\rho_{A_{\text{cfrp}}} = 0.049$ [g/cm$^2$], $\rho_{\text{foam}} = 1.5$ [lbf/ft$^3$].
45 | The weight of the horizontal and vertical tails is
46 |
47 | \begin{align}
48 | \label{e:htweight}
49 | W_{\text{h}}/m_{\text{fac}} &= \rho_{\text{foam}} \frac{S_{\text{h}}^2}{b_{\text{h}}} \bar{A} + g\rho_{A_{\text{cfrp}}} S_{\text{h}} \\
50 | \label{e:vtweight}
51 | W_{\text{v}}/m_{\text{fac}} &= \rho_{\text{foam}} \frac{S_{\text{v}}^2}{b_{\text{v}}} \bar{A} + g\rho_{A_{\text{cfrp}}} S_{\text{v}}
52 | \end{align}
53 |
54 | where $b_{\text{h}}$ and $b_{\text{v}}$ are the spans of the horizontal and vertical tails respectively and $\bar{A}$ is the cross sectional area of the NACA 0008 airfoil. The margin factor $m_{\text{fac}}=1.1$, is included to account for control surfaces, attachment joints, actuators, etc.
55 |
56 | ## TailBoomAero
57 |
58 | The drag of the tail boom is calculated using a turbulent flat plate model,
59 |
60 | \begin{align}
61 | \label{e:boomdrag}
62 | D_{\text{boom}} &\geq \frac{1}{2} C_f \rho V^2 l_{\text{h}}\pi d \\
63 | C_f &\geq \frac{0.445}{Re_{\text{boom}}^{0.3}} \\
64 | Re_{\text{boom}} &= \frac{V\rho l_{\text{h}}}{\mu}
65 | \end{align}
66 |
67 | ## TailAero
68 |
69 | The drag of the horizontal and vertical tails is computed using a GP-compatible fit of XFOIL data for a range of Reynolds numbers and NACA airfoil thicknesses,
70 |
71 | \begin{align}
72 | D_{\text{h}} &\geq \frac{1}{2} c_{d_{\text{h}}} \rho V^2 S_{\text{h}} \\
73 | D_{\text{v}} &\geq \frac{1}{2} c_{d_{\text{v}}} \rho V^2 S_{\text{v}} \\
74 | \label{e:taildrag}
75 | c_{d_{\text{(v,h)}}}^{70.5599} &\geq \num{7.42688d-90} \left( \frac{Re_{\text{(v,h)}}}{\num{1d3}} \right)^{-33.0637}(100 \tau_{\text{(v,h)}})^{18.0419} \nonumber \\
76 | & + \num{5.02826d-163}\left(\frac{Re_{\text{(v,h)}}}{\num{1d3}}\right)^{-18.7959} (100\tau_{\text{(v,h)}})^{53.1879} \\
77 | &+ \num{4.22901d-77}\left(\frac{Re_{\text{(v,h)}}}{\num{1d3}}\right)^{-41.1704} (100\tau_{\text{(v,h)}})^{28.4609} \nonumber \\
78 | Re_{\text{v}} &= \frac{V\rho S_{\text{v}}/b_{\text{v}}}{\mu} \\
79 | Re_{\text{h}} &= \frac{V\rho S_{\text{h}}/b_{\text{h}}}{\mu}
80 | \end{align}
81 |
82 | where the selected airfoil is the NACA 0008 for both the horizontal and vertical tails (i.e. $\tau_{\text{(v,h)}} = 0.08$).
83 | The XFOIL data was generated for a zero angle of attack, based upon steady level flight where neither surface is generating lift.
84 | A comparison of the XFOIL data and Equation~\eqref{e:taildrag} is shown in Figure~\ref{f:taildragpolar}.
85 |
86 | 
87 |
88 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/tail/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/empennage.py:
--------------------------------------------------------------------------------
1 | " empennage.py "
2 | from gpkit import Model, parse_variables
3 | from .horizontal_tail import HorizontalTail
4 | from .vertical_tail import VerticalTail
5 | from .tail_boom import TailBoom, TailBoomState
6 |
7 | #pylint: disable=attribute-defined-outside-init, no-member, exec-used
8 | #pylint: disable=too-many-instance-attributes, invalid-name, undefined-variable
9 | class Empennage(Model):
10 | """empennage model, consisting of vertical, horizontal and tailboom
11 |
12 | Variables
13 | ---------
14 | mfac 1.0 [-] tail weight margin factor
15 | W [lbf] empennage weight
16 |
17 | SKIP VERIFICATION
18 |
19 | Upper Unbounded
20 | ---------------
21 | W, vtail.Vv, htail.Vh, tailboom.cave (if not tbSecWeight)
22 | htail.planform.tau (if not hSparModel)
23 | vtail.planform.tau (if not vSparModel)
24 |
25 | Lower Unbounded
26 | ---------------
27 | htail.lh, htail.Vh, htail.planform.b, htail.mh
28 | vtail.lv, vtail.Vv, vtail.planform.b
29 | htail.planform.tau (if not hSparModel)
30 | vtail.planform.tau (if not vSparModel)
31 | htail.spar.Sy (if hSparModel), htail.spar.J (if hSparModel)
32 | vtail.spar.Sy (if vSparModel), vtail.spar.J (if vSparModel)
33 | tailboom.Sy, tailboom.cave (if not tbSecWeight), tailboom.J (if tbSecWeight)
34 |
35 | LaTex Strings
36 | -------------
37 | mfac m_{\\mathrm{fac}}
38 |
39 | """
40 | @parse_variables(__doc__, globals())
41 | def setup(self, N=2):
42 | self.htail = HorizontalTail()
43 | self.hSparModel = self.htail.sparModel
44 | self.htail.substitutions.update({self.htail.mfac: 1.1})
45 | lh = self.lh = self.htail.lh
46 | self.vtail = VerticalTail()
47 | self.vSparModel = self.vtail.sparModel
48 | self.vtail.substitutions.update({self.vtail.mfac: 1.1})
49 | lv = self.lv = self.vtail.lv
50 | self.tailboom = TailBoom(N=N)
51 | self.tbSecWeight = self.tailboom.secondaryWeight
52 | self.components = [self.htail, self.vtail, self.tailboom]
53 | l = self.l = self.tailboom.l
54 |
55 | constraints = [
56 | W/mfac >= sum(c.W for c in self.components),
57 | l >= lh, l >= lv,
58 | ]
59 |
60 | return self.components, constraints
61 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/horizontal_tail.py:
--------------------------------------------------------------------------------
1 | " horizontal tail "
2 | import numpy as np
3 | from gpkit import parse_variables
4 | from .tail_aero import TailAero
5 | from gpkitmodels.GP.aircraft.wing.wing import Wing
6 | from gpkitmodels.GP.aircraft.wing.wing_skin import WingSkin
7 | from gpkitmodels.GP.aircraft.wing.wing_core import WingCore
8 |
9 | #pylint: disable=attribute-defined-outside-init, no-member
10 | #pylint: disable=exec-used, undefined-variable
11 |
12 | class HorizontalTail(Wing):
13 | """ Horizontal Tail Model
14 |
15 | Variables
16 | ---------
17 | Vh [-] horizontal tail volume coefficient
18 | lh [ft] horizontal tail moment arm
19 | CLhmin 0.75 [-] max downlift coefficient
20 | mh [-] horizontal tail span effectiveness
21 |
22 | Upper Unbounded
23 | ---------------
24 | lh, Vh, W, planform.tau (if not sparModel)
25 |
26 | Lower Unbounded
27 | ---------------
28 | lh, Vh, planform.b, mh, planform.tau (if not sparModel)
29 | spar.Sy (if sparModel), spar.J (if sparJ)
30 |
31 | LaTex Strings
32 | -------------
33 | Vh V_{\\mathrm{h}}
34 | lh l_{\\mathrm{h}}
35 | CLmin C_{L_{\\mathrm{min}}}
36 | mh m_{\\mathrm{h}}
37 |
38 | """
39 | flight_model = TailAero
40 | fillModel = WingCore
41 | sparModel = None
42 |
43 | @parse_variables(__doc__, globals())
44 | def setup(self, N=3):
45 | self.ascs = Wing.setup(self, N)
46 | self.planform.substitutions.update(
47 | {self.planform.AR: 4, self.planform.lam: 0.8})
48 | if self.fillModel:
49 | self.foam.substitutions.update({self.foam.Abar: 0.0548,
50 | self.foam.material.rho: 0.024})
51 |
52 | return self.ascs, mh*(1+2.0/self.planform["AR"]) <= 2*np.pi
53 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tail_aero.py:
--------------------------------------------------------------------------------
1 | " tail aerodynamics "
2 | import os
3 | import pandas as pd
4 | from gpkit import Model, parse_variables
5 | from gpfit.fit_constraintset import XfoilFit
6 |
7 | #pylint: disable=exec-used, attribute-defined-outside-init, undefined-variable
8 | #pylint: disable=no-member
9 |
10 | class TailAero(Model):
11 | """Tail Aero Model
12 |
13 | Variables
14 | ---------
15 | Re [-] Reynolds number
16 | Cd [-] drag coefficient
17 |
18 | Upper Unbounded
19 | ---------------
20 | Cd, Re, S, V, b, rho
21 |
22 | Lower Unbounded
23 | ---------------
24 | S, tau, V, b, rho
25 |
26 | LaTex Strings
27 | -------------
28 | Cd C_d
29 |
30 | """
31 | @parse_variables(__doc__, globals())
32 | def setup(self, static, state):
33 | self.state = state
34 |
35 | cmac = self.cmac = static.planform.cmac
36 | b = self.b = static.planform.b
37 | S = self.S = static.planform.S
38 | tau = self.tau = static.planform.tau
39 | rho = self.rho = state.rho
40 | V = self.V = state.V
41 | mu = self.mu = state.mu
42 | path = os.path.dirname(__file__)
43 | fd = pd.read_csv(path + os.sep + "tail_dragfit.csv").to_dict(
44 | orient="records")[0]
45 |
46 | constraints = [
47 | Re == V*rho*S/b/mu,
48 | # XfoilFit(fd, Cd, [Re, static["\\tau"]],
49 | # err_margin="RMS", airfoil="naca 0008")
50 | XfoilFit(fd, Cd, [Re, tau], err_margin="RMS")
51 | ]
52 |
53 | return constraints
54 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tail_boom.py:
--------------------------------------------------------------------------------
1 | " tail boom model "
2 | from numpy import pi
3 | from gpkit import Model, parse_variables, Variable, VectorVariable, units
4 | from .tube_spar import TubeSpar
5 | from gpkitmodels.GP.beam.beam import Beam
6 | from gpkitmodels import g
7 |
8 | #pylint: disable=exec-used, undefined-variable, invalid-name
9 | #pylint: disable=attribute-defined-outside-init
10 |
11 | class TailBoomAero(Model):
12 | """ Tail Boom Aero Model
13 |
14 | Variables
15 | ---------
16 | Cf [-] tail boom skin friction coefficient
17 | Re [-] tail boom reynolds number
18 |
19 | Upper Unbounded
20 | ---------------
21 | Re, Cf, l, V, rho
22 |
23 | Lower Unbounded
24 | ---------------
25 | l, V, rho
26 |
27 | LaTex Strings
28 | -------------
29 | Cf C_f
30 |
31 | """
32 | @parse_variables(__doc__, globals())
33 | def setup(self, static, state):
34 | self.state = state
35 |
36 | l = self.l = static.l
37 | rho = self.rho = state.rho
38 | V = self.V = state.V
39 | mu = self.mu = state.mu
40 |
41 | return [Re == V*rho*l/mu,
42 | Cf >= 0.455/Re**0.3,
43 | ]
44 |
45 | class TailBoomState(Model):
46 | """ Tail Boom Loading State
47 |
48 | Variables
49 | ---------
50 | rhosl 1.225 [kg/m^3] air density at sea level
51 | Vne 40 [m/s] never exceed vehicle speed
52 |
53 | LaTex Strings
54 | -------------
55 | rhosl \\rho_{\\mathrm{sl}}
56 | Vne V_{\\mathrm{NE}}
57 |
58 | """
59 | @parse_variables(__doc__, globals())
60 | def setup(self):
61 | pass
62 |
63 |
64 | class VerticalBoomTorsion(Model):
65 | """ Tail Boom Torsion from Vertical Tail
66 |
67 | Variables
68 | ---------
69 | T [N*m] vertical tail moment
70 | taucfrp 210 [MPa] torsional stress limit of carbon
71 |
72 | Upper Unbounded
73 | ---------------
74 | J
75 |
76 | Lower Unbounded
77 | ---------------
78 | d0, b, S
79 |
80 | LaTex Strings
81 | -------------
82 | taucfrp \\tau_{\\mathrm{CFRP}}
83 |
84 | """
85 | @parse_variables(__doc__, globals())
86 | def setup(self, tailboom, vtail, state):
87 | J = self.J = tailboom.J
88 | d0 = self.d0 = tailboom.d
89 | b = self.b = vtail.planform.b
90 | S = self.S = vtail.planform.S
91 | rhosl = self.rhosl = state.rhosl
92 | Vne = self.Vne = state.Vne
93 | CLmax = vtail.planform.CLmax
94 |
95 | return [T >= 0.5*rhosl*Vne**2*S*CLmax*b,
96 | taucfrp >= T*d0/2/J
97 | ]
98 |
99 | class TailBoomBending(Model):
100 | """ Tail Boom Bending
101 |
102 | Variables
103 | ---------
104 | F [N] tail force
105 | th [-] tail boom deflection angle
106 | kappa 0.1 [-] max tail boom deflection
107 | Nsafety 1.0 [-] safety load factor
108 |
109 | Variables of length tailboom.N-1
110 | --------------------------------
111 | Mr [N*m] section root moment
112 |
113 |
114 | Upper Unbounded
115 | ---------------
116 | tailboom.I0, tailboom.Sy
117 | tailboom.J (if tailboomJ), tailboom.I
118 |
119 | Lower Unbounded
120 | ---------------
121 | htail.planform.S, htail.planform.CLmax
122 | tailboom.l, tailboom.deta
123 | state.qne
124 |
125 | LaTex Strings
126 | -------------
127 | th \\theta
128 | thmax \\theta_{\\mathrm{max}}
129 |
130 | """
131 | @parse_variables(__doc__, globals())
132 | def setup(self, tailboom, htail, state):
133 | N = self.N = tailboom.N
134 | self.state = state
135 | self.htail = htail
136 | self.tailboom = tailboom
137 |
138 | Beam.qbarFun = [1e-10]*N
139 | Beam.SbarFun = [1.]*N
140 | beam = self.beam = Beam(N)
141 |
142 | I = tailboom.I
143 | tailboom.I0 = I[0]
144 | l = tailboom.l
145 | S = htail.planform.S
146 | E = tailboom.material.E
147 | Sy = tailboom.Sy
148 | qne = state.qne
149 | CLmax = htail.planform.CLmax
150 | deta = tailboom.deta
151 | sigma = tailboom.material.sigma
152 |
153 | constraints = [beam.dx == deta,
154 | F >= qne*S,
155 | beam["\\bar{EI}"] <= E*I/F/l**2/2,
156 | Mr >= beam["\\bar{M}"][:-1]*F*l,
157 | sigma >= Mr/Sy,
158 | th == beam["\\theta"][-1],
159 | beam["\\bar{\\delta}"][-1]*CLmax*Nsafety <= kappa]
160 |
161 | self.tailboomJ = hasattr(tailboom, "J")
162 | if self.tailboomJ:
163 | constraints.append(tailboom.J >= 1e-10*units("m^4"))
164 |
165 | return constraints, beam
166 |
167 | class TailBoom(TubeSpar):
168 | """ Tail Boom Model
169 |
170 | Variables
171 | ---------
172 | l [ft] tail boom length
173 | S [ft^2] tail boom surface area
174 | b [ft] twice tail boom length
175 | deta 1./(N-1) [-] normalized segment length
176 | tau 1.0 [-] thickness to width ratio
177 | rhoA 0.15 [kg/m^2] total aerial density
178 |
179 | Variables of length N-1
180 | -----------------------
181 | cave [in] average segment width
182 |
183 | """
184 |
185 | flight_model = TailBoomAero
186 | tailLoad = TailBoomBending
187 | secondaryWeight = None
188 |
189 | @parse_variables(__doc__, globals())
190 | def setup(self, N=5):
191 | self.N = N
192 | self.spar = super(TailBoom, self).setup(N, self)
193 |
194 | if self.secondaryWeight:
195 | self.weight.right += rhoA*g*S
196 |
197 | d0 = self.d0 = self.d[0]
198 |
199 | return self.spar, [S == l*pi*d0, b == 2*l]
200 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tail_dragfit.csv:
--------------------------------------------------------------------------------
1 | ub0,ub1,rms_err,e31,e30,e11,e10,ftype,c4,lb1,lb0,K,a1,c3,c2,c1,c0,e21,e40,e41,e20,d,e00,e01,max_err
2 | 999999.99999999953,0.14999999999999999,0.040701846058073914,0.46746638912526689,-0.4823469575592283,0.2463415216530887,-0.48486661931969488,MA,218.73655005090146,0.05000000000000001,19999.999999999982,5,1,9.5091938064942685,16.259467895292175,5.4468646580249409,0.33993775619145372,0.46638445457840788,-0.60387089537858818,1.312443752168466,-0.53994320248315575,2,-0.18199062784771394,0.77460393316522413,0.19620270014169217
3 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tail_tests.py:
--------------------------------------------------------------------------------
1 | " test tail models "
2 | from gpkitmodels.GP.aircraft.tail.horizontal_tail import HorizontalTail
3 | from gpkitmodels.GP.aircraft.tail.vertical_tail import VerticalTail
4 | from gpkitmodels.GP.aircraft.tail.empennage import Empennage
5 | from gpkitmodels.GP.aircraft.wing.wing_test import FlightState
6 | from gpkitmodels.GP.aircraft.wing.boxspar import BoxSpar
7 | from gpkitmodels.GP.aircraft.tail.tail_boom import TailBoom
8 | from gpkit import Model, Variable, units
9 |
10 | #pylint: disable=no-member
11 |
12 | def test_htail():
13 |
14 | Sw = Variable("S_w", 50, "ft**2", "wing area")
15 | cmac = Variable("cmac", 15, "in", "wing MAC")
16 | ht = HorizontalTail()
17 | fs = FlightState()
18 | ht.substitutions.update({ht.W: 5, ht.mh: 0.01, ht.planform.AR: 4,
19 | ht.Vh: 0.5, ht.lh: 10, ht.planform.tau: 0.08})
20 | perf = ht.flight_model(ht, fs)
21 |
22 | m = Model(perf.Cd,
23 | [ht.Vh <= ht.planform.S*ht.lh/Sw/cmac, ht, fs, perf])
24 | m.solve(verbosity=0)
25 |
26 | def test_vtail():
27 |
28 | Sw = Variable("S_w", 50, "ft**2", "wing area")
29 | bw = Variable("b_w", 20, "ft", "wing span")
30 | vt = VerticalTail()
31 | fs = FlightState()
32 | vt.substitutions.update({vt.W: 5, vt.planform.AR: 3, vt.Vv: 0.04,
33 | vt.lv: 10, vt.planform.tau: 0.08})
34 | perf = vt.flight_model(vt, fs)
35 |
36 | m = Model(perf.Cd, [vt.Vv <= vt.planform.S*vt.lv/Sw/bw, vt, fs, perf])
37 | m.solve(verbosity=0)
38 |
39 | def test_emp():
40 |
41 | Sw = Variable("S_w", 50, "ft**2", "wing area")
42 | bw = Variable("b_w", 20, "ft", "wing span")
43 | cmac = Variable("cmac", 15, "in", "wing MAC")
44 | emp = Empennage()
45 | fs = FlightState()
46 | emp.substitutions.update({emp.W: 10, emp.tailboom.l: 5,
47 | emp.htail.planform.AR: 4,
48 | emp.vtail.planform.AR: 4,
49 | emp.htail.planform.tau: 0.08,
50 | emp.vtail.planform.tau: 0.08,
51 | emp.vtail.Vv: 0.04,
52 | emp.htail.Vh: 0.4,
53 | emp.htail.mh: 0.01})
54 | htperf = emp.htail.flight_model(emp.htail, fs)
55 | vtperf = emp.vtail.flight_model(emp.vtail, fs)
56 | tbperf = emp.tailboom.flight_model(emp.tailboom, fs)
57 | hbend = emp.tailboom.tailLoad(emp.tailboom, emp.htail, fs)
58 | vbend = emp.tailboom.tailLoad(emp.tailboom, emp.vtail, fs)
59 |
60 | m = Model(htperf.Cd + vtperf.Cd + tbperf.Cf,
61 | [emp.vtail.lv == emp.tailboom.l, emp.htail.lh == emp.tailboom.l,
62 | emp.htail.Vh <= emp.htail.planform.S*emp.htail.lh/Sw/cmac,
63 | emp.vtail.Vv <= emp.vtail.planform.S*emp.vtail.lv/Sw/bw,
64 | fs, emp, fs, htperf, vtperf, tbperf, hbend, vbend])
65 |
66 | from gpkit import settings
67 | if settings["default_solver"] == "cvxopt":
68 | for l in [hbend, vbend]:
69 | for v in ["\\bar{M}_{tip}", "\\bar{\\delta}_{root}",
70 | "\\theta_{root}"]:
71 | m.substitutions[l[v]] = 1e-3
72 |
73 | m.solve(verbosity=0, use_leqs=False) # cvxopt gets singular with leqs
74 |
75 | def test_tailboom_mod():
76 |
77 | Sw = Variable("S_w", 50, "ft**2", "wing area")
78 | bw = Variable("b_w", 20, "ft", "wing span")
79 | cmac = Variable("cmac", 15, "in", "wing MAC")
80 | cmax = Variable("cmax", 5, "in", "max width")
81 | TailBoom.__bases__ = (BoxSpar,)
82 | TailBoom.secondaryWeight = True
83 | emp = Empennage(N=5)
84 | fs = FlightState()
85 | emp.substitutions.update({emp.W: 10, emp.tailboom.l: 5,
86 | emp.htail.planform.AR: 4,
87 | emp.vtail.planform.AR: 4,
88 | emp.htail.planform.tau: 0.08,
89 | emp.vtail.planform.tau: 0.08,
90 | emp.vtail.Vv: 0.04,
91 | emp.htail.Vh: 0.4,
92 | emp.htail.mh: 0.01,
93 | emp.tailboom.wlim: 1})
94 | htperf = emp.htail.flight_model(emp.htail, fs)
95 | vtperf = emp.vtail.flight_model(emp.vtail, fs)
96 | tbperf = emp.tailboom.flight_model(emp.tailboom, fs)
97 | hbend = emp.tailboom.tailLoad(emp.tailboom, emp.htail, fs)
98 | vbend = emp.tailboom.tailLoad(emp.tailboom, emp.vtail, fs)
99 |
100 | m = Model(htperf.Cd + vtperf.Cd + tbperf.Cf,
101 | [emp.vtail.lv == emp.tailboom.l, emp.htail.lh == emp.tailboom.l,
102 | emp.htail.Vh <= emp.htail.planform.S*emp.htail.lh/Sw/cmac,
103 | emp.vtail.Vv <= emp.vtail.planform.S*emp.vtail.lv/Sw/bw,
104 | emp.tailboom.cave <= cmax,
105 | emp, fs, htperf, vtperf, tbperf, hbend, vbend])
106 |
107 | from gpkit import settings
108 | if settings["default_solver"] == "cvxopt":
109 | for l in [hbend, vbend]:
110 | for v in ["\\bar{M}_{tip}", "\\bar{\\delta}_{root}",
111 | "\\theta_{root}"]:
112 | m.substitutions[l[v]] = 1e-3
113 |
114 | m.solve(verbosity=0)
115 |
116 | def test():
117 | test_htail()
118 | test_vtail()
119 | test_emp()
120 | test_tailboom_mod()
121 |
122 | if __name__ == "__main__":
123 | test()
124 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/taildragpolar.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/tail/taildragpolar.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tailpolars/genpolar.sh:
--------------------------------------------------------------------------------
1 | NACA=$1
2 | POLARFILE=naca$1.cl0.Re$2k.pol
3 |
4 | if [ -f $POLARFILE ] ; then
5 | echo "yes"
6 | rm $POLARFILE
7 | fi
8 |
9 | xfoil << EOF
10 | naca $1
11 | oper
12 | v $2e3
13 | pacc
14 | $POLARFILE
15 |
16 | iter 200
17 | cl 0.0
18 |
19 | quit
20 | EOF
21 |
22 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tailpolars/naca_cl0fits.py:
--------------------------------------------------------------------------------
1 | "naca_polarfits.py"
2 | from builtins import zip
3 | from builtins import range
4 | import numpy as np
5 | import pandas as pd
6 | import matplotlib.pyplot as plt
7 | plt.rcParams.update({'font.size':15})
8 |
9 | def text_to_df(filename):
10 | "parse XFOIL polars and concatente data in DataFrame"
11 | lines = list(open(filename))
12 | for i, l in enumerate(lines):
13 | lines[i] = l.split("\n")[0]
14 | for j in 10-np.arange(9):
15 | if " "*j in lines[i]:
16 | lines[i] = lines[i].replace(" "*j, " ")
17 | if "---" in lines[i]:
18 | start = i
19 | data = {}
20 | titles = lines[start-1].split(" ")[1:]
21 | for t in titles:
22 | data[t] = []
23 |
24 | for l in lines[start+1:]:
25 | for i, v in enumerate(l.split(" ")[1:]):
26 | data[titles[i]].append(v)
27 |
28 | df = pd.DataFrame(data)
29 | df = df.astype(float)
30 | return df
31 |
32 | def fit_setup(naca_range, re_range):
33 | "set up x and y parameters for gp fitting"
34 | tau = [[float(n)]*len(re_range) for n in naca_range]
35 | re = [re_range]*len(naca_range)
36 | cd = []
37 | for n in naca_range:
38 | for r in re_range:
39 | dataf = text_to_df("naca%s.cl0.Re%dk.pol" % (n, r))
40 | cd.append(dataf["CD"])
41 |
42 |
43 | u1 = np.hstack(re)
44 | u2 = np.hstack(tau)
45 | w = np.hstack(cd)
46 | u1 = u1.astype(np.float)
47 | u2 = u2.astype(np.float)
48 | w = w.astype(np.float)
49 | u = [u1, u2]
50 | x = np.log(u)
51 | y = np.log(w)
52 | return x, y
53 |
54 | def return_fit(u_1, u_2):
55 | "naca tau and reynolds fit"
56 | w = (7.42688e-90 * (u_1)**-33.0637 * (u_2)**18.0419
57 | + 5.02826e-163 * (u_1)**-18.7959 * (u_2)**53.1879
58 | + 4.22901e-77 * (u_1)**-41.1704 * (u_2)**28.4609)**(1/70.5599)
59 | # SMA function, K=3, max RMS error = 0.0173
60 | return w
61 |
62 | def plot_fits(naca_range, re_range):
63 | "plot fit compared to data"
64 |
65 | fig, ax = plt.subplots()
66 | colors = ["k", "m", "b", "g", "y", "r"]
67 | assert len(colors) == len(naca_range)
68 | res = np.linspace(re_range[0], re_range[-1], 50)
69 | for n, col in zip(naca_range, colors):
70 | cd = []
71 | for r in re_range:
72 | dataf = text_to_df("naca%s.cl0.Re%dk.pol" % (n, r))
73 | cd.append(dataf["CD"])
74 | if True in [c.empty for c in cd]:
75 | i = [c.empty for c in cd].index(True)
76 | cd[i] = (cd[i-1] + cd[i+1])/2
77 | ax.plot(re_range, cd, "o", mec=col, mfc="None", mew=1.5)
78 | w = return_fit(res, float(n))
79 | ax.plot(res, w, c=col, label="NACA %s" % n, lw=2)
80 | ax.legend(fontsize=15)
81 | labels = ["k" + item.get_text() for item in ax.get_xticklabels()]
82 | labels = ["%dk" % l for l in np.linspace(200, 900, len(labels))]
83 | ax.set_xticklabels(labels)
84 | ax.set_xlabel("$Re$")
85 | ax.set_ylabel("$c_{dp}$")
86 | ax.grid()
87 | return fig, ax
88 |
89 | if __name__ == "__main__":
90 | Re = list(range(200, 950, 50))
91 | NACA = ["0005", "0008", "0009", "0010", "0015", "0020"]
92 | X, Y = fit_setup(NACA, Re) # call fit(X, Y, 4, "SMA") to get fit
93 | F, A = plot_fits(NACA, Re)
94 | F.savefig("taildragpolar.pdf", bbox_inches="tight")
95 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tailpolars/nacasweep.sh:
--------------------------------------------------------------------------------
1 | NACA="0005 0008 0009 0010 0015 0020"
2 | Re="200 250 300 350 400 450 500 550 600 650 700 750 800 850 900"
3 |
4 | for r in $Re
5 | do
6 | for n in $NACA
7 | do
8 | ./genpolar.sh $n $r
9 | done
10 | done
11 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/tube_spar.py:
--------------------------------------------------------------------------------
1 | " tube spar "
2 | from numpy import pi
3 | from gpkitmodels.GP.materials import cfrpfabric
4 | from gpkitmodels import g
5 | from gpkit import Model, parse_variables
6 |
7 | class TubeSpar(Model):
8 | """ Tail Boom Model
9 |
10 | Variables
11 | ---------
12 | mfac 1.0 [-] weight margin factor
13 | k 0.8 [-] taper index
14 | kfac self.minusk2 [-] (1-k/2)
15 | W [lbf] spar weight
16 |
17 | Variables of length N-1
18 | -----------------------
19 | I [m^4] moment of inertia
20 | d [in] diameter
21 | t [in] thickness
22 | dm [kg] segment mass
23 | Sy [m^3] section modulus
24 |
25 | Upper Unbounded
26 | ---------------
27 | W
28 |
29 | Lower Unbounded
30 | ---------------
31 | J, l, I0
32 |
33 | LaTex Strings
34 | -------------
35 | kfac (1-k/2)
36 | mfac m_{\\mathrm{fac}}
37 |
38 | """
39 |
40 | minusk2 = lambda self, c: 1-c(self.k)/2.
41 | material = cfrpfabric
42 |
43 | @parse_variables(__doc__, globals())
44 | def setup(self, N, surface):
45 | deta = surface.deta
46 | tmin = self.material.tmin
47 | rho = self.material.rho
48 | l = surface.l
49 |
50 | self.weight = W/mfac >= g*dm.sum()
51 |
52 | return [I <= pi*t*d**3/8.0,
53 | Sy <= 2*I/d,
54 | dm >= pi*rho*d*deta*t*kfac*l,
55 | self.weight,
56 | t >= tmin]
57 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/tail/vertical_tail.py:
--------------------------------------------------------------------------------
1 | " vertical tail "
2 | from gpkit import parse_variables
3 | from .tail_aero import TailAero
4 | from gpkitmodels.GP.aircraft.wing.wing import Wing
5 | from gpkitmodels.GP.aircraft.wing.wing_core import WingCore
6 | from gpkitmodels.GP.aircraft.wing.wing_skin import WingSkin
7 |
8 | #pylint: disable=attribute-defined-outside-init, no-member, exec-used
9 |
10 | class VerticalTail(Wing):
11 | """ Vertical Tail Model
12 |
13 | Variables
14 | ---------
15 | Vv [-] vertical tail volume coefficient
16 | lv [ft] vertical tail moment arm
17 |
18 | Upper Unbounded
19 | ---------------
20 | lv, Vv, W, planform.tau (if not sparModel)
21 |
22 | Lower Unbounded
23 | ---------------
24 | lv, Vv, planform.b, planform.tau (if not sparModel)
25 | spar.Sy (if sparModel), spar.J (if sparJ)
26 |
27 | LaTex Strings
28 | -------------
29 | Vv V_{\\mathrm{v}}
30 | lv l_{\\mathrm{v}}
31 |
32 | """
33 |
34 | flight_model = TailAero
35 | fillModel = WingCore
36 | sparModel = None
37 |
38 | @parse_variables(__doc__, globals())
39 | def setup(self, N=3):
40 | self.ascs = Wing.setup(self, N)
41 | self.planform.substitutions.update(
42 | {self.planform.lam: 0.8, self.planform.AR: 4})
43 | if self.fillModel:
44 | self.foam.substitutions.update({self.foam.Abar: 0.0548,
45 | self.foam.material.rho: 0.024})
46 |
47 | return self.ascs
48 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/README.md:
--------------------------------------------------------------------------------
1 | # Wing Structural and Aero Models
2 |
3 | The wing GP model is mostly based off of Mark Drela's wing bending notes from MIT's [OpenCourseWare](https://ocw.mit.edu/courses/aeronautics-and-astronautics/16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006/systems-labs-06/spl10.pdf) site. Basic assumptions are a constant tapered wing, no sweep, bending loads are taken by the spar, torsional loads are taken by the skin.
4 |
5 | The usage is as follows: `Wing` in `wing.py` is created either by itself or in an aircraft model. It has two functions that return models to be created separately from `Wing`, `WingAero` and `WingLoading` (also in `wing.py`). `WingAero` is an erodynamic model of the JHO airfoil. $C_d$ is to be used in an overall aircraft aerodyanmic model. `WingLoading` creates loading models that govern the bending loads of the spar and the torsional loads of the skin. Inside `Wing` 2 objects are created `CapSpar` or `TubeSpar` and `WingSkin` with the option to create `WingInterior` if `hollow=True`. Their weights are summed to give an overall weight of the wing. Each submodel is described in detail.
6 |
7 | ## Wing
8 |
9 | Constrains basic shape of wing using
10 |
11 | $$ b^2 = SAR$$
12 | $$\bar{c}(y) \equiv \frac{c(y)}{S/b} = \frac{2}{1+\lambda} \left( 1 + (\lambda - 1) \frac{2y}{b} \right) $$
13 |
14 | ## WingAero
15 |
16 | Calculates the wing drag from induced drag and wing profile drag. Wing profile drag assumes the JHO airfoil and is calculated using a posynomial fit to XFOIL data at various reynolds numbers.
17 |
18 | 
19 |
20 | ## CapSpar
21 |
22 | Set up constraints for size of spar. Spar thickness is assumed to be no greater than the max thickness of the airfoil. Width is no greater than %10 of the chord. Moment of inerita is calculated using first order approximation.
23 |
24 | ## ChordSparL
25 |
26 | Basic assumption is that the distributed load of lift and weight is proportional to the local chord.
27 |
28 | $$ q(y) \approx K_q c(y) $$
29 | $$ K_q = \frac{N_{\text{max}}W_{\text{cent}}}{S} $$
30 |
31 | This model takes as an input `CapSpar` and `Wcent`, or the center weight of the aircaft. Uses discretized beam theory to predict moment and deflection. Stress and overall wing deflection are constrainted.
32 |
33 | ## GustSparL
34 |
35 | The main assumption is the the loading is the same as the `ChordSparL` case but there is an additional gust term that varies as $1-\cos$ along the span. So the distributed load that is used as an input to the discretized beam model is
36 |
37 | $$\bar{q}(y) = \frac{q(y)b}{W_{\text{cent}}N_{\text{max}}} &\geq \bar{c}(y) \left[1 + \frac{c_{l_{\alpha}}}{C_L} \alpha_{\text{gust}} (y) \left(1 + \frac{W_{\text{wing}}}{W_{\text{cent}}} \right) \right] $$
38 |
39 | where $\alpha_{\text{gust}}$ is
40 |
41 | $$ \alpha_{\text{gust}}(y) = \tan^{-1}\left(\frac{V_{\text{gust}}(y)}{V}
42 | \right). $$
43 |
44 | and $V_{\text{gust}}$ is:
45 |
46 | $$V_{\text{gust}}(y) = V_{\text{ref}} \left(1-\cos\left(\frac{2y}{b} \frac{\pi}{2} \right) \right) $$
47 |
48 | $\alpha_{\text{gust}}$ is approximated by a monomial fit on the range of gust velocities $V/V_{\text{gust}} \in [0, 0.7]$.
49 |
50 | 
51 |
52 |
53 | ## WingSkin
54 |
55 | The wing skin calculates the weight of the wing based off of the surface aera of the wing
56 |
57 | $$ W_{\text{skin}} \geq 2 \rho_{\text{cfrp}}} t S g. $$
58 |
59 | ## WingSkinL
60 |
61 | The main assumptions here are that the wing has a constant thickness skin, the torsional loads are taken by the skin, and the highest torison occurs at the root. This model only has one constraint
62 |
63 | $$ \tau_{CFRP} \geq \frac{C_{m_w}S\rhoV_{NE}^2}{\bar{J/t} c_{root}^2 t} $$
64 |
65 | The $\bar{J/t}$ is taken from the JHO airfoil.
66 |
67 | ## WingInterior
68 |
69 | If this model is enabled, the wing is assummed to be filled with foam. The cross sectional aera of the wing is assumed to be the JHO airfoil.
70 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/wing/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/arctan_fit.csv:
--------------------------------------------------------------------------------
1 | lb0,ub0,d,K,e00,a1,ftype,max_err,c0,rms_err
2 | 1.0000000000000013e-15,0.69999999999999996,1,1,0.99602497577104232,1,MA,0.082380875848525215,0.94604144923634659,0.039722989129247634
3 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/arctan_fit.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from gpfit.fit import fit
3 | import os
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 | import sys
7 | plt.rcParams.update({'font.size':15})
8 | GENERATE = True
9 |
10 | def arctanfit():
11 | u = np.linspace(1e-15, 0.7, 100)
12 | w = np.arctan(u)
13 |
14 | x = np.log(u)
15 | y = np.log(w)
16 |
17 | cn, err = fit(x, y, 1, "MA")
18 | rm = err
19 | print("RMS error: %.4f" % rm)
20 |
21 | yfit = cn.evaluate(x)
22 | df = cn.get_dataframe()
23 | fig, ax = plt.subplots()
24 | ax.plot(u, w, lw=2)
25 | ax.plot(u, np.exp(yfit), "--", lw=2)
26 | ax.set_xlim([0, 0.7])
27 | ax.grid()
28 | ax.set_xlabel("$V_{\\mathrm{gust}}/V$")
29 | ax.set_ylabel("$\\alpha_{\\mathrm{gust}}$")
30 | ax.legend(["$\\arctan{(V_{\\mathrm{gust}}/V)}$",
31 | "$0.905 (V_{\\mathrm{gust}}/V)^{0.961}$"], loc=2, fontsize=15)
32 | return df, fig, ax
33 |
34 | if __name__ == "__main__":
35 | df, fig, ax = arctanfit()
36 | df.to_csv("arctan_fit.csv")
37 | fig.savefig("arctanfit.pdf", bbox_inches="tight")
38 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/boxspar.py:
--------------------------------------------------------------------------------
1 | " box spar "
2 | from gpkit import Model, parse_variables, SignomialsEnabled
3 | from .sparloading import SparLoading
4 | from .gustloading import GustL
5 | from gpkitmodels.GP.materials import cfrpud, cfrpfabric, foamhd
6 | from gpkitmodels import g
7 |
8 | #pylint: disable=exec-used, undefined-variable, unused-argument, invalid-name
9 |
10 | class BoxSpar(Model):
11 | """ Box Spar Model
12 |
13 | Scalar Variables
14 | ----------------
15 | W [lbf] spar weight
16 | wlim 0.15 [-] spar width to chord ratio
17 | mfac 0.97 [-] curvature knockdown factor
18 | tcoret 0.02 [-] core to thickness ratio
19 |
20 | Variables of length N-1
21 | -----------------------
22 | hin [in] height between caps
23 | I [m^4] spar x moment of inertia
24 | Sy [m^3] section modulus
25 | dm [kg] segment spar mass
26 | w [in] spar width
27 | d [in] cross sectional diameter
28 | t [in] spar cap thickness
29 | tshear [in] shear web thickness
30 | tcore [in] core thickness
31 |
32 | SKIP VERIFICATION
33 |
34 | Upper Unbounded
35 | ---------------
36 | W
37 |
38 | Lower Unbounded
39 | ---------------
40 | Sy, b, J, surface.deta
41 |
42 | LaTex Strings
43 | -------------
44 | wlim w_{\\mathrm{lim}}
45 | mfac m_{\\mathrm{fac}}
46 | hin h_{\\mathrm{in}_i}
47 | I I_i
48 | Sy S_{y_i}
49 | dm \\Delta{m}
50 | w w_i
51 | t t_i
52 | tshear t_{\\mathrm{shear}_i}
53 | tcoret (t_{\\mathrm{core}}/t)
54 |
55 | """
56 | loading = SparLoading
57 | gustloading = GustL
58 | material = cfrpud
59 | shearMaterial = cfrpfabric
60 | coreMaterial = foamhd
61 |
62 | @parse_variables(__doc__, globals())
63 | def setup(self, N, surface):
64 | self.surface = surface
65 |
66 | b = self.b = surface.b
67 | cave = self.cave = surface.cave
68 | tau = self.tau = surface.tau
69 | deta = surface.deta
70 | rho = self.material.rho
71 | rhoshear = self.shearMaterial.rho
72 | rhocore = self.coreMaterial.rho
73 | tshearmin = self.shearMaterial.tmin
74 | tmin = self.material.tmin
75 |
76 | self.weight = W >= 2*dm.sum()*g
77 |
78 | constraints = [I/mfac <= w*t*hin**2,
79 | dm >= (rho*4*w*t + 4*tshear*rhoshear*(hin + w)
80 | + 2*rhocore*tcore*(w + hin))*b/2*deta,
81 | w <= wlim*cave,
82 | cave*tau >= hin + 4*t + 2*tcore,
83 | self.weight,
84 | t >= tmin,
85 | Sy*(hin/2 + 2*t + tcore) <= I,
86 | tshear >= tshearmin,
87 | tcore >= tcoret*cave*tau,
88 | d == w,
89 | ]
90 |
91 | return constraints
92 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/capspar.py:
--------------------------------------------------------------------------------
1 | " cap spar "
2 | from gpkit import Model, parse_variables
3 | from .sparloading import SparLoading
4 | from .gustloading import GustL
5 | from gpkitmodels.GP.materials import cfrpud, cfrpfabric, foamhd
6 | from gpkitmodels import g
7 |
8 | #pylint: disable=exec-used, undefined-variable, unused-argument, invalid-name
9 |
10 | class CapSpar(Model):
11 | """ Cap Spar Model
12 |
13 | Scalar Variables
14 | ----------------
15 | E 2e7 [psi] Young modulus of CFRP
16 | W [lbf] spar weight
17 | wlim 0.15 [-] spar width to chord ratio
18 | mfac 0.97 [-] curvature knockdown factor
19 |
20 | Variables of length N-1
21 | -----------------------
22 | hin [in] height between caps
23 | I [m^4] spar x moment of inertia
24 | Sy [m^3] section modulus
25 | dm [kg] segment spar mass
26 | w [in] spar width
27 | t [in] spar cap thickness
28 | tshear [in] shear web thickness
29 |
30 | Upper Unbounded
31 | ---------------
32 | W, cave, tau
33 |
34 | Lower Unbounded
35 | ---------------
36 | Sy, b, surface.deta
37 |
38 | LaTex Strings
39 | -------------
40 | wlim w_{\\mathrm{lim}}
41 | mfac m_{\\mathrm{fac}}
42 | hin h_{\\mathrm{in}_i}
43 | I I_i
44 | Sy S_{y_i}
45 | dm \\Delta{m}
46 | w w_i
47 | t t_i
48 | tshear t_{\\mathrm{shear}_i}
49 |
50 | """
51 | loading = SparLoading
52 | gustloading = GustL
53 | material = cfrpud
54 | shearMaterial = cfrpfabric
55 | coreMaterial = foamhd
56 |
57 | @parse_variables(__doc__, globals())
58 | def setup(self, N, surface):
59 | self.surface = surface
60 |
61 | cave = self.cave = surface.cave
62 | b = self.b = surface.b
63 | deta = surface.deta
64 | tau = self.tau = surface.tau
65 | rho = self.material.rho
66 | rhoshear = self.shearMaterial.rho
67 | rhocore = self.coreMaterial.rho
68 | tshearmin = self.shearMaterial.tmin
69 |
70 | return [I/mfac <= 2*w*t*(hin/2)**2,
71 | dm >= (rho*(2*w*t) + 2*tshear*rhoshear*(hin + 2*t)
72 | + rhocore*w*hin)*b/2*deta,
73 | W >= 2*dm.sum()*g,
74 | w <= wlim*cave,
75 | cave*tau >= hin + 2*t,
76 | Sy*(hin/2 + t) <= I,
77 | tshear >= tshearmin
78 | ]
79 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/constant_taper_chord.py:
--------------------------------------------------------------------------------
1 | " constant taper chord "
2 | import numpy as np
3 |
4 | def c_bar(lam, N):
5 | "returns wing chord lengths for constant taper wing"
6 | eta = np.linspace(0, 1, N)
7 | c = 2/(1+lam)*(1+(lam-1)*eta)
8 | cbarmac = 2./3*(1+lam+lam**2)/(1+lam)
9 | deta = np.diff(eta)
10 | return c, eta, deta, cbarmac
11 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/gustloaddiagram.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/wing/gustloaddiagram.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/gustloading.py:
--------------------------------------------------------------------------------
1 | " spar loading for gust case "
2 | import os
3 | from numpy import pi, hstack, array
4 | from ad.admath import cos
5 | import pandas as pd
6 | from gpkit import parse_variables
7 | from gpfit.fit_constraintset import FitCS
8 | from .sparloading import SparLoading
9 |
10 | #pylint: disable=invalid-name, no-member, arguments-differ, exec-used
11 | #pylint: disable=attribute-defined-outside-init, undefined-variable
12 |
13 | class GustL(SparLoading):
14 | """ Gust Loading Model
15 |
16 | Variables
17 | ---------
18 | vgust 10 [m/s] gust velocity
19 | Ww [lbf] wing weight
20 | v [m/s] vehicle speed
21 | cl [-] wing lift coefficient
22 |
23 | Variables of length wing.N
24 | --------------------------
25 | agust [-] gust angle of attack
26 | cosminus1 self.return_cosm1 [-] 1 minus cosine factor
27 |
28 | LaTex Strings
29 | -------------
30 | vgust V_{\\mathrm{gust}}
31 | Ww W_{\\mathrm{w}}
32 | cl c_l
33 | agust \\alpha_{\\mathrm{gust}}
34 | cosminus1 (cos(x)-1)
35 |
36 | """
37 | new_qbarFun = None
38 | new_SbarFun = None
39 |
40 | def return_cosm1(self, c):
41 | eta = c(self.wing.planform.eta).to("dimensionless").magnitude
42 | return hstack([1e-10, 1-array(cos(eta[1:]*pi/2))])
43 |
44 | @parse_variables(__doc__, globals())
45 | def setup(self, wing, state, out=False):
46 | self.load = SparLoading.setup(self, wing, state, out=out)
47 |
48 | cbar = self.wing.planform.cbar
49 | W = self.W # from SparLoading
50 | q = self.q
51 | N = self.N
52 | b = self.b
53 |
54 | path = os.path.dirname(os.path.abspath(__file__))
55 | df = pd.read_csv(path + os.sep + "arctan_fit.csv").to_dict(
56 | orient="records")[0]
57 |
58 | constraints = [
59 | # fit for arctan from 0 to 1, RMS = 0.044
60 | FitCS(df, agust, [cosminus1*vgust/v]),
61 | q >= W*N/b*cbar*(1 + 2*pi*agust/cl*(1+Ww/W)),
62 | ]
63 |
64 | return self.load, constraints
65 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/jho1polarfit1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/aircraft/wing/jho1polarfit1.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/jho1polars/genpolar.sh:
--------------------------------------------------------------------------------
1 | AIRFOIL=$1
2 | POLARFILE=$1.ncrit09.Re$2k.pol
3 |
4 | if [ -f $POLARFILE ] ; then
5 | echo "yes"
6 | rm $POLARFILE
7 | fi
8 |
9 | xfoil << EOF
10 | load $1.dat
11 | oper
12 | v $2e3
13 | pacc
14 | $POLARFILE
15 |
16 | iter 200
17 | cseq 0.2 1.2 .05
18 |
19 | quit
20 | EOF
21 |
22 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/jho1polars/jho1_polarfits.py:
--------------------------------------------------------------------------------
1 | "jho1_polarfits.py"
2 | from builtins import zip
3 | from builtins import range
4 | import numpy as np
5 | import pandas as pd
6 | import matplotlib.pyplot as plt
7 | plt.rcParams.update({'font.size':15})
8 |
9 | def text_to_df(filename):
10 | "parse XFOIL polars and concatente data in DataFrame"
11 | lines = list(open(filename))
12 | for i, l in enumerate(lines):
13 | lines[i] = l.split("\n")[0]
14 | for j in 10-np.arange(9):
15 | if " "*j in lines[i]:
16 | lines[i] = lines[i].replace(" "*j, " ")
17 | if "---" in lines[i]:
18 | start = i
19 | data = {}
20 | titles = lines[start-1].split(" ")[1:]
21 | for t in titles:
22 | data[t] = []
23 |
24 | for l in lines[start+1:]:
25 | for i, v in enumerate(l.split(" ")[1:]):
26 | data[titles[i]].append(v)
27 |
28 | df = pd.DataFrame(data)
29 | return df
30 |
31 | def fit_setup(Re_range):
32 | "set up x and y parameters for gp fitting"
33 | CL = []
34 | CD = []
35 | RE = []
36 | for r in Re_range:
37 | dataf = text_to_df("jho1.ncrit09.Re%dk.pol" % r)
38 | CL.append(dataf["CL"])
39 | CD.append(dataf["CD"])
40 | RE.append([r*1000.0]*len(dataf["CL"]))
41 |
42 | u1 = np.hstack(CL)
43 | u2 = np.hstack(RE)
44 | w = np.hstack(CD)
45 | u1 = u1.astype(np.float)
46 | u2 = u2.astype(np.float)
47 | w = w.astype(np.float)
48 | u = [u1, u2]
49 | x = np.log(u)
50 | y = np.log(w)
51 | return x, y
52 |
53 | def return_fit(cl, re):
54 | "polar fit for the JHO1 airfoil"
55 | cd = (0.0247*cl**2.49*re**-1.11 + 2.03e-7*cl**12.7*re**-0.338 +
56 | 6.35e10*cl**-0.243*re**-3.43 + 6.49e-6*cl**-1.9*re**-0.681)**(1/3.72)
57 | # cd = (0.0247*cl**2.5*re**-1.1 + 2.03e-7*cl**13*re**-0.34 +
58 | # 6.35e10*cl**-0.24*re**-3.4 + 6.49e-6*cl**-1.9*re**-0.68)**(1/3.7)
59 | # SMA function, K=3, max RMS error = 0.00489
60 | return cd
61 |
62 | def plot_fits(re):
63 | "plot fit compared to data"
64 | colors = ["k", "m", "b", "g", "y"]
65 | assert len(re) == len(colors)
66 | fig, ax = plt.subplots()
67 | fig1, ax1 = plt.subplots()
68 | cls = np.linspace(0.2, 1.3, 20)
69 | for r, col in zip(re, colors):
70 | dataf = text_to_df("jho1.ncrit09.Re%dk.pol" % r)
71 | ax.plot(dataf["CL"], dataf["CD"], "o", mec=col, mfc="none", mew=1.5)
72 | cd = return_fit(cls, r*1000.)
73 | ax.plot(cls, cd, c=col, label="Re = %dk" % r, lw=2)
74 | ax1.plot(cls, cls**1.5/cd)
75 | ax.legend(loc=2, fontsize=15)
76 | ax.set_xlabel("$C_L$")
77 | ax.set_ylabel("$c_{d_p}$")
78 | ax.grid()
79 | return fig, ax, fig1
80 |
81 | if __name__ == "__main__":
82 | Re = list(range(200, 750, 50))
83 | X, Y = fit_setup(Re) # call fit(X, Y, 4, "SMA") to get fit
84 | F, A, F1 = plot_fits([300, 350, 400, 450, 500])
85 | F.savefig("jho1polarfit1.pdf", bbox_inches="tight")
86 | F1.savefig("jho1cl32cd.pdf", bbox_inches="tight")
87 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/jho1polars/jho1polarsweep.sh:
--------------------------------------------------------------------------------
1 | AIRFOIL=jho1
2 | Re="200 250 300 350 400 450 500 550 600 650 700"
3 | for r in $Re
4 | do
5 | ./genpolar.sh $AIRFOIL $r
6 | done
7 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/jho_fitdata.csv:
--------------------------------------------------------------------------------
1 | ub0,ub1,rms_err,e31,e30,e11,e10,ftype,lb1,lb0,K,a1,c3,c2,c1,c0,e21,e20,d,e00,e01,max_err
2 | 1.3,700000.00000000023,0.0065776630393203682,-2.7130075007872931,-0.17866429593832706,-0.66747689316035896,2.9630718142366268,SMA,149999.99999999994,0.20000000000000001,4,3.0904265782626554,175843713.87100798,2.2831557182654378e-06,0.0016819467416519377,1.0237722536072613e-07,-0.34964098451136472,-1.6533259069045529,2,18.8561449416522,-0.18331956404932631,0.018001147362385339
3 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/sparloading.py:
--------------------------------------------------------------------------------
1 | " spar loading "
2 | from gpkit import Model, parse_variables
3 | from numpy import pi
4 |
5 | #pylint: disable=no-member, unused-argument, exec-used, invalid-name
6 | #pylint: disable=undefined-variable, attribute-defined-outside-init
7 |
8 | class SparLoading(Model):
9 | """ Spar Loading Model
10 |
11 | Variables
12 | ---------
13 | Nmax 5 [-] max loading
14 | Nsafety 1.0 [-] safety load factor
15 | kappa 0.2 [-] max tip deflection ratio
16 | W [lbf] loading weight
17 | N [-] loading factor
18 | twmax 15.*pi/180 [-] max tip twist
19 | Stip 1e-10 [N] tip loading
20 | Mtip 1e-10 [N*m] tip moment
21 | throot 1e-10 [-] root deflection angle
22 | wroot 1e-10 [m] root deflection
23 |
24 | Variables of length wing.N
25 | --------------------------
26 | q [N/m] distributed wing loading
27 | S [N] shear along wing
28 | M [N*m] wing section root moment
29 | th [-] deflection angle
30 | w [m] wing deflection
31 |
32 | Variables of length wing.N-1
33 | ----------------------------
34 | Mtw [N*m] local moment due to twisting
35 | theta [-] twist deflection
36 | EIbar [-] EIbar
37 | Sout [-] outboard variable
38 |
39 | LaTex Strings
40 | -------------
41 | Nmax N_{\\mathrm{max}}
42 | kappa \\kappa
43 | Mr M_r
44 |
45 | """
46 | def new_qbarFun(self, c):
47 | " define qbar model for chord loading "
48 | barc = self.wing.planform.cbar
49 | return [f(c) for f in self.wing.substitutions[barc]]
50 |
51 | new_SbarFun = None
52 |
53 | @parse_variables(__doc__, globals())
54 | def setup(self, wing, state, out=False):
55 | self.wing = wing
56 |
57 | b = self.b = self.wing.planform.b
58 | I = self.I = self.wing.spar.I
59 | Sy = self.Sy = self.wing.spar.Sy
60 | cave = self.cave = self.wing.planform.cave
61 | cbar = self.cbar = self.wing.planform.cbar
62 | E = self.wing.spar.material.E
63 | sigma = self.wing.spar.material.sigma
64 | deta = self.wing.planform.deta
65 |
66 | constraints = []
67 | if not out:
68 | constraints.extend([
69 | S[:-1] >= S[1:] + 0.5*deta*(b/2.)*(q[:-1] + q[1:]),
70 | M[:-1] >= M[1:] + 0.5*deta*(b/2)*(S[:-1] + S[1:])])
71 |
72 | constraints.extend([
73 | N == Nsafety*Nmax, q >= N*W/b*cbar,
74 | S[-1] >= Stip, M[-1] >= Mtip, th[0] >= throot,
75 | th[1:] >= th[:-1] + 0.5*deta*(b/2)*(M[1:] + M[:-1])/E/I,
76 | w[0] >= wroot, w[1:] >= w[:-1] + 0.5*deta*(b/2)*(th[1:] + th[:-1]),
77 | sigma >= M[:-1]/Sy, w[-1]/(b/2) <= kappa,
78 | ])
79 |
80 | self.wingSparJ = hasattr(self.wing.spar, "J")
81 |
82 | if self.wingSparJ:
83 | qne = self.qne = state.qne
84 | J = self.J = self.wing.spar.J
85 | G = self.wing.spar.shearMaterial.G
86 | cm = self.wing.planform.CM
87 | constraints.extend([
88 | Mtw >= cm*cave**2*qne*deta*b/2*Nsafety,
89 | theta[0] >= Mtw[0]/G/J[0]*deta[0]*b/2,
90 | theta[1:] >= theta[:-1] + Mtw[1:]/G/J[1:]*deta[1:]*b/2,
91 | twmax >= theta[-1]
92 | ])
93 | return constraints
94 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/tube_spar.py:
--------------------------------------------------------------------------------
1 | " tube spar for wing "
2 | from numpy import pi
3 | from gpkit import Variable, Model, Vectorize
4 | from chord_spar_loading import ChordSparL
5 |
6 | class TubeSpar(Model):
7 | " tube spar model "
8 | def setup(self, b, cave, tau, N=5):
9 | self.N = N
10 |
11 | rho_cfrp = Variable("\\rho_{CFRP}", 1.6, "g/cm^3", "density of CFRP")
12 | E = Variable("E", 2e7, "psi", "Youngs modulus of CF")
13 |
14 | with Vectorize(self.N-1):
15 | d = Variable("d", "in", "spar diameter")
16 | I = Variable("I", "m^4", "spar x moment of inertia")
17 | Sy = Variable("S_y", "m**3", "section modulous")
18 | A = Variable("A", "in**2", "spar cross sectional area")
19 | dm = Variable("dm", "kg", "segment spar mass")
20 |
21 | W = Variable("W", "lbf", "tube spar weight")
22 | g = Variable("g", 9.81, "m/s**2", "gravitational constant")
23 |
24 | constraints = [
25 | dm >= rho_cfrp*A*b/(N-1),
26 | W >= 2*dm.sum()*g,
27 | cave*tau >= d,
28 | 4*I**2/A**2/(d/2)**2 + A/pi <= (d/2)**2,
29 | Sy*(d/2) <= I,
30 | E == E
31 | ]
32 |
33 | return constraints
34 |
35 | def loading(self, Wcent):
36 | return ChordSparL(self, Wcent)
37 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/wing.py:
--------------------------------------------------------------------------------
1 | " wing.py "
2 | from builtins import range
3 | from os import sep
4 | from os.path import abspath, dirname
5 | import numpy as np
6 | import pandas as pd
7 | from gpkit import Model, parse_variables
8 | from .wing_core import WingCore
9 | from .wing_skin import WingSkin
10 | from .capspar import CapSpar
11 | from gpfit.fit_constraintset import XfoilFit
12 |
13 | #pylint: disable=no-member, invalid-name, unused-argument, exec-used
14 | #pylint: disable=undefined-variable, attribute-defined-outside-init
15 | #pylint: disable=too-many-instance-attributes
16 |
17 | class Planform(Model):
18 | """ Planform Area Definition
19 |
20 | Scalar Variables
21 | ---------
22 | S [ft^2] surface area
23 | AR [-] aspect ratio
24 | b [ft] span
25 | tau [-] airfoil thickness ratio
26 | CLmax 1.39 [-] maximum lift coefficient
27 | CM 0.14 [-] wing moment coefficient
28 | croot [ft] root chord
29 | cmac [ft] mean aerodynamic chord
30 | lam 0.5 [-] taper ratio
31 | cbarmac self.return_cmac [-] non-dim MAC
32 |
33 | Variables of length N
34 | ---------------------
35 | eta np.linspace(0,1,N) [-] (2y/b)
36 | cbar self.return_c [-] non-dim chord at nodes
37 |
38 | Variables of length N-1
39 | -----------------------
40 | cave [ft] mid section chord
41 | cbave self.return_avg [-] non-dim mid section chord
42 | deta self.return_deta [-] \\Delta (2y/b)
43 |
44 | Upper Unbounded
45 | --------------- # bounding any pair of variables will work
46 | cave, b, tau
47 |
48 | Lower Unbounded
49 | ---------------
50 | cave, b, tau
51 |
52 | LaTex Strings
53 | -------------
54 | tau \\tau
55 | CLmax C_{L_{\\mathrm{max}}}
56 | CM C_M
57 | croot c_{\\mathrm{root}}
58 | cmac c_{\\mathrm{MAC}}
59 | lam \\lambda
60 | cbarmac \\bar{c}_{\\mathrm{MAC}}
61 |
62 | """
63 | def return_c(self, c):
64 | " return normalized chord distribution "
65 | lam = c(self.lam).to("dimensionless").magnitude
66 | eta = c(self.eta).to("dimensionless").magnitude
67 | return np.array([2./(1+lam)*(1+(lam-1)*e) for e in eta])
68 |
69 | def return_cmac(self, c):
70 | " return normalized MAC "
71 | cbar = self.return_c(c)
72 | lam = cbar[1:]/cbar[:-1]
73 | maci = 2./3*cbar[:-1]*(1 + lam + lam**2)/(1 + lam)
74 | deta = np.diff(c(self.eta))
75 | num = sum([(cbar[i] + cbar[i+1])/2*maci[i]*deta[i] for i
76 | in range(len(deta))])
77 | den = sum([(cbar[i] + cbar[i+1])/2*deta[i] for i in range(len(deta))])
78 | return num/den/cbar[0]
79 |
80 | return_avg = lambda self, c: (self.return_c(c)[:-1]
81 | + self.return_c(c)[1:])/2.
82 | return_deta = lambda self, c: np.diff(c(self.eta))
83 |
84 | @parse_variables(__doc__, globals())
85 | def setup(self, N):
86 | return [b**2 == S*AR,
87 | cave == cbave*S/b,
88 | croot == S/b*cbar[0],
89 | cmac == croot*cbarmac]
90 |
91 | class WingAero(Model):
92 | """ Wing Aero Model
93 |
94 | Variables
95 | ---------
96 | Cd [-] wing drag coefficient
97 | CL [-] lift coefficient
98 | CLstall 1.3 [-] stall CL
99 | e 0.9 [-] span efficiency
100 | Re [-] reynolds number
101 | cdp [-] wing profile drag coefficient
102 |
103 | Upper Unbounded
104 | ---------------
105 | Cd, Re, static.planform.AR
106 | state.V, state.mu (if not muValue), state.rho (if not rhoValue)
107 |
108 | Lower Unbounded
109 | ---------------
110 | state.V, state.mu (if not muValue), state.rho (if not rhoValue)
111 |
112 | LaTex Strings
113 | -------------
114 | Cd C_d
115 | CL C_L
116 | CLstall C_{L_{\\mathrm{stall}}}
117 | cdp c_{d_p}
118 |
119 | """
120 | @parse_variables(__doc__, globals())
121 | def setup(self, static, state,
122 | fitdata=dirname(abspath(__file__)) + sep + "jho_fitdata.csv"):
123 | self.state = state
124 | self.static = static
125 |
126 | df = pd.read_csv(fitdata)
127 | fd = df.to_dict(orient="records")[0]
128 |
129 | AR = static.planform.AR
130 | cmac = static.planform.cmac
131 | rho = state.rho
132 | V = state.V
133 | mu = state.mu
134 | # needed for Climb model in solar
135 | self.rhoValue = bool(rho.key.value)
136 | self.muValue = bool(mu.key.value)
137 |
138 | if fd["d"] == 2:
139 | independentvars = [CL, Re]
140 | elif fd["d"] == 3:
141 | independentvars = [CL, Re, static.planform.tau]
142 |
143 | return [Cd >= cdp + CL**2/np.pi/AR/e,
144 | Re == rho*V*cmac/mu,
145 | # XfoilFit(fd, cdp, [CL, Re], airfoil="jho1.dat"),
146 | XfoilFit(fd, cdp, independentvars, name="polar"),
147 | CL <= CLstall
148 | ]
149 |
150 | class Wing(Model):
151 | """
152 | Wing Model
153 |
154 | Variables
155 | ---------
156 | W [lbf] wing weight
157 | mfac 1.2 [-] wing weight margin factor
158 |
159 | SKIP VERIFICATION
160 |
161 | Upper Unbounded
162 | ---------------
163 | W, planform.tau (if not sparJ)
164 |
165 | Lower Unbounded
166 | ---------------
167 | planform.b, spar.Sy (if sparModel), spar.J (if sparJ)
168 |
169 | LaTex Strings
170 | -------------
171 | mfac m_{\\mathrm{fac}}
172 |
173 | """
174 |
175 | sparModel = CapSpar
176 | fillModel = WingCore
177 | flight_model = WingAero
178 | skinModel = WingSkin
179 | sparJ = False
180 |
181 | @parse_variables(__doc__, globals())
182 | def setup(self, N=5):
183 | self.N = N
184 | self.planform = Planform(N)
185 | self.components = []
186 |
187 | if self.skinModel:
188 | self.skin = self.skinModel(self.planform)
189 | self.components.extend([self.skin])
190 | if self.sparModel:
191 | self.spar = self.sparModel(N, self.planform)
192 | self.components.extend([self.spar])
193 | self.sparJ = hasattr(self.spar, "J")
194 | if self.fillModel:
195 | self.foam = self.fillModel(self.planform)
196 | self.components.extend([self.foam])
197 |
198 | constraints = [W/mfac >= sum(c["W"] for c in self.components)]
199 |
200 | return constraints, self.planform, self.components
201 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/wing_core.py:
--------------------------------------------------------------------------------
1 | " wing interior "
2 | from gpkit import Model, parse_variables
3 | from gpkitmodels.GP.materials import foamhd
4 | from gpkitmodels import g
5 |
6 | #pylint: disable=exec-used, no-member, undefined-variable
7 |
8 | class WingCore(Model):
9 | """ Wing Core Model
10 |
11 | Variables
12 | ---------
13 | W [lbf] wing core weight
14 | Abar 0.0753449 [-] normalized cross section area
15 |
16 | Upper Unbounded
17 | ---------------
18 | W
19 |
20 | Lower Unbounded
21 | ---------------
22 | cave, b, surface.deta
23 |
24 | LaTex Strings
25 | -------------
26 | rhocore \\rho_{\\mathrm{core}}
27 | Abar \\bar{A}
28 |
29 | """
30 | material = foamhd
31 |
32 | @parse_variables(__doc__, globals())
33 | def setup(self, surface):
34 | self.surface = surface
35 |
36 | cave = self.cave = surface.cave
37 | b = self.b = surface.b
38 | deta = surface.deta
39 | rho = self.material.rho
40 |
41 | return [W >= 2*(g*rho*Abar*cave**2*b/2*deta).sum()]
42 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/wing_skin.py:
--------------------------------------------------------------------------------
1 | " wing skin "
2 | from gpkit import Model, parse_variables
3 | from gpkitmodels.GP.materials import cfrpfabric
4 | from gpkitmodels import g
5 |
6 | class WingSkin(Model):
7 | """ Wing Skin model
8 |
9 | Variables
10 | ---------
11 | W [lbf] wing skin weight
12 | t [in] wing skin thickness
13 | Jtbar 0.01114 [1/mm] torsional moment of inertia
14 | Cmw 0.121 [-] negative wing moment coeff
15 | rhosl 1.225 [kg/m^3] sea level air density
16 | Vne 45 [m/s] never exceed vehicle speed
17 |
18 | Upper Unbounded
19 | ---------------
20 | W, surface.croot
21 |
22 | Lower Unbounded
23 | ---------------
24 | surface.S
25 |
26 | LaTex Strings
27 | -------------
28 | W W_{\\mathrm{skin}}
29 | t t_{\\mathrm{skin}}
30 | Jtbar \\bar{J/t}
31 | Cmw C_{m_w}
32 | rhosl \\rho_{\\mathrm{SL}}
33 | Vne V_{\\mathrm{NE}}
34 |
35 | """
36 | material = cfrpfabric
37 |
38 | @parse_variables(__doc__, globals())
39 | def setup(self, surface):
40 | self.surface = surface
41 |
42 | croot = surface.croot
43 | S = surface.S
44 | rho = self.material.rho
45 | tau = self.material.tau
46 | tmin = self.material.tmin
47 |
48 | return [W >= rho*S*2*t*g,
49 | t >= tmin,
50 | tau >= 1/Jtbar/croot**2/t*Cmw*S*rhosl*Vne**2]
51 |
52 | class WingSecondStruct(Model):
53 | """ Wing Skin model
54 |
55 | Variables
56 | ---------
57 | W [lbf] wing skin weight
58 | rhoA 0.35 [kg/m^2] total aerial density
59 |
60 | Upper Unbounded
61 | ---------------
62 | W
63 |
64 | Lower Unbounded
65 | ---------------
66 | S
67 |
68 | LaTex Strings
69 | -------------
70 | W W_{\\mathrm{skin}}
71 | rhoA \\rho_{A}
72 |
73 | """
74 | @parse_variables(__doc__, globals())
75 | def setup(self, surface):
76 | S = self.S = surface.S
77 |
78 | return [W >= rhoA*S*g]
79 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/aircraft/wing/wing_test.py:
--------------------------------------------------------------------------------
1 | " wing test "
2 | from gpkitmodels.GP.aircraft.wing.wing import Wing
3 | from gpkitmodels.GP.aircraft.wing.wing_skin import WingSkin
4 | from gpkitmodels.GP.aircraft.wing.wing_core import WingCore
5 | from gpkitmodels.GP.aircraft.wing.boxspar import BoxSpar
6 | from gpkit import Model, parse_variables
7 |
8 | #pylint: disable=no-member, exec-used
9 |
10 | class FlightState(Model):
11 | """ Flight State
12 |
13 | Variables
14 | ---------
15 | V 50 [m/s] airspeed
16 | rho 1.255 [kg/m^3] air density
17 | mu 1.5e-5 [N*s/m**2] air viscosity
18 | qne [kg/s^2/m] never exceed dynamic pressure
19 |
20 | """
21 | @parse_variables(__doc__, globals())
22 | def setup(self):
23 | return [qne == V**2*rho*1.2]
24 |
25 | def wing_test():
26 | " test wing models "
27 |
28 | W = Wing()
29 | W.substitutions[W.W] = 50
30 | W.substitutions[W.planform.tau] = 0.115
31 | fs = FlightState()
32 | perf = W.flight_model(W, fs)
33 | loading = [W.spar.loading(W, fs)]
34 | loading[0].substitutions["W"] = 100
35 | loading.append(W.spar.gustloading(W, fs))
36 | loading[1].substitutions["W"] = 100
37 |
38 | from gpkit import settings
39 | if settings["default_solver"] == "cvxopt":
40 | for l in loading:
41 | for v in ["Mtip", "Stip", "wroot", "throot"]:
42 | l.substitutions[v] = 1e-1
43 |
44 | m = Model(perf.Cd, [
45 | loading[1].v == fs.V,
46 | loading[1].cl == perf.CL,
47 | loading[1].Ww == W.W,
48 | loading[1].Ww <= 0.5*fs.rho*fs.V**2*perf.CL*W.planform.S,
49 | W, fs, perf, loading])
50 | m.solve(verbosity=0)
51 |
52 | def box_spar():
53 | " test wing models "
54 |
55 | Wing.sparModel = BoxSpar
56 | W = Wing()
57 | W.substitutions[W.W] = 50
58 | W.substitutions[W.planform.tau] = 0.115
59 | fs = FlightState()
60 | perf = W.flight_model(W, fs)
61 | loading = [W.spar.loading(W, fs)]
62 | loading[0].substitutions["W"] = 100
63 | loading.append(W.spar.gustloading(W, fs))
64 | loading[1].substitutions["W"] = 100
65 |
66 | from gpkit import settings
67 | if settings["default_solver"] == "cvxopt":
68 | for l in loading:
69 | for v in ["Mtip", "Stip", "wroot", "throot"]:
70 | l.substitutions[v] = 1e-2
71 |
72 | m = Model(perf.Cd, [
73 | loading[1].v == fs.V,
74 | loading[1].cl == perf.CL,
75 | loading[1].Ww == W.W,
76 | loading[1].Ww <= fs.qne*perf.CL*W.planform.S,
77 | W, fs, perf, loading])
78 | m.solve(verbosity=0)
79 |
80 | def test():
81 | " tests "
82 | wing_test()
83 | box_spar()
84 |
85 | if __name__ == "__main__":
86 | test()
87 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/beam/README.md:
--------------------------------------------------------------------------------
1 | # Beam
2 |
3 | This model is valid for a discritized beam and calculates non-dimensional sheer stresses, moments, angles and deflections for a given distributed load. Assumes segment lengths are equal along the beam.
4 |
5 | Required inputs are a non-dimensional distributed load that is a vector variable and the number of nodes
6 |
7 | \begin{table}[]
8 | \centering
9 | \caption{My caption}
10 | \label{my-label}
11 | \begin{tabular}{lll}
12 | Input & Description & Type \\
13 | qbar & Distributed load with N values & VectorVariable \\
14 | N & Number of nodes & int
15 | \end{tabular}
16 | \end{table}
17 |
18 | The non-dimensional equation derivation is shown below for wing bending application.
19 |
20 | Using a standard Bernoulli-Euler discretized beam model with $n$ nodes, the shear forces and moments can be computed from the distributed loads $q(y)$, with boundary conditions of zero shear forces and moments at the wing tips.
21 |
22 | \begin{align}
23 | \label{e:shear}
24 | \mathcal{S}_i &= \mathcal{S}_{i+1} - \frac{q_{i+1} + q_i}{2}\Delta y \\
25 | \label{e:moment}
26 | M_i &= M_{i+1} - \frac{\mathcal{S}_{i+1} + \mathcal{S}_i}{2}\Delta y \\
27 | \label{e:shearboundary}
28 | \mathcal{S}_n &= 0 \\
29 | \label{e:momentboundary}
30 | M_n &= 0
31 | \end{align}
32 |
33 | Similarly, the angle deflection and deflection can be calculated with boundary conditions of zero angle and deflection and the wing root.
34 |
35 | \begin{align}
36 | \label{e:angle}
37 | \Theta_{i} &= \Theta_{i+1} + \frac{1}{2} \left(\frac{M_i}{EI_i} + \frac{M_{i-1}}{EI_{i-1}} \right) \Delta y \\
38 | \label{e:deflection}
39 | w_{i} &= w_{i+1} + \frac{1}{2} (\Theta_i + \Theta_{i-1}) \Delta y \\
40 | \label{e:angleboundary}
41 | \Theta_0 &= 0 \\
42 | \label{e:defboundary}
43 | w_0 &= 0
44 | \end{align}
45 |
46 | Equations~\eqref{e:shear}-\eqref{e:defboundary} are GP-compatible if expressed as
47 |
48 | \begin{align}
49 | \label{e:sheargp}
50 | \mathcal{S}_{i+1} &\geq \mathcal{S}_i + \frac{q_{i+1} + q_i}{2} \Delta y \\
51 | \label{e:momentgp}
52 | \mathcal{M}_{i+1} &\geq \mathcal{M}_i + \frac{\mathcal{S}_{i+1} + \mathcal{S}_i}{2} \Delta y \\
53 | \label{e:anglegp}
54 | \Theta_{i} &\geq \Theta_{i+1} + \frac{1}{2} \left(\frac{\mathcal{M}_i}{EI_i} + \frac{\mathcal{M}_{i-1}}{EI_{i-1}} \right) \Delta y \\
55 | \label{e:deflection}
56 | w_{i} &\geq w_{i+1} + \frac{1}{2} (\Theta_i + \Theta_{i-1}) \Delta y
57 | \end{align}
58 |
59 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/beam/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/beam/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/GP/beam/beam.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/GP/beam/beam.pdf
--------------------------------------------------------------------------------
/gpkitmodels/GP/beam/beam.py:
--------------------------------------------------------------------------------
1 | " discretized beam model "
2 | from gpkit import Model, Variable, Vectorize
3 |
4 | #pylint: disable=invalid-name
5 |
6 | class Beam(Model):
7 | """discretized beam bending model
8 |
9 | Upper Unbounded
10 | ---------------
11 | EIbar, dbar_tip
12 |
13 | Lower Unbounded
14 | ---------------
15 | dx, qbar (if not qbarFun)
16 |
17 | """
18 | qbarFun = None
19 | SbarFun = None
20 | MbarFun = None
21 |
22 | def setup(self, N):
23 |
24 | with Vectorize(N-1):
25 | EIbar = self.EIbar = Variable("\\bar{EI}", "-",
26 | "normalized YM and moment of inertia")
27 | dx = self.dx = Variable("dx", "-", "normalized length of element")
28 |
29 | with Vectorize(N):
30 | Sbar = Variable("\\bar{S}", self.SbarFun, "-", "normalized shear")
31 | Mbar = Variable("\\bar{M}", self.MbarFun, "-", "normalized moment")
32 | th = Variable("\\theta", "-", "deflection slope")
33 | dbar = Variable("\\bar{\\delta}", "-", "normalized displacement")
34 | self.dbar_tip = dbar[-1]
35 |
36 |
37 | throot = Variable("\\theta_{root}", 1e-10, "-", "Base angle")
38 | dbarroot = Variable("\\bar{\\delta}_{root}", 1e-10, "-",
39 | "Base deflection")
40 |
41 | constraints = []
42 | if self.SbarFun is None:
43 | with Vectorize(N):
44 | qbar = self.qbar = Variable("\\bar{q}", self.qbarFun, "-",
45 | "normalized loading")
46 | Sbartip = Variable("\\bar{S}_{tip}", 1e-10, "-", "Tip loading")
47 | constraints.extend([
48 | Sbar[:-1] >= Sbar[1:] + 0.5*dx*(qbar[:-1] + qbar[1:]),
49 | Sbar[-1] >= Sbartip])
50 |
51 | if self.MbarFun is None:
52 | Mbartip = Variable("\\bar{M}_{tip}", 1e-10, "-", "Tip moment")
53 | constraints.extend([
54 | Mbar[:-1] >= Mbar[1:] + 0.5*dx*(Sbar[:-1] + Sbar[1:]),
55 | Mbar[-1] >= Mbartip])
56 |
57 | constraints.extend([
58 | th[0] >= throot,
59 | th[1:] >= th[:-1] + 0.5*dx*(Mbar[1:] + Mbar[:-1])/EIbar,
60 | dbar[0] >= dbarroot,
61 | dbar[1:] >= dbar[:-1] + 0.5*dx*(th[1:] + th[:-1]),
62 | ])
63 |
64 | return constraints
65 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/materials/__init__.py:
--------------------------------------------------------------------------------
1 | from .composite import CFRPFabric, CFRPUD, Kevlar
2 | from .foam import FoamHD, FoamLD
3 |
4 | cfrpfabric = CFRPFabric()
5 | cfrpud = CFRPUD()
6 | foamhd = FoamHD()
7 | foamld = FoamLD()
8 | kevlar = Kevlar()
9 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/materials/composite.py:
--------------------------------------------------------------------------------
1 | from gpkit import Model, parse_variables
2 |
3 | class CFRPFabric(Model):
4 | """ Carbon Fiber Reinforced Plastic Fabric Material Properties
5 |
6 | Constants
7 | ---------
8 | rho 1.6 [g/cm^3] density of CFRP
9 | tmin 0.3048 [mm] minimum gauge thickness
10 | tau 570 [MPa] torsional stress limit
11 | E 150 [GPa] Youngs modulus
12 | sigma 400 [MPa] max stress
13 | G 2 [GPa] shear modulus
14 |
15 | LaTex Strings
16 | -------------
17 | rho \\rho_{\\mathrm{CFRP}}
18 | tmin t_{\\mathrm{min-CFRP}}
19 | tau \\tau_{\\mathrm{CFRP}}
20 |
21 | """
22 | @parse_variables(__doc__, globals())
23 | def setup(self):
24 | pass
25 |
26 | class CFRPUD(Model):
27 | """ Carbon Fiber Reinforced Plastic Unidirectional Material Properties
28 |
29 | Constants
30 | ---------
31 | rho 1.6 [g/cm^3] density of CFRP
32 | E 137 [GPa] Youngs Modulus of CFRP
33 | sigma 1700 [MPa] maximum stress limit of CFRP
34 | tmin 0.1 [mm] minimum gague thickness
35 |
36 | LaTex Strings
37 | -------------
38 | rho \\rho_{\\mathrm{CFRP}}
39 | tmin t_{\\mathrm{min-CFRP}}
40 | sigma \\sigma_{\\mathrm{CFRP}}
41 |
42 | """
43 | @parse_variables(__doc__, globals())
44 | def setup(self):
45 | pass
46 |
47 | class Kevlar(Model):
48 | """ Kevlar Material Properties
49 |
50 | Constants
51 | ---------
52 | rho 0.049 [g/cm^3] density of Kevlar
53 | tmin 0.012 [in] minimum gauge thickness
54 | tau 200 [MPa] torsional stress limit
55 |
56 | LaTex Strings
57 | -------------
58 | rho \\rho_{\\mathrm{Kevlar}}
59 | tmin t_{\\mathrm{min-Kevlar}}
60 | tau \\tau_{\\mathrm{Kevlar}}
61 |
62 | """
63 | @parse_variables(__doc__, globals())
64 | def setup(self):
65 | pass
66 |
--------------------------------------------------------------------------------
/gpkitmodels/GP/materials/foam.py:
--------------------------------------------------------------------------------
1 | from gpkit import Model, parse_variables
2 |
3 | class FoamHD(Model):
4 | """ Foam high density material properties
5 |
6 | Constants
7 | ---------
8 | rho 0.036 [g/cm^3] foam density
9 |
10 | LaTex Strings
11 | -------------
12 | rho \\rho_{\\mathrm{foam}}
13 |
14 | """
15 | @parse_variables(__doc__, globals())
16 | def setup(self):
17 | pass
18 |
19 | class FoamLD(Model):
20 | """ Foam low density material properties
21 |
22 | Constants
23 | ---------
24 | rho 0.024 [g/cm^3] foam density
25 |
26 | LaTex Strings
27 | -------------
28 | rho \\rho_{\\mathrm{foam}}
29 |
30 | """
31 | @parse_variables(__doc__, globals())
32 | def setup(self):
33 | pass
34 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/SimPleAC/SimPleAC.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from gpkit import Model, Variable, SignomialsEnabled, VarKey, units
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 |
6 | class SimPleAC(Model):
7 | def setup(self):
8 | # Env. constants
9 | g = Variable("g", 9.81, "m/s^2", "gravitational acceleration")
10 | mu = Variable("\\mu", 1.775e-5, "kg/m/s", "viscosity of air", pr=4.)
11 | rho = Variable("\\rho", 1.23, "kg/m^3", "density of air", pr=5.)
12 | rho_f = Variable("\\rho_f", 817, "kg/m^3", "density of fuel")
13 |
14 | # Non-dimensional constants
15 | C_Lmax = Variable("C_{L,max}", 1.6, "-", "max CL with flaps down", pr=5.)
16 | e = Variable("e", 0.92, "-", "Oswald efficiency factor", pr=3.)
17 | k = Variable("k", 1.17, "-", "form factor", pr=10.)
18 | N_ult = Variable("N_{ult}", 3.3, "-", "ultimate load factor", pr=15.)
19 | S_wetratio = Variable("(\\frac{S}{S_{wet}})", 2.075, "-", "wetted area ratio", pr=3.)
20 | tau = Variable("\\tau", 0.12, "-", "airfoil thickness to chord ratio", pr=10.)
21 | W_W_coeff1 = Variable("W_{W_{coeff1}}", 2e-5, "1/m",
22 | "wing weight coefficent 1", pr= 30.) #orig 12e-5
23 | W_W_coeff2 = Variable("W_{W_{coeff2}}", 60., "Pa",
24 | "wing weight coefficent 2", pr=10.)
25 | p_labor = Variable('p_{labor}',1.,'1/min','cost of labor', pr = 20.)
26 |
27 | # Dimensional constants
28 | Range = Variable("Range",3000, "km", "aircraft range")
29 | TSFC = Variable("TSFC", 0.6, "1/hr", "thrust specific fuel consumption")
30 | V_min = Variable("V_{min}", 25, "m/s", "takeoff speed", pr=20.)
31 | W_0 = Variable("W_0", 6250, "N", "aircraft weight excluding wing", pr=20.)
32 |
33 | # Free Variables
34 | LoD = Variable('L/D','-','lift-to-drag ratio')
35 | D = Variable("D", "N", "total drag force")
36 | V = Variable("V", "m/s", "cruising speed")
37 | W = Variable("W", "N", "total aircraft weight")
38 | Re = Variable("Re", "-", "Reynold's number")
39 | CDA0 = Variable("(CDA0)", "m^2", "fuselage drag area") #0.035 originally
40 | C_D = Variable("C_D", "-", "drag coefficient")
41 | C_L = Variable("C_L", "-", "lift coefficient of wing")
42 | C_f = Variable("C_f", "-", "skin friction coefficient")
43 | W_f = Variable("W_f", "N", "fuel weight")
44 | V_f = Variable("V_f", "m^3", "fuel volume")
45 | V_f_avail = Variable("V_{f_{avail}}","m^3","fuel volume available")
46 | T_flight = Variable("T_{flight}", "hr", "flight time")
47 |
48 | # Free variables (fixed for performance eval.)
49 | A = Variable("A", "-", "aspect ratio",fix = True)
50 | S = Variable("S", "m^2", "total wing area", fix = True)
51 | W_w = Variable("W_w", "N", "wing weight")#, fix = True)
52 | W_w_strc = Variable('W_w_strc','N','wing structural weight', fix = True)
53 | W_w_surf = Variable('W_w_surf','N','wing skin weight', fix = True)
54 | V_f_wing = Variable("V_f_wing",'m^3','fuel volume in the wing', fix = True)
55 | V_f_fuse = Variable('V_f_fuse','m^3','fuel volume in the fuselage', fix = True)
56 | constraints = []
57 |
58 | # Weight and lift model
59 | constraints += [W >= W_0 + W_w + W_f,
60 | W_0 + W_w + 0.5 * W_f <= 0.5 * rho * S * C_L * V ** 2,
61 | W <= 0.5 * rho * S * C_Lmax * V_min ** 2,
62 | T_flight >= Range / V,
63 | LoD == C_L/C_D]
64 |
65 | # Thrust and drag model
66 | C_D_fuse = CDA0 / S
67 | C_D_wpar = k * C_f * S_wetratio
68 | C_D_ind = C_L ** 2 / (np.pi * A * e)
69 | constraints += [W_f >= TSFC * T_flight * D,
70 | D >= 0.5 * rho * S * C_D * V ** 2,
71 | C_D >= C_D_fuse + C_D_wpar + C_D_ind,
72 | V_f_fuse <= 10*units('m')*CDA0,
73 | Re <= (rho / mu) * V * (S / A) ** 0.5,
74 | C_f >= 0.074 / Re ** 0.2]
75 |
76 | # Fuel volume model
77 | with SignomialsEnabled():
78 | constraints +=[V_f == W_f / g / rho_f,
79 | V_f_wing**2 <= 0.0009*S**3/A*tau**2, # linear with b and tau, quadratic with chord
80 | V_f_avail <= V_f_wing + V_f_fuse, #[SP]
81 | V_f_avail >= V_f
82 | ]
83 |
84 | # Wing weight model
85 | constraints += [W_w_surf >= W_W_coeff2 * S,
86 | W_w_strc**2. >= W_W_coeff1**2. / tau**2. * (N_ult**2. * A ** 3. * ((W_0+V_f_fuse*g*rho_f) * W * S)),
87 | W_w >= W_w_surf + W_w_strc]
88 |
89 | return constraints
90 |
91 | def test():
92 | m = SimPleAC()
93 | m.cost = m['W_f']
94 | sol = m.localsolve(verbosity = 2)
95 |
96 | if __name__ == "__main__":
97 | # Most basic way to execute the model
98 | from gpkit import Vectorize
99 | with Vectorize(3):
100 | m = SimPleAC()
101 | # m.cost = m['W']
102 | # sol1 = m.localsolve(verbosity = 2)
103 | m.substitutions["Range"] = [1000, 3000, 6000]
104 | m.extend([ m["A"] == m["A"][0],
105 | m["S"] == m["S"][0],
106 | m["W_w"] == m["W_w"][0],
107 | m["W_w_strc"] == m["W_w_strc"][0],
108 | m["W_w_surf"] == m["W_w_surf"][0],
109 | m["V_f_wing"] == m["V_f_wing"][0],
110 | m["V_f_fuse"] == m["V_f_fuse"][0]])
111 | m.cost = 3*m['W_f'][0] + m['W_f'][1] + 0.33*m['W_f'][2]
112 | sol = m.localsolve(verbosity = 1)
113 | # print(sol1.diff(sol2))
114 | print(sol.table())
115 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/SimPleAC/SimPleAC_multimission.py:
--------------------------------------------------------------------------------
1 | from builtins import range
2 | import numpy as np
3 | from gpkit import Model, Variable, SignomialsEnabled, SignomialEquality, \
4 | VarKey, units, Vectorize, settings
5 | from gpkitmodels.SP.SimPleAC.SimPleAC_mission import Mission, SimPleAC
6 | from gpkitmodels.SP.atmosphere.atmosphere import Atmosphere
7 |
8 | # SimPleAC with multimission design (updated 5/31/2019, by Berk Ozturk)
9 |
10 | class Multimission(Model):
11 | def setup(self,aircraft,Nmissions,Nsegments):
12 | self.aircraft = aircraft
13 | self.missions = []
14 | for i in range(0,Nmissions):
15 | self.missions.append(Mission(self.aircraft,Nsegments))
16 |
17 | # Multimission objective variables
18 | W_f_mm = Variable('W_{f_{mm}}','N','multimission fuel weight')
19 |
20 | with Vectorize(Nmissions):
21 | # Mission variables
22 | hcruise = Variable('h_{cruise_{mm}}', 'm', 'minimum cruise altitude')
23 | Range = Variable("Range_{mm}", "km", "aircraft range")
24 | W_p = Variable("W_{p_{mm}}", "N", "payload weight", pr=20.)
25 | rho_p = Variable("\\rho_{p_{mm}}", 1500, "kg/m^3", "payload density", pr=10.)
26 | V_min = Variable("V_{min_{mm}}", 25, "m/s", "takeoff speed", pr=20.)
27 | cost_index = Variable("C_{mm}", '1/hr','hourly cost index')
28 | TOfac = Variable('T/O factor_{mm}', 2.,'-','takeoff thrust factor')
29 |
30 | constraints = []
31 |
32 | # Setting up the missions
33 | for i in range(0,Nmissions):
34 | constraints += [
35 | self.missions[i]['h_{cruise_m}'] == hcruise[i],
36 | self.missions[i]['Range_m'] == Range[i],
37 | self.missions[i]['W_{p_m}'] == W_p[i],
38 | self.missions[i]['\\rho_{p_m}'] == rho_p[i],
39 | self.missions[i]['V_{min_m}'] == V_min[i],
40 | self.missions[i]['C_m'] == cost_index[i],
41 | self.missions[i]['T/O factor_m'] == TOfac[i],
42 | # Upper bounding relevant variables
43 | W_f_mm <= 1e11*units('N'),
44 | ]
45 |
46 | # Multimission constraints
47 | constraints += [W_f_mm >= sum(self.missions[i]['W_{f_m}'] for i in range(0,Nmissions))]
48 |
49 | return constraints, self.aircraft, self.missions
50 |
51 | def test():
52 | Nmissions = 2
53 | Nsegments = 4
54 | aircraft = SimPleAC()
55 | m = Multimission(aircraft,Nmissions,Nsegments)
56 | m.substitutions.update({
57 | 'h_{cruise_{mm}}':[5000*units('m'), 5000*units('m')],
58 | 'Range_{mm}' :[3000*units('km'), 2000*units('km')],
59 | 'W_{p_{mm}}' :[6250*units('N'), 8000*units('N')],
60 | '\\rho_{p_{mm}}' :[1500*units('kg/m^3'), 2000*units('kg/m^3')],
61 | 'C_{mm}' :[120*units('1/hr'), 360*units('1/hr')],
62 | })
63 |
64 | m.cost = (m.missions[0]['W_{f_m}']*units('1/N') + m.missions[1]['C_m']*m.missions[1]['t_m'])
65 | if settings["default_solver"] == "cvxopt":
66 | return
67 | else:
68 | sol = m.localsolve(verbosity=0)
69 |
70 | if __name__ == "__main__":
71 | Nmissions = 2
72 | Nsegments = 4
73 | aircraft = SimPleAC()
74 | m = Multimission(aircraft,Nmissions,Nsegments)
75 | m.substitutions.update({
76 | 'h_{cruise_{mm}}':[5000*units('m'), 5000*units('m')],
77 | 'Range_{mm}' :[3000*units('km'), 2000*units('km')],
78 | 'W_{p_{mm}}' :[6250*units('N'), 8000*units('N')],
79 | '\\rho_{p_{mm}}' :[1500*units('kg/m^3'), 2000*units('kg/m^3')],
80 | 'C_{mm}' :[120*units('1/hr'), 360*units('1/hr')],
81 | })
82 |
83 | m.cost = (m.missions[0]['W_{f_m}']*units('1/N') + m.missions[1]['C_m']*m.missions[1]['t_m'])
84 | sol = m.localsolve(verbosity = 2)
85 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/SimPleAC/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/SimPleAC/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/SimPleAC/simpleac.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/SimPleAC/simpleac.pdf
--------------------------------------------------------------------------------
/gpkitmodels/SP/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/aircraft/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/aircraft/prop/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/dae51_fitdata.csv:
--------------------------------------------------------------------------------
1 | lb1,lb0,ub0,d,e11,e10,K,e21,e20,e00,e01,a1,ftype,max_err,c2,c1,c0,rms_err,ub1
2 | 50000.00000000001,0.4742,1.4495,2,-0.8108155563264187,6.320590230366453,3,-2.8766519103828543,42.173818269878126,-0.7631609541518628,-5.0062373433526455,6.153287557875197,SMA,0.32163665175749223,2.655008812201457,8.068321204642572e-09,309019700605405.5,0.09733995717389085,700000.0000000002
3 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/dae51polars/dae51.dat:
--------------------------------------------------------------------------------
1 | DAE 51
2 | # Daedalus propeller airfoil
3 | 1.000000 -0.000051
4 | 0.989166 0.001996
5 | 0.975819 0.004501
6 | 0.957448 0.007991
7 | 0.931599 0.013004
8 | 0.901654 0.018957
9 | 0.870818 0.025167
10 | 0.839998 0.031363
11 | 0.809336 0.037430
12 | 0.778839 0.043301
13 | 0.748503 0.048933
14 | 0.718326 0.054243
15 | 0.688314 0.059175
16 | 0.658443 0.063716
17 | 0.628653 0.067839
18 | 0.598855 0.071530
19 | 0.569004 0.074792
20 | 0.539074 0.077652
21 | 0.509071 0.080095
22 | 0.479015 0.082100
23 | 0.448934 0.083641
24 | 0.418877 0.084690
25 | 0.388863 0.085217
26 | 0.358908 0.085197
27 | 0.329049 0.084600
28 | 0.299321 0.083399
29 | 0.269760 0.081570
30 | 0.240419 0.079081
31 | 0.211388 0.075895
32 | 0.182766 0.071967
33 | 0.154699 0.067243
34 | 0.127404 0.061670
35 | 0.101193 0.055204
36 | 0.076503 0.047832
37 | 0.054060 0.039675
38 | 0.034881 0.031085
39 | 0.020059 0.022737
40 | 0.010052 0.015405
41 | 0.004208 0.009557
42 | 0.001251 0.005050
43 | 0.000113 0.001479
44 | 0.000123 -0.001494
45 | 0.001002 -0.004085
46 | 0.002793 -0.006422
47 | 0.006019 -0.008524
48 | 0.011960 -0.010609
49 | 0.022698 -0.012727
50 | 0.040333 -0.014554
51 | 0.064233 -0.015717
52 | 0.091882 -0.016173
53 | 0.121305 -0.016067
54 | 0.151560 -0.015570
55 | 0.182231 -0.014790
56 | 0.213149 -0.013797
57 | 0.244240 -0.012649
58 | 0.275464 -0.011392
59 | 0.306798 -0.010061
60 | 0.338230 -0.008690
61 | 0.369725 -0.007318
62 | 0.401184 -0.005962
63 | 0.432597 -0.004640
64 | 0.463976 -0.003366
65 | 0.495325 -0.002147
66 | 0.526644 -0.000994
67 | 0.557930 0.000082
68 | 0.589182 0.001071
69 | 0.620390 0.001960
70 | 0.651547 0.002731
71 | 0.682648 0.003367
72 | 0.713694 0.003847
73 | 0.744690 0.004152
74 | 0.775644 0.004269
75 | 0.806546 0.004190
76 | 0.837373 0.003898
77 | 0.868134 0.003359
78 | 0.898985 0.002532
79 | 0.929868 0.001410
80 | 0.956572 0.000219
81 | 0.975393 -0.000742
82 | 0.988983 -0.001472
83 | 1.000000 -0.002051
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/dae51polars/dae51.txt:
--------------------------------------------------------------------------------
1 | 1.000000 -0.000051
2 | 0.989166 0.001996
3 | 0.975819 0.004501
4 | 0.957448 0.007991
5 | 0.931599 0.013004
6 | 0.901654 0.018957
7 | 0.870818 0.025167
8 | 0.839998 0.031363
9 | 0.809336 0.037430
10 | 0.778839 0.043301
11 | 0.748503 0.048933
12 | 0.718326 0.054243
13 | 0.688314 0.059175
14 | 0.658443 0.063716
15 | 0.628653 0.067839
16 | 0.598855 0.071530
17 | 0.569004 0.074792
18 | 0.539074 0.077652
19 | 0.509071 0.080095
20 | 0.479015 0.082100
21 | 0.448934 0.083641
22 | 0.418877 0.084690
23 | 0.388863 0.085217
24 | 0.358908 0.085197
25 | 0.329049 0.084600
26 | 0.299321 0.083399
27 | 0.269760 0.081570
28 | 0.240419 0.079081
29 | 0.211388 0.075895
30 | 0.182766 0.071967
31 | 0.154699 0.067243
32 | 0.127404 0.061670
33 | 0.101193 0.055204
34 | 0.076503 0.047832
35 | 0.054060 0.039675
36 | 0.034881 0.031085
37 | 0.020059 0.022737
38 | 0.010052 0.015405
39 | 0.004208 0.009557
40 | 0.001251 0.005050
41 | 0.000113 0.001479
42 | 0.000123 -0.001494
43 | 0.001002 -0.004085
44 | 0.002793 -0.006422
45 | 0.006019 -0.008524
46 | 0.011960 -0.010609
47 | 0.022698 -0.012727
48 | 0.040333 -0.014554
49 | 0.064233 -0.015717
50 | 0.091882 -0.016173
51 | 0.121305 -0.016067
52 | 0.151560 -0.015570
53 | 0.182231 -0.014790
54 | 0.213149 -0.013797
55 | 0.244240 -0.012649
56 | 0.275464 -0.011392
57 | 0.306798 -0.010061
58 | 0.338230 -0.008690
59 | 0.369725 -0.007318
60 | 0.401184 -0.005962
61 | 0.432597 -0.004640
62 | 0.463976 -0.003366
63 | 0.495325 -0.002147
64 | 0.526644 -0.000994
65 | 0.557930 0.000082
66 | 0.589182 0.001071
67 | 0.620390 0.001960
68 | 0.651547 0.002731
69 | 0.682648 0.003367
70 | 0.713694 0.003847
71 | 0.744690 0.004152
72 | 0.775644 0.004269
73 | 0.806546 0.004190
74 | 0.837373 0.003898
75 | 0.868134 0.003359
76 | 0.898985 0.002532
77 | 0.929868 0.001410
78 | 0.956572 0.000219
79 | 0.975393 -0.000742
80 | 0.988983 -0.001472
81 | 1.000000 -0.002051
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/dae51polars/dae51_polarfits.py:
--------------------------------------------------------------------------------
1 | "dae51_polarfits.py"
2 | from __future__ import print_function
3 | from builtins import zip
4 | from builtins import range
5 | import numpy as np
6 | import pandas as pd
7 | import matplotlib.pyplot as plt
8 | from gpfit.fit import fit
9 | import sys
10 | from gpkitmodels.GP.aircraft.wing.wing import Wing
11 | from gpkitmodels.GP.aircraft.prop.propeller import ActuatorProp
12 | import inspect
13 | import os
14 |
15 | GENERATE = True
16 | plt.rcParams.update({'font.size':15})
17 |
18 | def text_to_df(filename):
19 | "parse XFOIL polars and concatente data in DataFrame"
20 | lines = list(open(filename))
21 | for i, l in enumerate(lines):
22 | lines[i] = l.split("\n")[0]
23 | for j in 10-np.arange(9):
24 | if " "*j in lines[i]:
25 | lines[i] = lines[i].replace(" "*j, " ")
26 | if "---" in lines[i]:
27 | start = i
28 | data = {}
29 | titles = lines[start-1].split(" ")[1:]
30 | for t in titles:
31 | data[t] = []
32 |
33 | for l in lines[start+1:]:
34 | for i, v in enumerate(l.split(" ")[1:]):
35 | data[titles[i]].append(v)
36 |
37 | df = pd.DataFrame(data)
38 | return df
39 |
40 | def fit_setup(Re_range):
41 | "set up x and y parameters for gp fitting"
42 | CL = []
43 | CD = []
44 | RE = []
45 | fig, ax = plt.subplots()
46 | for r in Re_range:
47 | dataf = text_to_df("dae51.ncrit09.Re%dk.pol" % r)
48 | if r < 150:
49 | cl = dataf["CL"].values.astype(np.float)
50 | cd = dataf["CD"].values.astype(np.float)
51 | CL.append(np.hstack([cl[cl >= 1.0]]*1))
52 | CD.append(np.hstack([cd[cl >= 1.0]]*1))
53 | elif r < 200:
54 | cl = dataf["CL"].values.astype(np.float)
55 | cd = dataf["CD"].values.astype(np.float)
56 | CL.append(cl[cl >= 0.9])
57 | CD.append(cd[cl >= 0.9])
58 | else:
59 | CL.append(dataf["CL"].values.astype(np.float))
60 | CD.append(dataf["CD"].values.astype(np.float))
61 | ax.plot(dataf["CL"].values.astype(np.float), dataf["CD"].values.astype(np.float))
62 | ax.legend(["%d" % re for re in Re_range])
63 | RE.append([r*1000.0]*len(CL[-1]))
64 |
65 | fig.savefig("polarstest.pdf")
66 |
67 | u1 = np.hstack(CL)
68 | u2 = np.hstack(RE)
69 | w = np.hstack(CD)
70 | u = [u1, u2]
71 | xx = np.log(u2)
72 | x = np.log(u)
73 | y = np.log(w)
74 | return x, y
75 |
76 | def return_fit(cl, re):
77 | "polar fit for the dae51 airfoil"
78 | cd = (3.0902e14*cl**-.763161*re**-5.00624
79 | + 8.06832e-09*cl**6.32059*re**-0.810816
80 | + 2.65501*cl**42.1738*re**-2.87665 )**(1/6.1539)
81 | # SMA function, K=3, max RMS error = 0.09734
82 | return cd
83 |
84 | def plot_fits(re, cnstr, x, y):
85 | "plot fit compared to data"
86 | # colors = ["k", "m", "b", "g", "y"]
87 | colors = ["#084081", "#0868ac", "#2b8cbe", "#4eb3d3", "#7bccc4"]
88 | assert len(re) == len(colors)
89 | lre, ind = np.unique(X[1], return_index=True)
90 | xre = np.exp(lre)
91 | xcl = [np.exp(X[0][ind[i-1]:ind[i]]) for i in range(1, len(ind))]
92 | cds = [np.exp(Y[ind[i-1]:ind[i]]) for i in range(1, len(ind))]
93 | yfit = cnstr.evaluate(x)
94 | cdf = [np.exp(yfit[ind[i-1]:ind[i]]) for i in range(1, len(ind))]
95 | fig, ax = plt.subplots()
96 | i = 0
97 | for r, cl, cd, fi in zip(xre, xcl, cds, cdf):
98 | roundre = int(np.round(r)/1000)
99 | if roundre in re:
100 | ax.plot(cl, cd, "o", mec=colors[i], mfc="none", mew=1.5)
101 | ax.plot(cl, fi, c=colors[i], label="Re = %dk" % roundre, lw=2)
102 | i += 1
103 | ax.set_xlabel("$C_L$")
104 | ax.set_ylabel("$c_{d_p}$")
105 | ax.legend(loc=2)
106 | ax.grid()
107 | return fig, ax
108 |
109 | if __name__ == "__main__":
110 | Re = np.array([50, 75, 125, 150, 200, 300, 400, 500, 600, 700])
111 | X, Y = fit_setup(Re) # call fit(X, Y, 4, "SMA") to get fit
112 | np.random.seed(0)
113 | cn, err = fit(X, Y, 3, "SMA")
114 | print("RMS error: %.5f" % err)
115 | df = cn.get_dataframe()
116 | if GENERATE:
117 | path = os.path.dirname(inspect.getfile(ActuatorProp))
118 | df.to_csv(path + os.sep + "dae51_fitdata.csv", index=False)
119 | else:
120 | df.to_csv("dae51fitdata.csv", index=False)
121 |
122 | # replot = np.array([150, 200, 300, 350, 400])
123 | replot = np.array([300, 350, 400, 450, 500])
124 | F, A = plot_fits(replot, cn, X, Y)
125 | if len(sys.argv) > 1:
126 | path = sys.argv[1]
127 | F.savefig(path + "dae51polarfit1.eps", bbox_inches="tight")
128 | else:
129 | F.savefig("dae51polarfit1.eps", bbox_inches="tight")
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/dae51polars/polarstest.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/aircraft/prop/dae51polars/polarstest.pdf
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/prop/propeller.py:
--------------------------------------------------------------------------------
1 | " propeller model "
2 | from builtins import range
3 | from numpy import pi
4 | from gpkit import Model, Variable,Vectorize,parse_variables, SignomialsEnabled, SignomialEquality
5 | from gpkit.constraints.tight import Tight as TCS
6 | from gpfit.fit_constraintset import XfoilFit
7 | import os
8 | import pandas as pd
9 |
10 |
11 |
12 | class BladeElementPerf(Model):
13 | """ Single element of a propeller blade
14 |
15 | Variables
16 | ---------
17 | dT [lbf] thrust
18 | eta_i [-] local induced efficiency
19 | dQ [N*m] torque
20 | omega [rpm] propeller rotation rate
21 | Wa [m/s] Axial total relative velocity
22 | Wt [m/s] Tangential total relative velocity
23 | Wr [m/s] Total relative velocity
24 | va [m/s] Axial induced velocity
25 | vt [m/s] Tangential induced velocity
26 | G [m^2/s] Circulation
27 | cl [-] local lift coefficient
28 | cd [-] local drag coefficient
29 | B 2 [-] number of blades
30 | r [m] local radius
31 | lam_w [-] advance ratio
32 | eps [-] blade efficiency
33 | AR_b [-] blade aspect ratio
34 | AR_b_max 50 [-] max blade aspect ratio
35 | Re [-] blade reynolds number
36 | f [-] intermediate tip loss variable
37 | F [-] Prandtl tip loss factor
38 | cl_max .6 [-] max airfoil cl
39 | dr [m] length of blade element
40 | M [-] Mach number
41 | a 295 [m/s] Speed of sound at altitude
42 |
43 | """
44 |
45 | @parse_variables(__doc__, globals())
46 | def setup(self,static, state):
47 | V = state.V
48 | rho = state.rho
49 | R = static.R
50 | mu = state.mu
51 | path = os.path.dirname(__file__)
52 | fd = pd.read_csv(path + os.sep + "dae51_fitdata.csv").to_dict(
53 | orient="records")[0]
54 | c = static.c
55 | constraints = [TCS([Wa>=V + va]),
56 | TCS([Wt + vt<=omega*r]),
57 | TCS([G == (1./2.)*Wr*c*cl]),
58 | F == (2./pi)*(1.01116*f**.0379556)**(10), #This is the GP fit of arccos(exp(-f))
59 | M == Wr/a,
60 | lam_w == (r/R)*(Wa/Wt),
61 | va == vt*(Wt/Wa),
62 | eps == cd/cl,
63 | TCS([dQ >= rho*B*G*(Wa+eps*Wt)*r*dr]),
64 | AR_b == R/c,
65 | AR_b <= AR_b_max,
66 | Re == Wr*c*rho/mu,
67 | eta_i == (V/(omega*r))*(Wt/Wa),
68 | TCS([f+(r/R)*B/(2*lam_w) <= (B/2.)*(1./lam_w)]),
69 | XfoilFit(fd, cd, [cl,Re], name="polar"),
70 | cl <= cl_max
71 | ]
72 | with SignomialsEnabled():
73 | constraints += [SignomialEquality(Wr**2,(Wa**2+Wt**2)),
74 | TCS([dT <= rho*B*G*(Wt-eps*Wa)*dr]),
75 | TCS([vt**2*F**2*(1.+(4.*lam_w*R/(pi*B*r))**2) >= (B*G/(4.*pi*r))**2]),
76 | ]
77 | return constraints, state
78 |
79 |
80 |
81 | class BladeElementProp(Model):
82 | """ Performance for a propeller with multiple elements
83 |
84 | Variables
85 | ---------
86 | Mtip .5 [-] Max tip mach number
87 | omega_max 10000 [rpm] maximum rotation rate
88 | eta [-] overall efficiency
89 | omega [rpm] rotation rate
90 | T [lbf] total thrust
91 | Q [N*m] total torque
92 | """
93 | @parse_variables(__doc__, globals())
94 | def setup(self,static, state, N=5):
95 | with Vectorize(N):
96 | blade = BladeElementPerf(static, state)
97 |
98 |
99 | constraints = [blade.dr == static.R/(N),
100 | blade.omega == omega,
101 | blade.r[0] == static.R/(2.*N)]
102 |
103 | for n in range(1,N):
104 | constraints += [TCS([blade.r[n] >= blade.r[n-1] + static.R/N]),
105 | blade.eta_i[n] == blade.eta_i[n-1],
106 | ]
107 |
108 | constraints += [TCS([Q >= sum(blade.dQ)]),
109 | eta == state.V*T/(omega*Q),
110 | blade.M[-1] <= Mtip,
111 | static.T_m >= T,
112 | omega <= omega_max
113 | ]
114 |
115 | with SignomialsEnabled():
116 | constraints += [TCS([T <= sum(blade.dT)])]
117 |
118 | return constraints, blade
119 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/tail/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/aircraft/tail/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/tail/tail_boom_flex.py:
--------------------------------------------------------------------------------
1 | " tail boom flexibility "
2 | from numpy import pi
3 | from gpkit import Model, parse_variables, SignomialsEnabled
4 |
5 | class TailBoomFlexibility(Model):
6 | """ Tail Boom Flexibility Model
7 |
8 | Variables
9 | ---------
10 | Fne [-] tail boom flexibility factor
11 | deda [-] wing downwash derivative
12 | SMcorr 0.55 [-] corrected static margin
13 | sph1 [-] flexibility helper variable 1
14 | sph2 [-] flexibility helper variable 2
15 |
16 | LaTex Strings
17 | -------------
18 | Fne F_{\mathrm{NE}}
19 | deda d\\epsilon/d\\alpha
20 | SMcorr SM_{\\mathrm{corr}}
21 |
22 | """
23 | @parse_variables(__doc__, globals())
24 | def setup(self, htail, hbending, wing):
25 | mh = htail.mh
26 | mw = wing.mw
27 | Vh = htail.Vh
28 | th = hbending.th
29 | CLhmin = htail.CLhmin
30 | CLwmax = wing.planform.CLmax
31 | Sw = wing.planform.S
32 | bw = wing.planform.b
33 | lh = htail.lh
34 | CM = wing.planform.CM
35 |
36 | constraints = [
37 | Fne >= 1 + mh*th,
38 | sph1*(mw*Fne/mh/Vh) + deda <= 1,
39 | sph2 <= Vh*CLhmin/CLwmax,
40 | # (sph1 + sph2).mono_lower_bound({"sph1": .48, "sph2": .52}) >= (
41 | # SMcorr + wing["C_M"]/wing["C_{L_{max}}"]),
42 | deda >= mw*Sw/bw/4/pi/lh]
43 |
44 | with SignomialsEnabled():
45 | constraints.extend([sph1 + sph2 >= SMcorr + CM/CLwmax])
46 |
47 | return constraints
48 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/wing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/aircraft/wing/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/wing/boxspar.py:
--------------------------------------------------------------------------------
1 | " box spar "
2 | from gpkit import parse_variables, SignomialsEnabled
3 | from gpkitmodels.GP.aircraft.wing.boxspar import BoxSpar as BoxSparGP
4 |
5 | #pylint: disable=exec-used, undefined-variable, unused-argument, invalid-name
6 |
7 | class BoxSpar(BoxSparGP):
8 | """ Box Spar Model
9 |
10 | Variables of length N-1
11 | -----------------------
12 | J [m^4] spar x polar moment of inertia
13 |
14 | """
15 | @parse_variables(__doc__, globals())
16 | def setup(self, N, surface):
17 | self.boxspar = BoxSparGP.setup(self, N=N, surface=surface)
18 |
19 | cave = self.cave
20 | tau = self.tau
21 | w = self.w
22 | tshear = self.tshear
23 |
24 | with SignomialsEnabled():
25 | constraints = [J <= cave*tau*w*tshear/3*(cave*tau + w)]
26 |
27 | return self.boxspar, constraints
28 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/aircraft/wing/wing.py:
--------------------------------------------------------------------------------
1 | " wing.py "
2 | import numpy as np
3 | from gpkitmodels.GP.aircraft.wing.wing import Wing as WingGP
4 | from gpkit import parse_variables, SignomialsEnabled
5 |
6 | #pylint: disable=attribute-defined-outside-init, invalid-name
7 |
8 | class Wing(WingGP):
9 | """ SP wing model
10 |
11 | Variables
12 | ---------
13 | mw [-] span wise effectiveness
14 |
15 | """
16 | @parse_variables(__doc__, globals())
17 | def setup(self, N=5):
18 | self.wing = WingGP.setup(self, N=N)
19 | with SignomialsEnabled():
20 | constraints = [mw*(1 + 2/self.planform["AR"]) >= 2*np.pi]
21 |
22 | return self.wing, constraints
23 |
--------------------------------------------------------------------------------
/gpkitmodels/SP/atmosphere/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/SP/atmosphere/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/SP/atmosphere/atmosphere.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from gpkit import Model, Variable, SignomialsEnabled, SignomialEquality, VarKey, units
3 | from gpkit.constraints.bounded import Bounded
4 | from gpkit import Vectorize
5 | import numpy as np
6 | import matplotlib.pyplot as plt
7 |
8 |
9 | class Atmosphere(Model):
10 | """
11 | Atmosphere models borrowed from Tony Tao, 2017.
12 | Fits included for all variables. Density and viscosity are demonstrated.
13 | The rest are commented, to be used with modeler's discretion!
14 | Boundedness will vary depending on model application for other variables.
15 | Signomial equalities are fast and reliable here!
16 | """
17 | def setup(self):
18 | # Env. constants
19 | alt_top = Variable('h_{top}',10000,'m','highest altitude valid')
20 | #a_MSL = Variable('a_{MSL}',340.20,'m/s','Speed of sound at MSL')
21 | mu_MSL = Variable('\\mu_{MSL}', 1.778e-5, "kg/m/s", 'dynamic viscosity at MSL', pr=4.)
22 | #nu_MSL = Variable('\\nu_{MSL}', 1.4524e-5, 'm^2/s', 'kinematic viscosity at MSL')
23 | #T_MSL = Variable('T_{MSL}', 288.19, 'K', 'temperature at MSL')
24 | rho_MSL = Variable("\\rho_{MSL}", 1.2256, "kg/m^3", "density of air at MSL", pr=5.)
25 | #p_MSL = Variable('P_{MSL}', 101308, 'Pa', 'pressure at MSL')
26 |
27 | alt = Variable('h','m','altitude')
28 | #a = Variable('a','m/s','Speed of sound')
29 | mu = Variable('\\mu', "kg/m/s", 'dynamic viscosity', pr=4.)
30 | #nu = Variable('\\nu', 'm^2/s', 'kinematic viscosity')
31 | #p = Variable('P', 'Pa', 'pressure')
32 | #T = Variable('T', 'K', 'temperature ')
33 | rho = Variable("\\rho", "kg/m^3", "density of air", pr=5.)
34 |
35 |
36 | # Defining ratios needed for constraints
37 | alt_rat = alt/alt_top
38 | #a_rat = a/a_MSL
39 | mu_rat = mu/mu_MSL
40 | #nu_rat = nu/nu_MSL
41 | #p_rat = p/p_MSL
42 | rho_rat = rho/rho_MSL
43 | #T_rat = T/T_MSL
44 |
45 | constraints = []
46 | with SignomialsEnabled():
47 | constraints += [
48 | alt <= alt_top,
49 | #a_rat ** -0.0140 >= 1.00 * (alt_rat) ** 1.20e-05 + 0.00173 * (alt_rat) ** 1.13,
50 | SignomialEquality(mu_rat ** -0.00795, 1.00 * (alt_rat) ** 1.33e-05 + 0.00156 * (alt_rat) ** 1.17),
51 | #nu_rat ** 0.00490 >= 1.00 * (alt_rat) ** 1.67e-05,# + 0.00424 * (alt_rat) ** 1.10,
52 | #p_rat ** -0.00318 >= 1.00 * (alt_rat) ** 2.18e-05,# + 0.00416 * (alt_rat) ** 1.12,
53 | SignomialEquality(rho_rat ** -0.00336, 1.00 * (alt_rat) ** 1.72e-05 + 0.00357 * (alt_rat) ** 1.11),
54 | #T_rat ** -0.00706 >= 1.00 * (alt_rat) ** 1.21e-05,# + 0.00173 * (alt_rat) ** 1.13,
55 | ]
56 |
57 | return constraints
58 |
59 | if __name__ == "__main__":
60 | m = Atmosphere()
61 | m.substitutions.update({'h':5000*units('m')})
62 | m.cost = m['\\mu']*m['\\rho']
63 | sol = m.localsolve(verbosity = 3)
64 | print(sol.table())
65 |
--------------------------------------------------------------------------------
/gpkitmodels/__init__.py:
--------------------------------------------------------------------------------
1 | from gpkit import Variable
2 | g = Variable("g", 9.81, "m/s^2", "earth surface gravitational acceleration",
3 | constant=True)
4 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/Economic Order Quantity/README.md:
--------------------------------------------------------------------------------
1 | ```python
2 | #inPDF: skip
3 | from gpkit import Variable
4 | ```
5 | # Economic Order Quantity
6 | Reference: Hopp, Wallace J., and Mark L. Spearman. Factory physics. Waveland Press, 2011.
7 |
8 | Econonic order quantity (EOQ) expresses the fundamental tradeoff between
9 | setup costs and holding costs. Small lot sizes have large setup costs per unit,
10 | but large lot sizes incur holding costs.
11 |
12 | The variables are:
13 | ```python
14 | D = Variable("D", "count/year", "Demand rate")
15 | c = Variable("c", "USD/count", "per unit production cost")
16 | A = Variable("A", "USD", "setup cost")
17 | h = Variable("h", "USD/count/year", "holding cost")
18 | Q = Variable("Q", "count", "lot size")
19 | Y = Variable("Y", "USD/year", "cost per year")
20 | ```
21 |
22 | The cost per year is simply the sum of holding, setup, and production costs,
23 | $$Y(Q) = \frac{hQ}{2} + \frac{AD}{Q} + cD$$
24 | Or in gpkit,
25 | ```python
26 | eoq = [Y >= h*Q/2 + A*D/Q + c*D]
27 | ```
28 |
29 | Now create a model that minimizes cost
30 | and substitute in the values given in Factory Physics.
31 | ```python
32 | from gpkit import Model, units
33 | m = Model(Y, eoq)
34 | m.substitutions.update({D: 1000, # /units("year"),
35 | c: 250, # *units("USD"),
36 | A: 500, # *units("USD"),
37 | h: 35}) # *units("USD/year")})
38 | sol = m.solve()
39 | # would like to assert here that
40 | # sol.subinto((2*A*D/h)**0.5 =~= sol.subinto(Q)
41 | ```
42 | ```python
43 | #inPDF: replace with sol.generated.tex
44 | with open("sol.generated.tex", "w") as f:
45 | f.write(m.solution.table(latex=True))
46 | ```
47 | Now sweep $Q$ to see the tradoff curve from Factory Physics
48 | ```python
49 | import numpy as np
50 | m.substitutions.update({
51 | Q: ("sweep", np.logspace(np.log10(10), np.log10(500), 25))})
52 | sol = m.solve()
53 | ```
54 | ```python
55 | #inPDF: skip
56 | import matplotlib.pyplot as plt
57 | plt.figure()
58 | plt.plot(sol("Q"), sol("Y")/sol("D") - sol("c"))
59 | plt.ylim((0, 30))
60 | plt.xlabel(Q)
61 | plt.ylabel("Holding plus Setup Cost [$/unit]")
62 | plt.savefig("qsweep.pdf")
63 | plt.close()
64 | ```
65 | 
66 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/Economic Order Quantity/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Economic Order Quantity/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/misc/Economic Order Quantity/eoq.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Economic Order Quantity/eoq.pdf
--------------------------------------------------------------------------------
/gpkitmodels/misc/Economic Order Quantity/qsweep.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Economic Order Quantity/qsweep.pdf
--------------------------------------------------------------------------------
/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/MoICylinder.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/MoICylinder.PNG
--------------------------------------------------------------------------------
/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/README.md:
--------------------------------------------------------------------------------
1 | Introduction
2 | -------------------------
3 |
4 | In this example, we express the area moment of inertia (I) of a hollow cylindrical section as a functional of its physical dimensions to formulate the bending section design problem in a GP form.
5 |
6 | ```python
7 |
8 | import numpy as np
9 | from gpkit import Variable, Model, units
10 | from gpkit.constraints.tight import Tight
11 | import matplotlib.pyplot as plt
12 |
13 | Tight.reltol = 1e-2
14 |
15 | class Beam(Model):
16 | def __init__(self):
17 |
18 | ```
19 |
20 |
21 | Hollow Cylinder Bending Inertia Equations
22 | --------------------------
23 |
24 | The inertia of a beam describes the stiffness of the beam in bending. If we desire to create a beam bending model in GP, it is a critical quantity. Since we are also interested in structural efficiency, we want to minimize the cross-sectional area of the section, which describes the weight of the beam along with the density of the beam and its length.
25 |
26 |
27 |
28 | \begin{figure}[h]
29 | \centering
30 | \includegraphics[width = 5cm]{MoICylinder}
31 | \caption{The cross-sectional parameters of a hollow cylindrical section}
32 | \label{fig:cylinder}
33 | \end{figure}
34 |
35 |
36 |
37 |
38 | The equations for the inertia and cross-sectional area of a hollow cylindrical section are shown in Figure 1 are:
39 |
40 | \begin{equation}
41 | \tag{1}
42 | A = \pi(r_o^2 - r_i^2)
43 | \end{equation}
44 |
45 | \begin{equation}
46 | \tag{2}
47 | I = \frac{1}{4}\pi(r_o^4 - r_i^4)
48 | \end{equation}
49 |
50 | where $r_o$ is the outer radius, $r_i$ is the inner radius, $A$ is the cross-sectional area, and $I$ is the moment of inertia of the section. The form as-is is not GP compatible by the following reasoning.
51 |
52 | If we say that greater $I$ is good and greater $A$ is bad, then the right bounding inequalities for the expressions above are:
53 |
54 | \begin{equation}
55 | \tag{3}
56 | A \geq \pi(r_o^2 - r_i^2)
57 | \end{equation}
58 |
59 | \begin{equation}
60 | \tag{4}
61 | I \leq \frac{1}{4}\pi(r_o^4 - r_i^4)
62 | \end{equation}
63 |
64 | since we are trying to maximize $I$ and minimize $A$. Equation 4 is GP-compatible, since we can add $\frac{1}{4}\pi r_i^4$ to both sides of the equation and have a valid posynomial. However, no such transformation is possible for Equation 3. And if we changed the sign of the inequality in Equation 3, then $A$ would go off to 0 ($10^{-\infty}$), a non-physical solution.
65 |
66 | GP Formulation
67 | -------------------
68 |
69 | We begin by rearranging Equation 3 to get the following:
70 |
71 | \begin{equation}
72 | \tag{5}
73 | \frac{A}{\pi} = r_o^2 - r_i^2
74 | \end{equation}
75 |
76 | We then split Equation 4 as shown in Equation 6. Note that we limit $I$ to be less than the right hand side (RHS) of the equation, for later conversion into posynomial form. This allows us to use the cross-sectional area relation from Equation 5 to describe the inertia, yielding Equation 7.
77 |
78 | \begin{equation}
79 | \tag{6}
80 | I \leq \frac{1}{4} \pi (r_o^2 - r_i^2 ) (r_o^2 + r_i^2)
81 | \end{equation}
82 |
83 | \begin{equation}
84 | \tag{7}
85 | I \leq \frac{1}{4} A (r_o^2 + r_i^2)
86 | \end{equation}
87 |
88 | We use the geometric mean to make an approximation for $(r_o^2 + r_i^2)$.
89 |
90 | \begin{equation}
91 | \tag{8}
92 | 2r_or_i \leq r_o^2 + r_i^2
93 | \end{equation}
94 |
95 | Substituting Equation 8 into Equation 7, we can get a conservative bound on the moment of inertia that is GP-compatible.
96 |
97 | \begin{equation}
98 | \tag{9}
99 | I \leq \frac{Ar_or_i}{2}
100 | \end{equation}
101 |
102 | Rearranging the area relation in Equation 3:
103 |
104 | \begin{equation}
105 | \tag{10}
106 | r_i^2 + \frac{A}{\pi} \leq r_o^2
107 | \end{equation}
108 |
109 | We substitute for $r_i$ from Equation 11 in Equation 12:
110 |
111 | \begin{equation}
112 | \tag{11}
113 | r_i^2 \geq \frac{4I^2}{A^2r_o^2}
114 | \end{equation}
115 |
116 | \begin{equation}
117 | \tag{12}
118 | \frac{4I^2}{A^2r_o^2} + \frac{A}{\pi} \leq r_o^2
119 | \end{equation}
120 |
121 | The final formulation is as follows:
122 |
123 | \begin{equation}
124 | \tag{13}
125 | \bar{W} \geq \rho A
126 | \end{equation}
127 |
128 | \begin{equation}
129 | \tag{14}
130 | \frac{4I^2}{A^2r_o^2} + \frac{A}{\pi} \leq r_o^2
131 | \end{equation}
132 |
133 | ```python
134 |
135 | I = Variable("I", "m^4", "Moment of inertia")
136 | A = Variable("A", "m^2", "Cross-sectional area")
137 | ro = Variable("r_o", "m", "Outer radius")
138 | romax = Variable("r_o_{max}", "m", "Maximum outer radius")
139 |
140 | constraints = [Tight([4*I**2/(A**2*ro**2) + A/np.pi <= ro**2]),
141 | ro <= romax]
142 |
143 | # Minimizing cross-sectional area => min(weight)
144 | objective = A
145 |
146 | Model.__init__(self,objective,constraints)
147 | ```
148 |
149 |
150 | This formulation is GP-compatible, because the equations are of monomial and posynomial forms, and we have the right bounds on the two parameters we care about. Greater moment of inertia $I$ is good, but it is upper bounded by the outer radius. And area $A$ is bounded because it is directly proportional to the weight of the structure in question, which we want to minimize. Using Equations 13 and 14, we can give conservative approximations for the area moments of inertia of hollow cylindrical beams, allowing us to calculate their deflections under bending loads.
151 |
152 | However, it is important to note that the geometric mean has been used in Equation 8 to give a conservative bound on the moment of inertia, which means that the solution is not exact. Furthermore, it doesn't seem absolutely clear that the constraint in Equation 14 will be always tight, but results from problems that use this models show empirically that it is always tight. The engineering intuition holds!
153 |
154 | ```python
155 |
156 |
157 | if __name__ == "__main__":
158 | M = Beam()
159 | M.substitutions.update({"I": 10**-4})
160 | M.substitutions.update({"r_o_{max}": 0.2})
161 | sol = M.sweep({"I": np.logspace(-10, -3, 100)}, skipsweepfailures=True)
162 | print(sol.table())
163 |
164 | I, A, ro = sol("I"), sol("A"), sol("r_o")
165 | plt.semilogx(I, (4*I**2/(A**2*ro**2) + A/np.pi - ro**2)/(ro**2))
166 | plt.xlabel("Moment of inertia [m^-4]")
167 | plt.ylabel("Tightness of constraint")
168 | plt.ylim([-2.5e-5, 1e-5])
169 | plt.title("Checking constraint tightness (normalized by ro**2)")
170 | plt.savefig("tightness.png")
171 | # plt.show()
172 |
173 | ```
174 |
175 | \begin{figure}[h]
176 | \centering
177 | \includegraphics[width = 10cm]{tightness}
178 | \caption{Verification that Equation 14 stays tight over a range of $I$ values}
179 | \label{fig:cylinder}
180 | \end{figure}
181 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/moi.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/moi.pdf
--------------------------------------------------------------------------------
/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/moi.py:
--------------------------------------------------------------------------------
1 | "get the python code from the markdown file"
2 | from gpkit.tools import mdparse
3 | exec(mdparse("moi.md"))
4 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/tightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Moment of Inertia (cylindrical beam)/tightness.png
--------------------------------------------------------------------------------
/gpkitmodels/misc/Net Present Value/README.md:
--------------------------------------------------------------------------------
1 | # NPV Model
2 | ```python
3 | #inPDF: skip
4 |
5 | from numpy import pi
6 | from gpkit import VectorVariable, Variable, Model, units
7 | import gpkit
8 | from gpkit.tools import te_exp_minus1
9 |
10 | class NPV(Model):
11 | def __init__(self, **kwargs):
12 |
13 |
14 | ```
15 | # Variables
16 |
17 | ```python
18 | #inPDF: replace with vartable.generated.tex
19 | N = 3 # number of payments
20 | npv = Variable('NPV', 'USD', 'net present value')
21 | PV = Variable('PV', 'USD', 'present value')
22 | C = VectorVariable(N, 'C', 'USD', 'cash flow')
23 | C_r = Variable('C_r', 5e6, 'USD/years', 'cash flow rate')
24 | r = Variable('r', 0.1, '1/years', 'interest rate')
25 | t = VectorVariable(N, 't', 'years', 'time')
26 |
27 | ```
28 |
29 | # Problem Set Up
30 |
31 | Let's assume that we want the Net Present Value (NPV) to be \$10 Million. NPV can be expressed by:
32 |
33 | $$ \text{NPV} = \displaystyle\sum\limits_{i=0}^N \text{PV}_i $$ .
34 |
35 | Let's assume that the time periods of the payments are not equal but that the PV$_{i}$ are equal and that there are 3 payments. PV can be expressed by
36 |
37 | $$\text{PV} = \text{C} e^{-rt} $$.
38 |
39 | With these assumptions we can get rid of the $i$ subscript and claim that
40 |
41 | $$\text{NPV} \leq 3\text{PV} $$
42 |
43 | Now the problem becomes solving for the length of each time period such that each PV is equal. Let's assume that the same payment, C, is made at each payment period and that C is given. This allows us to write
44 |
45 | $$ \text{C} \leq \text{PV}e^{r t_i} $$
46 |
47 | Let's assume that $t_0$ is the time of evaulation of the NPV and that $t_i$ is when every payment is made and every PV evaluated. This means that
48 |
49 | $$ \begin{bmatrix} \Delta t_1 = t_1 - t_0 \\
50 | \Delta t_2 = t_2 - t_1 \\
51 | \Delta t_3 = t_3 - t_2
52 | \end{bmatrix} $$
53 |
54 | If there is a cash flow rate, C$_r$ then,
55 |
56 | $$ \text{C} \leq\text{C}_r \Delta t $$
57 |
58 |
59 | # Constraints and Objective
60 |
61 | ```python
62 | #inPDF: replace with model.generated.tex
63 |
64 | constraints = [N*PV >= npv,
65 | C >= PV*(te_exp_minus1(r*t, 4) + 1),
66 | t >= t.left + C/C_r]
67 |
68 | cost = 1/npv
69 | ```
70 | ```python
71 | #inPDF: replace with sol.generated.tex
72 |
73 | Model.__init__(self, cost, constraints, **kwargs)
74 |
75 | @classmethod
76 | def test(cls):
77 | "test the model by solving it"
78 | return cls().solve()
79 |
80 | if __name__ == "__main__":
81 | M = NPV()
82 | SOL = M.solve()
83 | with open("vartable.generated.tex", "w") as f:
84 | f.write("\\begin{tabbing}\n XXXXXXXXXX \\= \\kill\n")
85 | for var in M.varkeys:
86 | f.write("$%s$ : [%s] %s \\\\\n" % (var.name, var.units, var.label))
87 | f.write("\\end{tabbing}")
88 | with open("model.generated.tex", "w") as f:
89 | f.write("$$ %s $$" % M.latex(excluded=["models"]).replace("[ll]", "{ll}"))
90 | with open("sol.generated.tex", "w") as f:
91 | f.write(SOL.table(latex=True))
92 | ```
93 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/Net Present Value/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Net Present Value/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/misc/Net Present Value/npv.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/misc/Net Present Value/npv.pdf
--------------------------------------------------------------------------------
/gpkitmodels/misc/Net Present Value/npv.py:
--------------------------------------------------------------------------------
1 | "get the python code from the markdown file"
2 | from gpkit.tools import mdparse
3 | exec(mdparse("npv.md"))
4 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/README.md:
--------------------------------------------------------------------------------
1 | Miscellanous models
2 | ===================
3 |
4 | This folder contains smaller models which have not been designed as part of a larger engineering system. Many of them are documented with GPkit's MarkDown literate programming framework, using the shell script and latex template in this directory.
5 |
--------------------------------------------------------------------------------
/gpkitmodels/misc/make_gpmd:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Simple command to generate GPkit MarkDown reports
3 | python -c "from gpkit.tools import mdmake; exec mdmake('README.md')"
4 | pandoc $1.md.tex.md --template ../default.latex -o $1.pdf
5 |
--------------------------------------------------------------------------------
/gpkitmodels/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/convexengineering/gplibrary/3229a68974b37d818a0fe1b2bad9b448f40f7323/gpkitmodels/tools/__init__.py
--------------------------------------------------------------------------------
/gpkitmodels/tools/fit_constraintset.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from builtins import str
3 | from builtins import zip
4 | from builtins import range
5 | import numpy as np
6 | from gpkit import ConstraintSet
7 | from gpkit import Variable, NomialArray
8 | from gpkit.small_scripts import unitstr
9 | from .xfoilWrapper import blind_call, single_cl
10 |
11 | class FitCS(ConstraintSet):
12 | def __init__(self, df, ivar, dvars, nobounds=False, err_margin=False, airfoil=False):
13 | self.airfoil = airfoil
14 | self.dvars = dvars
15 | self.ivar = ivar
16 |
17 | K = int(df["K"].iloc[0])
18 | d = int(df["d"].iloc[0])
19 | ftype = df["ftype"].iloc[0]
20 | A = np.array(df[["e%d%d" % (k, i) for k in range(1, K+1) for i in
21 | range(1, d+1)]])[0].astype(float)
22 | B = np.array(df[["c%d" % k for k in range(1, K+1)]])[0].astype(float)
23 |
24 | withvector = False
25 | withvar = False
26 | for dv in self.dvars:
27 | if hasattr(dv, "__len__"):
28 | withvector = True
29 | N = len(dv)
30 | else:
31 | withvar = True
32 | if withvector:
33 | if withvar:
34 | self.dvars = np.array([dv if isinstance(dv, NomialArray) else
35 | [dv]*N for dv in self.dvars]).T
36 | else:
37 | self.dvars = np.array(self.dvars).T
38 | else:
39 | self.dvars = np.array([self.dvars])
40 | monos = [B*NomialArray([(dv**A[k*d:(k+1)*d]).prod() for k in
41 | range(K)]) for dv in self.dvars]
42 |
43 | if err_margin == "Max":
44 | maxerr = float(df["max_err"].iloc[0])
45 | self.mfac = Variable("m_{fac-fit}", 1 + maxerr, "-",
46 | "max error of " + ivar.descr["label"]
47 | + " fit")
48 | elif err_margin == "RMS":
49 | rmserr = float(df["rms_err"].iloc[0])
50 | self.mfac = Variable("m_{fac-fit}", 1 + rmserr, "-",
51 | "RMS error of " + ivar.descr["label"]
52 | + " fit")
53 | else:
54 | self.mfac = Variable("m_{fac-fit}", 1.0, "-", "fit factor")
55 |
56 | if ftype == "ISMA":
57 | # constraint of the form 1 >= c1*u1^exp1*u2^exp2*w^(-alpha) + ....
58 | alpha = np.array(df[["a%d" % k for k in
59 | range(1, K+1)]])[0].astype(float)
60 | lhs = 1
61 | rhs = NomialArray([(mono/(ivar/self.mfac)**alpha).sum() for mono
62 | in monos])
63 | elif ftype == "SMA":
64 | # constraint of the form w^alpha >= c1*u1^exp1 + c2*u2^exp2 +....
65 | alpha = float(df["a1"].iloc[0])
66 | lhs = (ivar/self.mfac)**alpha
67 | rhs = NomialArray([mono.sum() for mono in monos])
68 | elif ftype == "MA":
69 | # constraint of the form w >= c1*u1^exp1, w >= c2*u2^exp2, ....
70 | lhs, rhs = (ivar/self.mfac), NomialArray(monos)
71 |
72 | if K == 1:
73 | # when possible, return an equality constraint
74 | if withvector:
75 | cstrt = [(lh == rh) for rh, lh in zip(rhs, lhs)]
76 | else:
77 | cstrt = (lhs == rhs)
78 | else:
79 | if withvector:
80 | cstrt = [(lh >= rh) for rh, lh in zip(rhs, lhs)]
81 | else:
82 | cstrt = (lhs >= rhs)
83 |
84 | constraints = [cstrt]
85 | if not hasattr(self.mfac, "__len__"):
86 | self.mfac = [self.mfac]*len(self.dvars)
87 | if not hasattr(self.ivar, "__len__"):
88 | self.ivar = [self.ivar]*len(self.dvars)
89 |
90 | self.bounds = []
91 | for dvar in self.dvars:
92 | bds = {}
93 | for i, v in enumerate(dvar):
94 | bds[v] = [float(df["lb%d" % (i+1)].iloc[0]),
95 | float(df["ub%d" % (i+1)].iloc[0])]
96 | self.bounds.append(bds)
97 |
98 | ConstraintSet.__init__(self, constraints)
99 |
100 | def process_result(self, result, TOL=0.03):
101 | super(FitCS, self).process_result(result)
102 |
103 |
104 | for mfac, dvrs, ivr, bds in zip(self.mfac, self.dvars, self.ivar,
105 | self.bounds):
106 |
107 | if self.airfoil:
108 | runxfoil = True
109 | if ".dat" in self.airfoil:
110 | topline = "load " + self.airfoil + " \n afl \n"
111 | elif "naca" in self.airfoil:
112 | topline = self.airfoil + "\n"
113 | else:
114 | print("Bad airfoil specified")
115 | else:
116 | runxfoil = False
117 | bndwrn = True
118 |
119 | if abs(result["sensitivities"]["constants"][mfac]) < 1e-5:
120 | bndwrn = False
121 | runxfoil = False
122 |
123 | if runxfoil:
124 | cl, re = 0.0, 0.0
125 | for d in dvrs:
126 | if "C_L" in str(d):
127 | cl = result(d)
128 | if "Re" in str(d):
129 | re = result(d)
130 | cdgp = result(ivr)
131 | failmsg = "Xfoil call failed at CL=%.4f and Re=%.1f" % (cl, re)
132 | try:
133 | x = blind_call(topline, cl, re, 0.0)
134 | if "VISCAL: Convergence failed" in x:
135 | print("Convergence Warning: %s" % failmsg)
136 | cd, cl = cdgp, 1.0
137 | else:
138 | cd, cl = x[0], x[1]
139 | except:
140 | print("Unable to start Xfoil: %s" % failmsg)
141 | cd, cl = cdgp, 1.0
142 |
143 | err = 1 - cdgp/cd
144 | if err > TOL:
145 | msg = ("Drag error for %s is %.2f. Re=%.1f; CL=%.4f;"
146 | " Xfoil cd=%.6f, GP sol cd=%.6f" %
147 | (", ".join(d.descr["models"]), err, re, cl, cd,
148 | cdgp))
149 | print("Warning: %s" % msg)
150 | else:
151 | bndwrn = False
152 |
153 | if bndwrn:
154 | for d in dvrs:
155 | num = result(d)
156 | err = 0.0
157 | if num < bds[d][0]:
158 | direct = "lower"
159 | bnd = bds[d][0]
160 | err = num/bnd
161 | if num > bds[d][1]:
162 | direct = "upper"
163 | bnd = bds[d][1]
164 | err = 1 - num/bnd
165 |
166 | if err > TOL:
167 | msg = ("Variable %.100s could cause inaccurate result"
168 | " because it exceeds" % d
169 | + " %s bound. Solution is %.4f but"
170 | " bound is %.4f" %
171 | (direct, num, bnd))
172 | print("Warning: " + msg)
173 |
--------------------------------------------------------------------------------
/gpkitmodels/tools/ipynb2module.py:
--------------------------------------------------------------------------------
1 |
2 | "tool for importing ipython notebooks as modules"
3 | from __future__ import print_function
4 |
5 |
6 | from builtins import object
7 | import io, os, sys, types
8 |
9 | # Import Modules
10 | from IPython import get_ipython
11 | from nbformat import read
12 | from IPython.core.interactiveshell import InteractiveShell
13 |
14 |
15 | def find_notebook(fullname, path=None):
16 | """find a notebook, given its fully qualified name and an optional path
17 |
18 | This turns "foo.bar" into "foo/bar.ipynb"
19 | and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar
20 | does not exist.
21 | """
22 | name = fullname.rsplit('.', 1)[-1]
23 | if not path:
24 | path = ['']
25 | for d in path:
26 | nb_path = os.path.join(d, name + ".ipynb")
27 | if os.path.isfile(nb_path):
28 | return nb_path
29 | # let import Notebook_Name find "Notebook Name.ipynb"
30 | nb_path = nb_path.replace("_", " ")
31 | if os.path.isfile(nb_path):
32 | return nb_path
33 |
34 |
35 | class NotebookLoader(object):
36 | """Module Loader for Jupyter Notebooks"""
37 | def __init__(self, path=None):
38 | self.shell = InteractiveShell.instance()
39 | self.path = path
40 |
41 | def load_module(self, fullname):
42 | """import a notebook as a module"""
43 | path = find_notebook(fullname, self.path)
44 |
45 | print("importing Jupyter notebook from %s" % path)
46 |
47 | # load the notebook object
48 | with io.open(path, 'r', encoding='utf-8') as f:
49 | nb = read(f, 4)
50 |
51 |
52 | # create the module and add it to sys.modules
53 | # if name in sys.modules:
54 | # return sys.modules[name]
55 | mod = types.ModuleType(fullname)
56 | mod.__file__ = path
57 | mod.__loader__ = self
58 | mod.__dict__['get_ipython'] = get_ipython
59 | sys.modules[fullname] = mod
60 |
61 | # extra work to ensure that magics that would affect the user_ns
62 | # actually affect the notebook module's ns
63 | save_user_ns = self.shell.user_ns
64 | self.shell.user_ns = mod.__dict__
65 |
66 | try:
67 | for cell in nb.cells:
68 | if cell.cell_type == 'code':
69 | # transform the input to executable Python
70 | code = self.shell.input_transformer_manager.transform_cell(cell.source)
71 | # run the code in themodule
72 | exec(code, mod.__dict__)
73 | finally:
74 | self.shell.user_ns = save_user_ns
75 | return mod
76 |
77 | class NotebookFinder(object):
78 | """Module finder that locates Jupyter Notebooks"""
79 | def __init__(self):
80 | self.loaders = {}
81 |
82 | def find_module(self, fullname, path=None):
83 | nb_path = find_notebook(fullname, path)
84 | if not nb_path:
85 | return
86 |
87 | key = path
88 | if path:
89 | # lists aren't hashable
90 | key = os.path.sep.join(path)
91 |
92 | if key not in self.loaders:
93 | self.loaders[key] = NotebookLoader(path)
94 | return self.loaders[key]
95 |
96 |
97 | # Register the hook
98 | def enable():
99 | sys.meta_path.append(NotebookFinder())
100 |
--------------------------------------------------------------------------------
/gpkitmodels/tools/summing_constraintset.py:
--------------------------------------------------------------------------------
1 | " helpers.py "
2 | from builtins import zip
3 | import numpy as np
4 | from gpkit import ConstraintSet, Variable
5 |
6 | def summing_vars(models, varname):
7 | "returns a list of variables with shared varname in model list"
8 | modelnames = set(m.lineage for m in models)
9 | vkeys = []
10 | for m in models:
11 | for v in m.varkeys[varname]:
12 | if v.lineage in modelnames:
13 | vkeys.append(v)
14 | vrs = [m[v] for m, v in zip(models, vkeys)]
15 | return vrs
16 |
17 | class SummingConstraintSet(ConstraintSet):
18 | def __init__(self, lhs, varname, models=[], variables=[], **kwargs):
19 | summedvars = set([v.key for v in variables])
20 | alreadysummed = set()
21 | for model in models:
22 | twovars = 0
23 | for var in model.varkeys:
24 | if var.name == varname:
25 | twovars += 1
26 | if twovars > 1:
27 | for dvar in model.variables_byname(varname):
28 | if model.lineage == dvar.lineage:
29 | mvars = dvar
30 | else:
31 | mvars = model[varname]
32 | if not hasattr(mvars, "__len__"):
33 | mvars = [mvars]
34 | # next line makes the recursion stop at depth one
35 | # for safety to avoid double counting
36 | mvars = [v for v in mvars if v.key.lineage == model.lineage]
37 | assert len(mvars) == 1
38 | summedvars = summedvars.union([v.key for v in mvars])
39 | for constraint in model.flat():
40 | if hasattr(constraint, "summedvars"):
41 | alreadysummed = alreadysummed.union(constraint.summedvars)
42 | summedvars = summedvars.difference(alreadysummed)
43 | ConstraintSet.__init__(self, [lhs >= sum(Variable(**vk.descr)
44 | for vk in summedvars)],
45 | **kwargs)
46 | @property
47 | def summedvars(self):
48 | return set(self[0].p_lt.varkeys)
49 |
--------------------------------------------------------------------------------
/gpkitmodels/tools/xfoilWrapper.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from builtins import range
3 | import subprocess
4 | import numpy as np
5 | import scipy.optimize as spo
6 | import math
7 | import sys
8 | from gpkit.tests.helpers import NullFile
9 |
10 | def blind_call(topline, cl, Re, M, max_iter = 100,
11 | pathname = "/usr/local/bin/xfoil"):
12 |
13 | if '.dat' in topline:
14 | tl=topline.split()
15 | afile=tl[1]
16 | if '.txt' in topline:
17 | tl=topline.split()
18 | afile=tl[1]
19 |
20 | proc = subprocess.Popen([pathname], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
21 | proc.stdin.write(topline +
22 | 'oper \n' +
23 | "iter %d\n" %(max_iter)+
24 | 'visc \n' +
25 | "%.2e \n" %(Re) +
26 | "M \n" +
27 | "%.2f \n" %(M) +
28 | "a 2.0 \n" +
29 | "cl %.4f \n" %(cl) +
30 | '\n' +
31 | 'quit \n')
32 | stdout_val = proc.communicate()[0]
33 | proc.stdin.close()
34 |
35 | if ("VISCAL: Convergence failed\n" in stdout_val):
36 | return stdout_val
37 |
38 | res = {}
39 | if ("VISCAL: Convergence failed\n" not in stdout_val):
40 | ostr = stdout_val.split()
41 | ctr = 0
42 | for i in range(0,len(ostr)):
43 | ix = len(ostr)-(i+1)
44 | vl = ostr[ix]
45 | if vl in ['a','CL','CD','Cm']:
46 | res[vl] = ostr[ix + 2]
47 | ctr += 1
48 | if ctr >= 4:
49 | break
50 | cd = res['CD']
51 | cl = res['CL']
52 | # alpha_ret = res['a']
53 | cm = res['Cm']
54 | return float(cd), float(cl), float(cm), stdout_val
55 |
56 | def single_cl(CL, Re = 1e7, M = 0.0, airfoil=[], pathname = "/home/ckarcher/Xfoil/bin/./xfoil",
57 | number_of_samples = 51, sampling_min=-10, sampling_max=20, fitting_fraction = 1.4):
58 |
59 | num_samples = number_of_samples
60 | sample_min=sampling_min
61 | sample_max=sampling_max
62 |
63 | ls_res = subprocess.check_output(["ls -a"], shell = True)
64 | ls = ls_res.split()
65 |
66 | remove_kulfan = False
67 | if list(airfoil):
68 | if ('.dat' in airfoil) or ('.txt' in airfoil):
69 | topline = 'load ' + airfoil + ' \n afl \n'
70 | elif ('naca' == airfoil.lower()[0:4]) and (len(airfoil)==8):
71 | topline = airfoil + ' \n'
72 | else:
73 | print("Error: Invalid airfoil passed into XFOIL. Defaulting to a NACA0012.")
74 | topline = 'naca0012 \n'
75 |
76 | initial_list = np.linspace(sample_min,sample_max,num_samples).tolist()
77 | cd_calcl = []
78 | cl_calcl = []
79 | alpha_calcl = []
80 | cm_calcl = []
81 | for alpha in initial_list:
82 | # x = blind_call(topline, alpha, Re, M)
83 | try:
84 | x = blind_call(topline, alpha, Re, M)
85 | except:
86 | x=[1,1]
87 | if len(x)==5:
88 | cd_calcl.append(x[0])
89 | cl_calcl.append(x[1])
90 | alpha_calcl.append(x[2])
91 | cm_calcl.append(x[3])
92 | elif len(x)>10:
93 | pass
94 |
95 | cd_calc = np.asarray(cd_calcl)
96 | cl_calc = np.asarray(cl_calcl)
97 | alpha_calc = np.asarray(alpha_calcl)
98 | cm_calc = np.asarray(cm_calcl)
99 |
100 | vldidx = np.where(cl_calc<=max(cl_calc))
101 | cd_calc=cd_calc[vldidx[0].tolist()]
102 | cl_calc=cl_calc[vldidx[0].tolist()]
103 | alpha_calc=alpha_calc[vldidx[0].tolist()]
104 | cm_calc=cm_calc[vldidx[0].tolist()]
105 |
106 | vldidx2 = np.where(cl_calc>=0.0)
107 | cd_calc=cd_calc[vldidx2[0].tolist()]
108 | cl_calc=cl_calc[vldidx2[0].tolist()]
109 | alpha_calc=alpha_calc[vldidx2[0].tolist()]
110 | cm_calc=cm_calc[vldidx2[0].tolist()]
111 |
112 | p_cd = np.polyfit(np.append(-cl_calc,cl_calc), np.append(cd_calc,cd_calc),int(len(cl_calc)/fitting_fraction))
113 | p_alpha = np.polyfit(np.append(-cl_calc,cl_calc), np.append(alpha_calc,alpha_calc),int(len(cl_calc)/fitting_fraction))
114 | p_cm = np.polyfit(np.append(-cl_calc,cl_calc), np.append(cm_calc,cm_calc),int(len(cl_calc)/fitting_fraction))
115 |
116 | cd_guess = np.polyval(p_cd,CL)
117 | alpha_guess = np.polyval(p_alpha,CL)
118 | cm_guess = np.polyval(p_cm,CL)
119 |
120 | return cd_guess, CL, alpha_guess, cm_guess
121 |
--------------------------------------------------------------------------------
/researchmodeltests.sh:
--------------------------------------------------------------------------------
1 | export PIP=`which pip`
2 | echo $PIP
3 | python -c "from gpkit.tests.test_repo import test_repos; test_repos(xmloutput=True, ingpkitmodels=True)"
4 |
--------------------------------------------------------------------------------
/runtests.sh:
--------------------------------------------------------------------------------
1 | python -c "from gpkit.tests.from_paths import run; run()"
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """Standard Python setup script for GPkit Models"""
2 | from __future__ import print_function
3 | import sys
4 | from distutils.core import setup
5 | from setuptools import find_packages
6 |
7 | LONG_DESCRIPTION = """
8 | GPkit Models is a library of geometric programming and signomial programming
9 | models that can be manipulated and solved using
10 | `GPkit `_.
11 |
12 | `Documentation `_
13 |
14 | `Citing GPkit `_
15 | """
16 |
17 | LICENSE = """The MIT License (MIT)
18 |
19 | Copyright (c) 2015 MIT Hoburg Research Group
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy
22 | of this software and associated documentation files (the "Software"), to deal
23 | in the Software without restriction, including without limitation the rights
24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25 | copies of the Software, and to permit persons to whom the Software is
26 | furnished to do so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all
29 | copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | SOFTWARE."""
38 |
39 | setup(
40 | name="gpkitmodels",
41 | description="Library of geometric and signomial programming models "
42 | "that can be manipulated and solved using GPkit.",
43 | author="MIT Department of Aeronautics and Astronautics",
44 | author_email="gpkit@mit.edu",
45 | url="https://www.github.com/hoburg/gpkit-models",
46 | install_requires=["numpy>=1.12", "scipy", "pint", "future"],
47 | version="0.0.0.0",
48 | packages=find_packages(),
49 | package_data={"gpkitmodels": ["GP/aircraft/wing/*.csv",
50 | ("GP/aircraft/fuselage/"
51 | "fuselage_profile_drag/*.csv"),
52 | "GP/aircraft/engine/*.csv",
53 | "GP/aircraft/motor/*.csv",
54 | "GP/aircraft/prop/*.csv",
55 | "SP/aircraft/prop/*.csv",
56 | "GP/aircraft/engine/DF70/*.csv",
57 | "GP/aircraft/tail/*.csv"]},
58 | license=LICENSE,
59 | long_description=LONG_DESCRIPTION,
60 | )
61 |
--------------------------------------------------------------------------------