diff --git a/combined_decision.png b/combined_decision.png new file mode 100644 index 0000000..67197a1 Binary files /dev/null and b/combined_decision.png differ diff --git a/decision_recommendation.png b/decision_recommendation.png new file mode 100644 index 0000000..0dd6828 Binary files /dev/null and b/decision_recommendation.png differ diff --git a/p1/README.md b/p1/README.md index dc7c631..52ccccf 100644 --- a/p1/README.md +++ b/p1/README.md @@ -23,6 +23,8 @@ 纯火箭方案(10 发射场 * 每日 1 发)排布与能量-工期曲线;并做发射频率敏感性分析。 - **`combined_scenario.py`** 混合方案(电梯优先 + 火箭补齐),输出组合方案能量-工期曲线,并生成 **100–300 年**区间三方案同图对比。 +- **`../pareto_optimization.py`**(新增) + **Pareto 前沿 + “膝点(knee point)”**分析:在**组合方案可行区间(约 101–186 年)**内,寻找“时间–能量”双目标权衡的拐点,并输出推荐决策点与图表/CSV。 - **`launch_capacity_analysis.py`** (可选)对历史火箭年发射量做 Logistic / Gompertz / Richards 拟合与预测,生成 `launch_capacity_*.png` 与 `launch_capacity_results.csv`。 @@ -38,6 +40,8 @@ | `combined_scenario_analysis.png` | 混合方案:总能量/电梯能量/火箭能量随工期变化 + 电梯占比 + 需要启用的火箭站点数 | | `scenario_comparison.png` | elevator-only / rocket-only / combined 的完工时间与能量柱状对比 | | **`three_scenarios_comparison.png`** | **100–300 年区间**:纯火箭、纯电梯、混合方案的“年份-能量”折线同图对比 | +| **`../pareto_combined_range.png`** | **组合方案(101–186 年)Pareto 前沿 + 膝点检测**(多方法对比、边际收益、构成变化) | +| **`../combined_decision.png`** | 组合方案(101–186 年)推荐拐点的“决策图”(更适合直接放报告) | | `launch_capacity_analysis.png` `launch_capacity_physical.png` `launch_capacity_predictions.png` | (可选)火箭发射能力上限/趋势拟合与预测 | #### 数据文件(如存在) @@ -126,7 +130,22 @@
-5) 其它历史遗留图 +5) Pareto 前沿与“膝点”(组合方案 101–186 年) + +> 说明:`pareto_optimization.py` 当前将图片/CSV 输出到上一级目录(`../`),因此这里用相对路径引用。 + +#### `../pareto_combined_range.png` + +![pareto_combined_range](../pareto_combined_range.png) + +#### `../combined_decision.png` + +![combined_decision](../combined_decision.png) + +
+ +
+6) 其它历史遗留图 本目录还包含 `earth_moon_transfer_analysis.png`(早期“地月转移:载荷–燃料/能量关系”图)。当前目录下未发现对应的 `earth_moon_transfer.py` 源码文件;如果你需要我把该脚本补回并与现有口径统一,我可以再加一个文件。 @@ -271,6 +290,33 @@ Rocket Only (219 years): Total energy: 50609 PJ - 当工期 ≥ 186 年时,电梯即可独立完成,混合方案会自动退化为纯电梯曲线(能耗趋于同一水平)。 - 纯火箭能耗显著更高(本模型下约为纯电梯的 **3×** 量级)。 +#### 4)Pareto 前沿与“膝点”(只在组合方案 101–186 年区间内找拐点) + +> 这个分析回答“在可行区间内,选择多少年最划算”的决策问题:既不想太慢、也不想能耗太高。 +> 我们把目标写成双目标:**最小化完工时间 \(T\)** 与 **最小化总能量 \(E\)**,并在 \(T\in[101, 186]\) 年上寻找“边际收益开始明显递减”的拐点(knee point)。 + +从本目录运行: + +```bash +python ../pareto_optimization.py +``` + +会生成(输出在上一级目录 `../`): + +- `../pareto_combined_range.png` +- `../combined_decision.png` +- `../pareto_combined_range.csv`(组合区间的 Pareto 数据) + +**组合区间(101–186 年)推荐膝点(示例输出)**: + +| 推荐方法 | 年份 | 总能量 (PJ) | 电梯占比 | 火箭占比 | +|---|---:|---:|---:|---:| +| 最大距离法(最稳健) | **139** | **24361** | **74.6%** | **25.4%** | + +解读要点: +- **101→139 年**:多给一些时间,能量下降很快(大量火箭任务转移到电梯/低纬火箭站)。 +- **139→186 年**:继续拉长工期仍能省能量,但**边际节省明显变小**;186 年达到纯电梯可独立完成的极限点。 + --- ### 发射场(10 个)与纬度效应(月球任务场景) @@ -354,4 +400,5 @@ Rocket Only (219 years): Total energy: 50609 PJ - **纯火箭**在“10 站 * 每日 1 发 * 125 吨/发”的约束下,最短约 **219 年**,且能耗约 **5×10⁴ PJ** 量级。 - **纯电梯**能耗最低(约 **1.57×10⁴ PJ**),但最短约 **186 年**。 - **混合方案**能把工期压到约 **101 年**,能耗介于两者之间,并随着工期增加逐步趋近纯电梯能耗。 +- **在混合方案可行区间(101–186 年)内**,用 Pareto/膝点法可给出一个“最划算”的折中点:本模型下约 **139 年**(约 **2.44×10⁴ PJ**,电梯约 **75%**、火箭约 **25%**)。 diff --git a/pareto_combined_range.csv b/pareto_combined_range.csv new file mode 100644 index 0000000..798e4a9 --- /dev/null +++ b/pareto_combined_range.csv @@ -0,0 +1,301 @@ +years,energy_PJ,elevator_fraction,sites_used,rocket_launches +101.0,31679.76095710881,0.54237,10,366104 +101.28501585047614,31620.609850415538,0.5439005351170569,10,364880 +101.57003170095227,31561.45874372226,0.5454310702341137,10,363656 +101.85504755142841,31502.241823054243,0.5469616053511706,10,362431 +102.14006340190454,31443.090716360963,0.5484921404682274,10,361207 +102.42507925238068,31383.87379569294,0.5500226755852843,10,359982 +102.71009510285683,31324.722688999667,0.5515532107023411,10,358758 +102.99511095333295,31265.571582306387,0.5530837458193979,10,357534 +103.2801268038091,31206.354661638365,0.5546142809364548,10,356309 +103.56514265428524,31147.203554945085,0.5561448160535117,10,355085 +103.85015850476137,31087.98663427707,0.5576753511705685,10,353860 +104.13517435523751,31028.835527583797,0.5592058862876255,10,352636 +104.42019020571364,30969.618606915767,0.5607364214046823,10,351411 +104.70520605618978,30910.467500222494,0.5622669565217392,10,350187 +104.99022190666592,30851.316393529218,0.563797491638796,10,348963 +105.27523775714205,30792.09947286119,0.5653280267558528,10,347738 +105.5602536076182,30733.320588222938,0.5668585618729096,9,346514 +105.84526945809434,30677.360233793443,0.5683890969899665,9,345289 +106.13028530857046,30621.464186366808,0.5699196321070233,9,344065 +106.4153011590466,30565.5038319373,0.5714501672240803,9,342840 +106.70031700952273,30509.60778451066,0.5729807023411371,9,341616 +106.98533285999888,30453.711737084028,0.574511237458194,9,340392 +107.27034871047502,30397.751382654526,0.5760417725752509,9,339167 +107.55536456095115,30341.855335227887,0.5775723076923077,9,337943 +107.84038041142729,30285.894980798384,0.5791028428093645,9,336718 +108.12539626190343,30229.99893337175,0.5806333779264214,9,335494 +108.41041211237956,30174.038578942243,0.5821639130434783,9,334269 +108.6954279628557,30118.14253151561,0.5836944481605351,9,333045 +108.98044381333183,30062.24648408897,0.5852249832775919,9,331821 +109.26545966380797,30006.28612965946,0.5867555183946488,9,330596 +109.55047551428412,29950.39008223283,0.5882860535117057,9,329372 +109.83549136476024,29894.429727803326,0.5898165886287625,9,328147 +110.12050721523639,29838.53368037669,0.5913471237458194,9,326923 +110.40552306571252,29782.561361639895,0.5928776588628762,9,325698 +110.69053891618866,29726.665314213256,0.5944081939799332,9,324474 +110.9755547666648,29671.31387329956,0.59593872909699,8,323250 +111.26057061714093,29616.757347939132,0.5974692642140468,8,322025 +111.54558646761707,29562.264447117264,0.5989997993311036,8,320801 +111.83060231809321,29507.707921756835,0.6005303344481605,8,319576 +112.11561816856934,29453.21502093497,0.6020608695652173,8,318352 +112.40063401904548,29398.658495574542,0.6035914046822742,8,317127 +112.68564986952163,29344.16559475267,0.6051219397993312,8,315903 +112.97066571999775,29289.672693930803,0.606652474916388,8,314679 +113.2556815704739,29235.116168570374,0.6081830100334449,8,313454 +113.54069742095002,29180.623267748502,0.6097135451505017,8,312230 +113.82571327142617,29126.066742388073,0.6112440802675585,8,311005 +114.11072912190231,29071.573841566213,0.6127746153846154,8,309781 +114.39574497237844,29017.017316205773,0.6143051505016722,8,308556 +114.68076082285458,28962.524415383912,0.615835685618729,8,307332 +114.96577667333071,28908.031514562044,0.6173662207357858,8,306108 +115.25079252380685,28853.474989201608,0.6188967558528428,8,304883 +115.535808374283,28798.982088379744,0.6204272909698997,8,303659 +115.82082422475912,28744.425563019315,0.6219578260869565,8,302434 +116.10584007523526,28689.932662197443,0.6234883612040134,8,301210 +116.3908559257114,28635.376136837014,0.6250188963210702,8,299985 +116.67587177618753,28580.88323601515,0.6265494314381271,8,298761 +116.96088762666368,28526.45534360956,0.6280799665551839,7,297537 +117.24590347713982,28471.997009615712,0.6296105016722408,7,296312 +117.53091932761595,28417.60224988323,0.6311410367892976,7,295088 +117.81593517809209,28363.14391588938,0.6326715719063545,7,293863 +118.10095102856822,28308.7491561569,0.6342021070234113,7,292639 +118.38596687904436,28254.290822163046,0.6357326421404682,7,291414 +118.67098272952049,28199.896062430566,0.637263177257525,7,290190 +118.95599857999663,28145.50130269808,0.6387937123745819,7,288966 +119.24101443047277,28091.042968704238,0.6403242474916389,7,287741 +119.5260302809489,28036.64205631926,0.6418547826086957,7,286517 +119.81104613142504,27982.18372232541,0.6433853177257525,7,285292 +120.09606198190119,27927.78896259293,0.6449158528428094,7,284068 +120.38107783237731,27873.330628599077,0.6464463879598662,7,282843 +120.66609368285346,27818.935868866593,0.647976923076923,7,281619 +120.9511095333296,27764.541109134116,0.6495074581939799,7,280395 +121.23612538380573,27710.08277514026,0.6510379933110367,7,279170 +121.52114123428187,27655.68801540778,0.6525685284280937,7,277946 +121.806157084758,27601.22968141393,0.6540990635451505,7,276721 +122.09117293523414,27546.834921681453,0.6556295986622074,7,275497 +122.37618878571027,27492.3765876876,0.6571601337792642,7,274272 +122.66120463618641,27437.981827955115,0.6586906688963211,7,273048 +122.94622048666255,27383.587068222638,0.6602212040133779,7,271824 +123.23123633713868,27329.128734228783,0.6617517391304347,7,270599 +123.51625218761482,27274.84492702738,0.6632822742474915,6,269375 +123.80126803809097,27220.569274449346,0.6648128093645485,6,268150 +124.0862838885671,27166.357097332555,0.6663433444816053,6,266926 +124.37129973904324,27112.081444754524,0.6678738795986622,6,265701 +124.65631558951938,27057.869267637732,0.6694044147157191,6,264477 +124.94133143999551,27003.65709052094,0.6709349498327759,6,263253 +125.22634729047165,26949.381437942906,0.6724654849498327,6,262028 +125.5113631409478,26895.169260826126,0.6739960200668896,6,260804 +125.79637899142392,26840.893608248083,0.6755265551839464,6,259579 +126.08139484190006,26786.681431131296,0.6770570903010034,6,258355 +126.36641069237619,26732.405778553257,0.6785876254180602,6,257130 +126.65142654285233,26678.193601436476,0.6801181605351171,6,255906 +126.93644239332846,26623.981424319685,0.6816486956521739,6,254682 +127.2214582438046,26569.705771741646,0.6831792307692307,6,253457 +127.50647409428075,26515.49359462486,0.6847097658862875,6,252233 +127.79148994475688,26461.21794204682,0.6862403010033443,6,251008 +128.076505795233,26407.005764930036,0.6877708361204011,6,249784 +128.36152164570916,26352.730112351997,0.6893013712374582,6,248559 +128.6465374961853,26298.51793523521,0.690831906354515,6,247335 +128.93155334666142,26244.300198266632,0.6923624414715718,6,246111 +129.21656919713757,26190.024545688593,0.6938929765886287,6,244886 +129.5015850476137,26135.81236857181,0.6954235117056855,6,243662 +129.78660089808983,26081.536715993767,0.6969540468227424,6,242437 +130.071616748566,26027.324538876983,0.6984845819397993,6,241213 +130.35663259904211,25973.048886298948,0.7000151170568562,6,239988 +130.64164844951824,25918.836709182156,0.701545652173913,6,238764 +130.9266642999944,25865.032259204538,0.70307618729097,5,237540 +131.21168015047053,25811.2648093821,0.7046067224080268,5,236315 +131.49669600094666,25757.56054378724,0.7061372575250836,5,235091 +131.7817118514228,25703.793093964807,0.7076677926421405,5,233866 +132.06672770189894,25650.088828369953,0.7091983277591973,5,232642 +132.35174355237507,25596.321378547516,0.7107288628762541,5,231417 +132.63675940285123,25542.61711295266,0.7122593979933112,5,230193 +132.92177525332735,25488.912847357806,0.713789933110368,5,228969 +133.20679110380348,25435.14539753536,0.7153204682274248,5,227744 +133.49180695427964,25381.441131940504,0.7168510033444816,5,226520 +133.77682280475577,25327.673682118068,0.7183815384615384,5,225295 +134.0618386552319,25273.96941652321,0.7199120735785952,5,224071 +134.34685450570802,25220.201966700773,0.721442608695652,5,222846 +134.63187035618418,25166.497701105916,0.722973143812709,5,221622 +134.9168862066603,25112.793435511063,0.7245036789297659,5,220398 +135.20190205713644,25059.025985688626,0.7260342140468227,5,219173 +135.48691790761256,25005.321720093765,0.7275647491638795,5,217949 +135.77193375808872,24951.554270271332,0.7290952842809364,5,216724 +136.05694960856485,24897.85000467648,0.7306258193979932,5,215500 +136.34196545904098,24844.082554854034,0.73215635451505,5,214275 +136.62698130951713,24790.378289259184,0.733686889632107,5,213051 +136.91199715999326,24736.67402366432,0.7352174247491639,5,211827 +137.1970130104694,24682.906573841883,0.7367479598662207,5,210602 +137.48202886094555,24629.202308247033,0.7382784949832776,5,209378 +137.76704471142168,24575.434858424593,0.7398090301003344,5,208153 +138.0520605618978,24521.726489146295,0.7413395652173913,5,206929 +138.33707641237396,24467.95903932386,0.7428701003344482,5,205704 +138.6220922628501,24414.254773729,0.744400635451505,5,204480 +138.90710811332622,24360.550508134147,0.7459311705685618,5,203256 +139.19212396380237,24307.39243233035,0.7474617056856189,4,202031 +139.4771398142785,24254.528682623262,0.7489922408026755,4,200807 +139.76215566475463,24201.602261198288,0.7505227759197323,4,199582 +140.0471715152308,24148.738511491203,0.7520533110367893,4,198358 +140.33218736570691,24095.812090066232,0.7535838461538461,4,197133 +140.61720321618304,24042.948340359148,0.7551143812709029,4,195909 +140.9022190666592,23990.084590652063,0.7566449163879598,4,194685 +141.18723491713533,23937.158169227092,0.7581754515050166,4,193460 +141.47225076761146,23884.29441952001,0.7597059866220734,4,192236 +141.7572666180876,23831.367998095036,0.7612365217391305,4,191011 +142.04228246856374,23778.50424838795,0.7627670568561873,4,189787 +142.32729831903987,23725.577826962977,0.7642975919732441,4,188562 +142.612314169516,23672.714077255892,0.7658281270903009,4,187338 +142.89733001999215,23619.850327548815,0.7673586622073579,4,186114 +143.18234587046828,23566.92390612384,0.7688891973244147,4,184889 +143.4673617209444,23514.060156416752,0.7704197324414715,4,183665 +143.75237757142054,23461.133734991778,0.7719502675585284,4,182440 +144.0373934218967,23408.269985284693,0.7734808026755853,4,181216 +144.32240927237282,23355.343563859722,0.7750113377926421,4,179991 +144.60742512284895,23302.479814152637,0.7765418729096989,4,178767 +144.8924409733251,23249.616064445556,0.7780724080267559,4,177543 +145.17745682380124,23196.68964302058,0.7796029431438126,4,176318 +145.46247267427736,23143.825893313497,0.7811334782608694,4,175094 +145.74748852475352,23090.899471888522,0.7826640133779263,4,173869 +146.03250437522965,23038.035722181445,0.7841945484949832,4,172645 +146.31752022570578,22985.109300756463,0.78572508361204,4,171420 +146.60253607618193,22932.245551049386,0.787255618729097,4,170196 +146.88755192665806,22879.3818013423,0.7887861538461538,4,168972 +147.1725677771342,22826.455379917323,0.7903166889632106,4,167747 +147.45758362761035,22773.589576565555,0.7918472240802675,4,166523 +147.74259947808648,22720.66315514058,0.7933777591973243,4,165298 +148.0276153285626,22667.799405433496,0.7949082943143811,4,164074 +148.31263117903876,22614.872984008525,0.7964388294314382,4,162849 +148.5976470295149,22562.204830829458,0.797969364548495,3,161625 +148.88266287999102,22509.616963828565,0.7994998996655518,3,160401 +149.16767873046717,22456.966604720914,0.8010304347826087,3,159176 +149.4526945809433,22404.378737720017,0.8025609698996655,3,157952 +149.73771043141943,22351.728378612366,0.8040915050167223,3,156727 +150.0227262818956,22299.140511611473,0.8056220401337794,3,155503 +150.30774213237171,22246.490152503826,0.8071525752508362,3,154278 +150.59275798284784,22193.902285502925,0.808683110367893,3,153054 +150.87777383332397,22141.314418502032,0.8102136454849498,3,151830 +151.16278968380013,22088.66405939438,0.8117441806020066,3,150605 +151.44780553427626,22036.076192393488,0.8132747157190634,3,149381 +151.73282138475238,21983.425833285837,0.8148052508361202,3,148156 +152.0178372352285,21930.83796628494,0.816335785953177,3,146932 +152.30285308570467,21878.18760717729,0.817866321070234,3,145707 +152.5878689361808,21825.599740176393,0.8193968561872909,3,144483 +152.87288478665693,21773.011873175496,0.8209273913043477,3,143259 +153.15790063713308,21720.36151406785,0.8224579264214046,3,142034 +153.4429164876092,21667.77364706695,0.8239884615384614,3,140810 +153.72793233808534,21615.1232879593,0.8255189966555183,3,139585 +154.0129481885615,21562.535420958407,0.8270495317725752,3,138361 +154.29796403903762,21509.885061850757,0.828580066889632,3,137136 +154.58297988951375,21457.297194849863,0.8301106020066888,3,135912 +154.8679957399899,21404.709327848966,0.8316411371237459,3,134688 +155.15301159046604,21352.058968741316,0.8331716722408027,3,133463 +155.43802744094216,21299.471101740422,0.8347022073578595,3,132239 +155.72304329141832,21246.82074263277,0.8362327424749164,3,131014 +156.00805914189445,21194.232875631875,0.8377632775919732,3,129790 +156.29307499237058,21141.582516524224,0.83929381270903,3,128565 +156.57809084284673,21088.993134712055,0.8408243478260871,3,127341 +156.86310669332286,21036.405267711158,0.8423548829431439,3,126117 +157.148122543799,20983.754908603503,0.8438854180602006,3,124892 +157.43313839427515,20931.16704160261,0.8454159531772575,3,123668 +157.71815424475128,20878.51668249496,0.8469464882943143,3,122443 +158.0031700952274,20825.928815494062,0.8484770234113711,3,121219 +158.28818594570356,20773.27845638641,0.8500075585284281,3,119994 +158.5732017961797,20720.69058938552,0.8515380936454849,3,118770 +158.85821764665582,20668.10272238462,0.8530686287625417,3,117546 +159.14323349713194,20615.45236327697,0.8545991638795986,3,116321 +159.4282493476081,20563.69918904202,0.8561296989966555,2,115097 +159.71326519808423,20511.979658520824,0.8576602341137123,2,113872 +159.99828104856036,20460.321970540033,0.8591907692307691,2,112648 +160.2832968990365,20408.602440018836,0.8607213043478259,2,111423 +160.56831274951264,20356.944752038045,0.8622518394648829,2,110199 +160.85332859998877,20305.28706405725,0.8637823745819397,2,108975 +161.1383444504649,20253.567533536054,0.8653129096989965,2,107750 +161.42336030094106,20201.909845555263,0.8668434448160535,2,106526 +161.70837615141718,20150.190315034066,0.8683739799331103,2,105301 +161.9933920018933,20098.53262705327,0.8699045150501672,2,104077 +162.27840785236947,20046.81309653208,0.8714350501672241,2,102852 +162.5634237028456,19995.155408551283,0.8729655852842809,2,101628 +162.84843955332173,19943.497720570493,0.8744961204013376,2,100404 +163.13345540379788,19891.778190049296,0.8760266555183945,2,99179 +163.418471254274,19840.1205020685,0.8775571906354513,2,97955 +163.70348710475014,19788.400971547308,0.8790877257525082,2,96730 +163.9885029552263,19736.743283566517,0.8806182608695652,2,95506 +164.27351880570242,19685.02375304532,0.882148795986622,2,94281 +164.55853465617855,19633.366065064525,0.8836793311036788,2,93057 +164.8435505066547,19581.708377083734,0.8852098662207357,2,91833 +165.12856635713084,19529.98884656254,0.8867404013377925,2,90608 +165.41358220760696,19478.331158581743,0.8882709364548494,2,89384 +165.69859805808312,19426.611628060553,0.8898014715719064,2,88159 +165.98361390855925,19374.953724401188,0.8913320066889632,2,86935 +166.26862975903538,19323.234193879995,0.89286254180602,2,85710 +166.55364560951153,19271.576505899204,0.894393076923077,2,84486 +166.83866145998763,19219.918817918406,0.8959236120401336,2,83262 +167.1236773104638,19168.199287397212,0.8974541471571906,2,82037 +167.40869316093995,19116.54159941642,0.8989846822742475,2,80813 +167.69370901141605,19064.822068895224,0.9005152173913042,2,79588 +167.9787248618922,19013.16438091443,0.9020457525083612,2,78364 +168.26374071236836,18961.44485039324,0.9035762876254182,2,77139 +168.54875656284446,18909.787162412442,0.9051068227424748,2,75915 +168.83377241332062,18858.129474431647,0.9066373578595316,2,74691 +169.11878826379674,18806.409943910454,0.9081678929765885,2,73466 +169.40380411427287,18754.75225592966,0.9096984280936453,2,72242 +169.68881996474903,18703.032725408466,0.9112289632107022,2,71017 +169.97383581522516,18651.37503742767,0.912759498327759,2,69793 +170.2588516657013,18599.655506906478,0.9142900334448159,2,68568 +170.54386751617744,18547.997818925687,0.9158205685618729,2,67344 +170.82888336665357,18496.34013094489,0.9173511036789297,2,66120 +171.1138992171297,18444.620600423696,0.9188816387959865,2,64895 +171.39891506760586,18392.962912442905,0.9204121739130434,2,63671 +171.68393091808198,18341.290399849782,0.9219427090301002,1,62446 +171.9689467685581,18289.91913300881,0.923473244147157,1,61222 +172.25396261903427,18238.48623930601,0.9250037792642141,1,59997 +172.5389784695104,18187.11497246504,0.9265343143812709,1,58773 +172.82399431998653,18135.743705624067,0.9280648494983277,1,57549 +173.10901017046268,18084.31081192127,0.9295953846153846,1,56324 +173.3940260209388,18032.9395450803,0.9311259197324414,1,55100 +173.67904187141494,17981.506651377495,0.9326564548494982,1,53875 +173.9640577218911,17930.135384536527,0.9341869899665552,1,52651 +174.24907357236722,17878.702490833723,0.9357175250836121,1,51426 +174.53408942284335,17827.33122399275,0.9372480602006688,1,50202 +174.8191052733195,17775.959957151783,0.9387785953177257,1,48978 +175.1041211237956,17724.52706344898,0.9403091304347824,1,47753 +175.38913697427176,17673.155796608007,0.9418396655518393,1,46529 +175.67415282474792,17621.72290290521,0.9433702006688963,1,45304 +175.95916867522402,17570.351636064235,0.9449007357859529,1,44080 +176.24418452570018,17518.918742361435,0.9464312709030099,1,42855 +176.52920037617633,17467.547475520467,0.9479618060200669,1,41631 +176.81421622665243,17416.176208679495,0.9494923411371236,1,40407 +177.0992320771286,17364.74331497669,0.9510228762541805,1,39182 +177.38424792760472,17313.37204813572,0.9525534113712374,1,37958 +177.66926377808085,17261.93915443292,0.9540839464882942,1,36733 +177.954279628557,17210.56788759195,0.9556144816053511,1,35509 +178.23929547903313,17159.134993889147,0.9571450167224079,1,34284 +178.52431132950926,17107.76372704818,0.9586755518394647,1,33060 +178.80932717998542,17056.392460207207,0.9602060869565218,1,31836 +179.09434303046154,17004.959566504403,0.9617366220735786,1,30611 +179.37935888093767,16953.588299663435,0.9632671571906354,1,29387 +179.66437473141383,16902.155405960635,0.9647976923076923,1,28162 +179.94939058188996,16850.784139119663,0.9663282274247491,1,26938 +180.2344064323661,16799.35124541686,0.9678587625418059,1,25713 +180.51942228284224,16747.97997857589,0.9693892976588627,1,24489 +180.80443813331837,16696.60871173492,0.9709198327759195,1,23265 +181.0894539837945,16645.175818032116,0.9724503678929765,1,22040 +181.37446983427066,16593.804551191148,0.9739809030100334,1,20816 +181.65948568474678,16542.371657488344,0.9755114381270902,1,19591 +181.9445015352229,16491.000390647372,0.977041973244147,1,18367 +182.22951738569907,16439.567496944575,0.978572508361204,1,17142 +182.5145332361752,16388.196230103604,0.9801030434782608,1,15918 +182.79954908665133,16336.824963262632,0.9816335785953176,1,14694 +183.08456493712748,16285.392069559832,0.9831641137123746,1,13469 +183.36958078760358,16234.02080271886,0.9846946488294313,1,12245 +183.65459663807974,16182.587909016058,0.9862251839464882,1,11020 +183.9396124885559,16131.21664217509,0.9877557190635452,1,9796 +184.224628339032,16079.783748472286,0.9892862541806018,1,8571 +184.50964418950815,16028.412481631316,0.9908167892976588,1,7347 +184.7946600399843,15977.041214790348,0.9923473244147157,1,6123 +185.0796758904604,15925.608321087544,0.9938778595317724,1,4898 +185.36469174093656,15874.237054246574,0.9954083946488294,1,3674 +185.6497075914127,15822.80416054377,0.9969389297658863,1,2449 +185.93472344188882,15771.432893702802,0.9984694648829431,1,1225 +186.21973929236498,15720.0,1.0,0,0 diff --git a/pareto_combined_range.png b/pareto_combined_range.png new file mode 100644 index 0000000..26756df Binary files /dev/null and b/pareto_combined_range.png differ diff --git a/pareto_front_data.csv b/pareto_front_data.csv new file mode 100644 index 0000000..41d47ac --- /dev/null +++ b/pareto_front_data.csv @@ -0,0 +1,499 @@ +years,energy_PJ,elevator_fraction,sites_used,rocket_launches +100.80160320641282,31721.015809442823,0.5413046092184368,10,366957 +101.20240480961924,31637.791423463375,0.5434569138276553,10,365235 +101.60320641282566,31554.5415104297,0.5456092184368738,10,363513 +102.00400801603206,31471.317124450256,0.5477615230460922,10,361791 +102.40480961923848,31388.092738470812,0.5499138276553106,10,360069 +102.8056112224449,31304.908639411875,0.552066132264529,10,358348 +103.2064128256513,31221.684253432435,0.5542184368737475,10,356626 +103.60721442885772,31138.459867452988,0.556370741482966,10,354904 +104.00801603206413,31055.235481473548,0.5585230460921844,10,353182 +104.40881763527054,30971.98556843986,0.5606753507014028,10,351460 +104.80961923847696,30888.761182460417,0.5628276553106213,10,349738 +105.21042084168337,30805.602610455724,0.5649799599198397,10,348017 +105.61122244488978,30723.31263851132,0.5671322645290581,9,346295 +106.0120240480962,30644.663419167766,0.5692845691382766,9,344573 +106.41282565130261,30566.014199824203,0.5714368737474951,9,342851 +106.81362725450902,30487.364980480637,0.5735891783567134,9,341129 +107.21442885771543,30408.703796829792,0.5757414829659319,9,339407 +107.61523046092185,30330.054577486233,0.5778937875751503,9,337685 +108.01603206412825,30251.469665145545,0.5800460921843688,9,335964 +108.41683366733467,30172.808481494696,0.5821983967935872,9,334242 +108.81763527054107,30094.15926215113,0.5843507014028057,9,332520 +109.21843687374749,30015.510042807575,0.5865030060120241,9,330798 +109.61923847695391,29936.84885915672,0.5886553106212425,9,329076 +110.02004008016033,29858.19963981316,0.590807615230461,9,327354 +110.42084168336673,29779.614727472468,0.5929599198396793,9,325633 +110.82164328657315,29700.965508128913,0.5951122244488979,9,323911 +111.22244488977955,29624.06439991274,0.5972645290581162,8,322189 +111.62324649298597,29547.38750240176,0.5994168336673347,8,320467 +112.02404809619239,29470.710604890784,0.6015691382765531,8,318745 +112.4248496993988,29394.027202786925,0.6037214428857716,8,317023 +112.82565130260521,29317.41392981451,0.60587374749499,8,315302 +113.22645290581163,29240.73703230353,0.6080260521042085,8,313580 +113.62725450901803,29164.060134792555,0.6101783567134269,8,311858 +114.02805611222445,29087.376732688695,0.6123306613226454,8,310136 +114.42885771543087,29010.699835177722,0.6144829659318638,8,308414 +114.82965931863727,28934.02293766674,0.6166352705410821,8,306692 +115.23046092184369,28857.33953556288,0.6187875751503006,8,304970 +115.6312625250501,28780.726262590473,0.620939879759519,8,303249 +116.03206412825651,28704.049365079496,0.6230921843687374,8,301527 +116.43286573146293,28627.37246756852,0.6252444889779559,8,299805 +116.83366733466934,28550.71043327357,0.6273967935871744,7,298083 +117.23446893787576,28474.171496392344,0.6295490981963928,7,296361 +117.63527054108216,28397.63255951111,0.6317014028056112,7,294639 +118.03607214428858,28321.151044238755,0.6338537074148296,7,292918 +118.43687374749499,28244.612107357527,0.636006012024048,7,291196 +118.8376753507014,28168.073170476295,0.6381583166332665,7,289474 +119.23847695390782,28091.52808094257,0.640310621242485,7,287752 +119.63927855711422,28014.989144061346,0.6424629258517034,7,286030 +120.04008016032064,27938.45020718012,0.6446152304609218,7,284308 +120.44088176352706,27861.911270298886,0.6467675350701403,7,282586 +120.84168336673346,27785.429755026526,0.6489198396793586,7,280865 +121.24248496993988,27708.890818145297,0.6510721442885772,7,279143 +121.6432865731463,27632.351881264072,0.6532244488977956,7,277421 +122.0440881763527,27555.80679173035,0.655376753507014,7,275699 +122.44488977955912,27479.267854849113,0.6575290581162324,7,273977 +122.84569138276554,27402.72891796789,0.659681362725451,7,272255 +123.24649298597194,27326.25355534803,0.6618336673346693,7,270534 +123.64729458917836,27249.903497244704,0.6639859719438878,6,268812 +124.04809619238478,27173.62124306671,0.6661382765531062,6,267090 +124.44889779559118,27097.338988888714,0.6682905811623246,6,265368 +124.8496993987976,27021.051174858923,0.6704428857715431,6,263646 +125.250501002004,26944.76892068093,0.6725951903807614,6,261924 +125.65130260521042,26868.55014196419,0.67474749498998,6,260203 +126.05210420841684,26792.262327934397,0.6768997995991984,6,258481 +126.45290581162325,26715.980073756407,0.6790521042084169,6,256759 +126.85370741482966,26639.69781957841,0.6812044088176353,6,255037 +127.25450901803607,26563.415565400413,0.6833567134268537,6,253315 +127.65531062124248,26487.127751370626,0.6855090180360721,6,251593 +128.0561122244489,26410.84549719263,0.6876613226452907,6,249871 +128.4569138276553,26334.626718475887,0.689813627254509,6,248150 +128.85771543086173,26258.3389044461,0.6919659318637276,6,246428 +129.25851703406815,26182.0566502681,0.6941182364729459,6,244706 +129.65931863727454,26105.774396090103,0.6962705410821642,6,242984 +130.06012024048096,26029.49214191211,0.6984228456913827,6,241262 +130.46092184368737,25953.20432788232,0.7005751503006011,6,239540 +130.8617234468938,25877.277074070076,0.7027274549098197,5,237819 +131.2625250501002,25801.708924852963,0.704879759519038,5,236097 +131.66332665330663,25726.13667195241,0.7070320641282566,5,234375 +132.06412825651302,25650.568522735288,0.7091843687374748,5,232653 +132.46492985971943,25575.00037351817,0.7113366733466934,5,230931 +132.86573146292585,25499.432224301057,0.7134889779559117,5,229209 +133.26653306613227,25423.8599714005,0.7156412825651303,5,227487 +133.6673346693387,25348.355006410962,0.7177935871743487,5,225766 +134.06813627254508,25272.78685719384,0.719945891783567,5,224044 +134.46893787575152,25197.214604293287,0.7220981963927856,5,222322 +134.8697394789579,25121.646455076167,0.7242505010020039,5,220600 +135.27054108216433,25046.078305859057,0.7264028056112224,5,218878 +135.67134268537075,24970.506052958503,0.728555110220441,5,217156 +136.07214428857716,24895.00108796896,0.7307074148296593,5,215435 +136.47294589178358,24819.432938751845,0.7328597194388778,5,213713 +136.87374749498997,24743.864789534724,0.7350120240480961,5,211991 +137.2745490981964,24668.29253663417,0.7371643286573146,5,210269 +137.6753507014028,24592.724387417056,0.739316633266533,5,208547 +138.07615230460922,24517.15623819994,0.7414689378757515,5,206825 +138.47695390781564,24441.647169526965,0.74362124248497,5,205104 +138.87775551102203,24366.079020309848,0.7457735470941883,5,203382 +139.27855711422845,24291.37393740676,0.7479258517034068,4,201660 +139.67935871743487,24216.987635529404,0.7500781563126253,4,199938 +140.08016032064128,24142.599280007358,0.7522304609218436,4,198216 +140.4809619238477,24068.212978130003,0.7543827655310622,4,196494 +140.88176352705412,23993.826676252647,0.7565350701402805,4,194772 +141.28256513026054,23919.500992448495,0.7586873747494991,4,193051 +141.68336673346693,23845.114690571136,0.7608396793587173,4,191329 +142.08416833667334,23770.728388693777,0.7629919839679359,4,189607 +142.48496993987976,23696.340033171735,0.7651442885771543,4,187885 +142.88577154308618,23621.95373129438,0.7672965931863728,4,186163 +143.2865731462926,23547.567429417024,0.7694488977955912,4,184441 +143.68737474949899,23473.243799257554,0.7716012024048096,4,182720 +144.0881763527054,23398.855443735512,0.773753507014028,4,180998 +144.48897795591182,23324.469141858157,0.7759058116232466,4,179276 +144.88977955911824,23250.0828399808,0.7780581162324649,4,177554 +145.29058116232466,23175.69448445876,0.7802104208416835,4,175832 +145.69138276553107,23101.308182581404,0.7823627254509018,4,174110 +146.0921843687375,23026.92188070405,0.7845150300601204,4,172388 +146.49298597194388,22952.59825054458,0.7866673346693386,4,170667 +146.8937875751503,22878.209895022537,0.7888196392785571,4,168945 +147.29458917835672,22803.82359314518,0.7909719438877756,4,167223 +147.69539078156313,22729.437291267826,0.793124248496994,4,165501 +148.09619238476955,22655.04893574578,0.7952765531062125,4,163779 +148.49699398797594,22580.760701549138,0.7974288577154308,3,162057 +148.89779559118236,22506.82485183411,0.7995811623246493,3,160336 +149.29859719438878,22432.826510012335,0.8017334669338678,3,158614 +149.6993987975952,22358.826653379274,0.8038857715430862,3,156892 +150.1002004008016,22284.828311557496,0.8060380761523047,3,155170 +150.501002004008,22210.829969735714,0.8081903807615229,3,153448 +150.90180360721445,22136.83011310266,0.8103426853707416,3,151726 +151.30260521042084,22062.894263387632,0.8124949899799598,3,150005 +151.70340681362725,21988.89592156585,0.8146472945891784,3,148283 +152.10420841683367,21914.89606493279,0.8167995991983968,3,146561 +152.5050100200401,21840.897723111015,0.8189519038076153,3,144839 +152.9058116232465,21766.899381289233,0.8211042084168337,3,143117 +153.3066132264529,21692.901039467455,0.8232565130260521,3,141395 +153.7074148296593,21618.901182834394,0.8254088176352705,3,139673 +154.10821643286573,21544.96533311937,0.8275611222444891,3,137952 +154.50901803607215,21470.966991297588,0.8297134268537074,3,136230 +154.90981963927857,21396.967134664534,0.831865731462926,3,134508 +155.31062124248496,21322.96879284275,0.8340180360721442,3,132786 +155.7114228456914,21248.97045102097,0.8361703406813629,3,131064 +156.1122244488978,21174.97210919919,0.8383226452905811,3,129342 +156.5130260521042,21101.03474467289,0.8404749498997997,3,127621 +156.91382765531063,21027.036402851107,0.8426272545090181,3,125899 +157.31462925851704,20953.038061029325,0.8447795591182367,3,124177 +157.71543086172346,20879.03820439627,0.846931863727455,3,122455 +158.11623246492985,20805.039862574486,0.8490841683366732,3,120733 +158.51703406813627,20731.041520752704,0.8512364729458918,3,119011 +158.9178356713427,20657.04166411965,0.8533887775551101,3,117289 +159.3186372745491,20583.582596109063,0.8555410821643287,2,115568 +159.71943887775552,20510.89248092589,0.8576933867735471,2,113846 +160.1202404809619,20438.202365742713,0.8598456913827655,2,112124 +160.52104208416833,20365.512034880972,0.8619979959919839,2,110402 +160.92184368737475,20292.8219196978,0.8641503006012023,2,108680 +161.32264529058116,20220.13180451462,0.8663026052104208,2,106958 +161.72344689378758,20147.50331619328,0.8684549098196394,2,105237 +162.124248496994,20074.813201010107,0.8706072144288577,2,103515 +162.52505010020042,20002.123085826937,0.8727595190380762,2,101793 +162.9258517034068,19929.43297064376,0.8749118236472945,2,100071 +163.32665330661322,19856.74263978202,0.877064128256513,2,98349 +163.72745490981964,19784.052524598847,0.8792164328657314,2,96627 +164.12825651302606,19711.424251956072,0.8813687374749499,2,94906 +164.52905811623248,19638.73392109433,0.8835210420841684,2,93184 +164.92985971943887,19566.043805911155,0.8856733466933867,2,91462 +165.3306613226453,19493.35369072798,0.8878256513026053,2,89740 +165.7314629258517,19420.663575544808,0.8899779559118237,2,88018 +166.13226452905812,19347.973244683064,0.892130260521042,2,86296 +166.53306613226454,19275.283129499894,0.8942825651302606,2,84574 +166.93386773547093,19202.654856857116,0.8964348697394788,2,82853 +167.33466933867737,19129.964525995376,0.8985871743486975,2,81131 +167.73547094188376,19057.2744108122,0.9007394789579157,2,79409 +168.13627254509018,18984.58429562903,0.9028917835671343,2,77687 +168.5370741482966,18911.893964767285,0.9050440881763527,2,75965 +168.93787575150301,18839.20384958411,0.9071963927855712,2,74243 +169.33867735470943,18766.575576941337,0.9093486973947896,2,72522 +169.73947895791582,18693.885461758164,0.911501002004008,2,70800 +170.14028056112227,18621.19513089642,0.9136533066132265,2,69078 +170.54108216432866,18548.505015713246,0.915805611222445,2,67356 +170.94188376753507,18475.814900530073,0.9179579158316633,2,65634 +171.3426853707415,18403.124569668333,0.9201102204408819,2,63912 +171.74348697394788,18330.54143105545,0.9222625250501001,1,62190 +172.14428857715433,18258.315830301275,0.9244148296593188,1,60469 +172.54509018036072,18186.02860268526,0.926567134268537,1,58747 +172.94589178356713,18113.741375069247,0.9287194388777555,1,57025 +173.34669338677355,18041.454147453234,0.930871743486974,1,55303 +173.74749498997994,17969.166919837222,0.9330240480961923,1,53581 +174.1482965931864,17896.87969222121,0.9351763527054109,1,51859 +174.54909819639278,17824.65409146703,0.9373286573146292,1,50138 +174.9498997995992,17752.366863851017,0.9394809619238477,1,48416 +175.3507014028056,17680.079636235005,0.9416332665330662,1,46694 +175.75150300601203,17607.792408618992,0.9437855711422846,1,44972 +176.15230460921845,17535.50518100298,0.9459378757515031,1,43250 +176.55310621242484,17463.217953386964,0.9480901803607213,1,41528 +176.95390781563128,17390.992352632788,0.95024248496994,1,39807 +177.35470941883767,17318.70512501677,0.9523947895791582,1,38085 +177.7555110220441,17246.41789740076,0.9545470941883768,1,36363 +178.1563126252505,17174.130669784747,0.9566993987975952,1,34641 +178.5571142284569,17101.843442168734,0.9588517034068136,1,32919 +178.95791583166334,17029.556214552722,0.9610040080160321,1,31197 +179.35871743486973,16957.26898693671,0.9631563126252505,1,29475 +179.75951903807615,16885.043386182526,0.9653086172344689,1,27754 +180.16032064128257,16812.756158566517,0.9674609218436875,1,26032 +180.56112224448898,16740.468930950505,0.9696132264529058,1,24310 +180.9619238476954,16668.181703334492,0.9717655310621244,1,22588 +181.3627254509018,16595.894475718476,0.9739178356713426,1,20866 +181.76352705410824,16523.607248102468,0.9760701402805613,1,19144 +182.16432865731463,16451.381647348284,0.9782224448897795,1,17423 +182.56513026052104,16379.094419732273,0.980374749498998,1,15701 +182.96593186372746,16306.80719211626,0.9825270541082165,1,13979 +183.36673346693385,16234.519964500247,0.9846793587174348,1,12257 +183.7675350701403,16162.232736884236,0.9868316633266534,1,10535 +184.1683366733467,16089.945509268222,0.9889839679358717,1,8813 +184.5691382765531,16017.65828165221,0.9911362725450902,1,7091 +184.96993987975952,15945.43268089803,0.9932885771543087,1,5370 +185.37074148296594,15873.145453282015,0.9954408817635271,1,3648 +185.77154308617236,15800.858225666005,0.9975931863727456,1,1926 +186.17234468937875,15728.570998049989,0.9997454909819639,1,204 +186.5731462925852,15720.0,1.0,0,0 +186.97394789579158,15720.0,1.0,0,0 +187.374749498998,15720.0,1.0,0,0 +187.77555110220442,15720.0,1.0,0,0 +188.1763527054108,15720.0,1.0,0,0 +188.57715430861725,15720.0,1.0,0,0 +188.97795591182364,15720.0,1.0,0,0 +189.37875751503006,15720.0,1.0,0,0 +189.77955911823648,15720.0,1.0,0,0 +190.1803607214429,15720.0,1.0,0,0 +190.5811623246493,15720.0,1.0,0,0 +190.9819639278557,15720.0,1.0,0,0 +191.38276553106215,15720.0,1.0,0,0 +191.78356713426854,15720.0,1.0,0,0 +192.18436873747495,15720.0,1.0,0,0 +192.58517034068137,15720.0,1.0,0,0 +192.98597194388776,15720.0,1.0,0,0 +193.3867735470942,15720.0,1.0,0,0 +193.7875751503006,15720.0,1.0,0,0 +194.18837675350701,15720.0,1.0,0,0 +194.58917835671343,15720.0,1.0,0,0 +194.98997995991982,15720.0,1.0,0,0 +195.39078156312627,15720.0,1.0,0,0 +195.79158316633266,15720.0,1.0,0,0 +196.19238476953907,15720.0,1.0,0,0 +196.5931863727455,15720.0,1.0,0,0 +196.9939879759519,15720.0,1.0,0,0 +197.39478957915833,15720.0,1.0,0,0 +197.79559118236472,15720.0,1.0,0,0 +198.19639278557116,15720.0,1.0,0,0 +198.59719438877755,15720.0,1.0,0,0 +198.99799599198397,15720.0,1.0,0,0 +199.3987975951904,15720.0,1.0,0,0 +199.79959919839678,15720.0,1.0,0,0 +200.20040080160322,15720.0,1.0,0,0 +200.6012024048096,15720.0,1.0,0,0 +201.00200400801603,15720.0,1.0,0,0 +201.40280561122245,15720.0,1.0,0,0 +201.80360721442887,15720.0,1.0,0,0 +202.20440881763528,15720.0,1.0,0,0 +202.60521042084167,15720.0,1.0,0,0 +203.00601202404812,15720.0,1.0,0,0 +203.4068136272545,15720.0,1.0,0,0 +203.80761523046093,15720.0,1.0,0,0 +204.20841683366734,15720.0,1.0,0,0 +204.60921843687373,15720.0,1.0,0,0 +205.01002004008018,15720.0,1.0,0,0 +205.41082164328657,15720.0,1.0,0,0 +205.81162324649299,15720.0,1.0,0,0 +206.2124248496994,15720.0,1.0,0,0 +206.61322645290582,15720.0,1.0,0,0 +207.01402805611224,15720.0,1.0,0,0 +207.41482965931863,15720.0,1.0,0,0 +207.81563126252507,15720.0,1.0,0,0 +208.21643286573146,15720.0,1.0,0,0 +208.61723446893788,15720.0,1.0,0,0 +209.0180360721443,15720.0,1.0,0,0 +209.4188376753507,15720.0,1.0,0,0 +209.81963927855713,15720.0,1.0,0,0 +210.22044088176352,15720.0,1.0,0,0 +210.62124248496994,15720.0,1.0,0,0 +211.02204408817636,15720.0,1.0,0,0 +211.42284569138278,15720.0,1.0,0,0 +211.8236472945892,15720.0,1.0,0,0 +212.22444889779558,15720.0,1.0,0,0 +212.62525050100203,15720.0,1.0,0,0 +213.02605210420842,15720.0,1.0,0,0 +213.42685370741484,15720.0,1.0,0,0 +213.82765531062125,15720.0,1.0,0,0 +214.22845691382764,15720.0,1.0,0,0 +214.6292585170341,15720.0,1.0,0,0 +215.03006012024048,15720.0,1.0,0,0 +215.4308617234469,15720.0,1.0,0,0 +215.8316633266533,15720.0,1.0,0,0 +216.2324649298597,15720.0,1.0,0,0 +216.63326653306615,15720.0,1.0,0,0 +217.03406813627254,15720.0,1.0,0,0 +217.43486973947896,15720.0,1.0,0,0 +217.83567134268537,15720.0,1.0,0,0 +218.2364729458918,15720.0,1.0,0,0 +218.6372745490982,15720.0,1.0,0,0 +219.0380761523046,15720.0,1.0,0,0 +219.43887775551104,15720.0,1.0,0,0 +219.83967935871743,15720.0,1.0,0,0 +220.24048096192385,15720.0,1.0,0,0 +220.64128256513027,15720.0,1.0,0,0 +221.04208416833666,15720.0,1.0,0,0 +221.4428857715431,15720.0,1.0,0,0 +221.8436873747495,15720.0,1.0,0,0 +222.2444889779559,15720.0,1.0,0,0 +222.64529058116233,15720.0,1.0,0,0 +223.04609218436875,15720.0,1.0,0,0 +223.44689378757516,15720.0,1.0,0,0 +223.84769539078155,15720.0,1.0,0,0 +224.248496993988,15720.0,1.0,0,0 +224.6492985971944,15720.0,1.0,0,0 +225.0501002004008,15720.0,1.0,0,0 +225.45090180360722,15720.0,1.0,0,0 +225.8517034068136,15720.0,1.0,0,0 +226.25250501002006,15720.0,1.0,0,0 +226.65330661322645,15720.0,1.0,0,0 +227.05410821643287,15720.0,1.0,0,0 +227.45490981963928,15720.0,1.0,0,0 +227.8557114228457,15720.0,1.0,0,0 +228.25651302605212,15720.0,1.0,0,0 +228.6573146292585,15720.0,1.0,0,0 +229.05811623246493,15720.0,1.0,0,0 +229.45891783567134,15720.0,1.0,0,0 +229.85971943887776,15720.0,1.0,0,0 +230.26052104208418,15720.0,1.0,0,0 +230.6613226452906,15720.0,1.0,0,0 +231.06212424849699,15720.0,1.0,0,0 +231.4629258517034,15720.0,1.0,0,0 +231.86372745490982,15720.0,1.0,0,0 +232.26452905811624,15720.0,1.0,0,0 +232.66533066132266,15720.0,1.0,0,0 +233.06613226452907,15720.0,1.0,0,0 +233.46693386773546,15720.0,1.0,0,0 +233.86773547094188,15720.0,1.0,0,0 +234.2685370741483,15720.0,1.0,0,0 +234.66933867735472,15720.0,1.0,0,0 +235.07014028056113,15720.0,1.0,0,0 +235.47094188376755,15720.0,1.0,0,0 +235.87174348697394,15720.0,1.0,0,0 +236.27254509018036,15720.0,1.0,0,0 +236.67334669338678,15720.0,1.0,0,0 +237.0741482965932,15720.0,1.0,0,0 +237.4749498997996,15720.0,1.0,0,0 +237.87575150300603,15720.0,1.0,0,0 +238.27655310621242,15720.0,1.0,0,0 +238.67735470941884,15720.0,1.0,0,0 +239.07815631262525,15720.0,1.0,0,0 +239.47895791583167,15720.0,1.0,0,0 +239.8797595190381,15720.0,1.0,0,0 +240.2805611222445,15720.0,1.0,0,0 +240.6813627254509,15720.0,1.0,0,0 +241.0821643286573,15720.0,1.0,0,0 +241.48296593186373,15720.0,1.0,0,0 +241.88376753507015,15720.0,1.0,0,0 +242.28456913827657,15720.0,1.0,0,0 +242.68537074148298,15720.0,1.0,0,0 +243.08617234468937,15720.0,1.0,0,0 +243.4869739478958,15720.0,1.0,0,0 +243.8877755511022,15720.0,1.0,0,0 +244.28857715430863,15720.0,1.0,0,0 +244.68937875751504,15720.0,1.0,0,0 +245.09018036072143,15720.0,1.0,0,0 +245.49098196392785,15720.0,1.0,0,0 +245.89178356713427,15720.0,1.0,0,0 +246.2925851703407,15720.0,1.0,0,0 +246.6933867735471,15720.0,1.0,0,0 +247.09418837675352,15720.0,1.0,0,0 +247.4949899799599,15720.0,1.0,0,0 +247.89579158316633,15720.0,1.0,0,0 +248.29659318637275,15720.0,1.0,0,0 +248.69739478957916,15720.0,1.0,0,0 +249.09819639278558,15720.0,1.0,0,0 +249.498997995992,15720.0,1.0,0,0 +249.8997995991984,15720.0,1.0,0,0 +250.3006012024048,15720.0,1.0,0,0 +250.70140280561122,15720.0,1.0,0,0 +251.10220440881764,15720.0,1.0,0,0 +251.50300601202406,15720.0,1.0,0,0 +251.90380761523048,15720.0,1.0,0,0 +252.30460921843687,15720.0,1.0,0,0 +252.70541082164328,15720.0,1.0,0,0 +253.1062124248497,15720.0,1.0,0,0 +253.50701402805612,15720.0,1.0,0,0 +253.90781563126254,15720.0,1.0,0,0 +254.30861723446895,15720.0,1.0,0,0 +254.70941883767534,15720.0,1.0,0,0 +255.11022044088176,15720.0,1.0,0,0 +255.51102204408818,15720.0,1.0,0,0 +255.9118236472946,15720.0,1.0,0,0 +256.312625250501,15720.0,1.0,0,0 +256.71342685370746,15720.0,1.0,0,0 +257.1142284569138,15720.0,1.0,0,0 +257.51503006012024,15720.0,1.0,0,0 +257.9158316633267,15720.0,1.0,0,0 +258.3166332665331,15720.0,1.0,0,0 +258.71743486973946,15720.0,1.0,0,0 +259.1182364729459,15720.0,1.0,0,0 +259.5190380761523,15720.0,1.0,0,0 +259.9198396793587,15720.0,1.0,0,0 +260.32064128256513,15720.0,1.0,0,0 +260.7214428857716,15720.0,1.0,0,0 +261.12224448897797,15720.0,1.0,0,0 +261.52304609218436,15720.0,1.0,0,0 +261.9238476953908,15720.0,1.0,0,0 +262.3246492985972,15720.0,1.0,0,0 +262.7254509018036,15720.0,1.0,0,0 +263.12625250501003,15720.0,1.0,0,0 +263.5270541082165,15720.0,1.0,0,0 +263.92785571142286,15720.0,1.0,0,0 +264.32865731462925,15720.0,1.0,0,0 +264.7294589178357,15720.0,1.0,0,0 +265.1302605210421,15720.0,1.0,0,0 +265.5310621242485,15720.0,1.0,0,0 +265.9318637274549,15720.0,1.0,0,0 +266.3326653306613,15720.0,1.0,0,0 +266.7334669338677,15720.0,1.0,0,0 +267.13426853707415,15720.0,1.0,0,0 +267.5350701402806,15720.0,1.0,0,0 +267.935871743487,15720.0,1.0,0,0 +268.3366733466934,15720.0,1.0,0,0 +268.7374749498998,15720.0,1.0,0,0 +269.1382765531062,15720.0,1.0,0,0 +269.5390781563126,15720.0,1.0,0,0 +269.93987975951904,15720.0,1.0,0,0 +270.3406813627255,15720.0,1.0,0,0 +270.7414829659319,15720.0,1.0,0,0 +271.14228456913827,15720.0,1.0,0,0 +271.5430861723447,15720.0,1.0,0,0 +271.9438877755511,15720.0,1.0,0,0 +272.3446893787575,15720.0,1.0,0,0 +272.74549098196394,15720.0,1.0,0,0 +273.1462925851704,15720.0,1.0,0,0 +273.5470941883767,15720.0,1.0,0,0 +273.94789579158316,15720.0,1.0,0,0 +274.3486973947896,15720.0,1.0,0,0 +274.749498997996,15720.0,1.0,0,0 +275.1503006012024,15720.0,1.0,0,0 +275.55110220440883,15720.0,1.0,0,0 +275.9519038076152,15720.0,1.0,0,0 +276.3527054108216,15720.0,1.0,0,0 +276.75350701402806,15720.0,1.0,0,0 +277.1543086172345,15720.0,1.0,0,0 +277.5551102204409,15720.0,1.0,0,0 +277.9559118236473,15720.0,1.0,0,0 +278.35671342685373,15720.0,1.0,0,0 +278.7575150300601,15720.0,1.0,0,0 +279.1583166332665,15720.0,1.0,0,0 +279.55911823647295,15720.0,1.0,0,0 +279.9599198396794,15720.0,1.0,0,0 +280.3607214428858,15720.0,1.0,0,0 +280.7615230460922,15720.0,1.0,0,0 +281.1623246492986,15720.0,1.0,0,0 +281.563126252505,15720.0,1.0,0,0 +281.9639278557114,15720.0,1.0,0,0 +282.36472945891785,15720.0,1.0,0,0 +282.7655310621243,15720.0,1.0,0,0 +283.16633266533063,15720.0,1.0,0,0 +283.5671342685371,15720.0,1.0,0,0 +283.9679358717435,15720.0,1.0,0,0 +284.3687374749499,15720.0,1.0,0,0 +284.7695390781563,15720.0,1.0,0,0 +285.17034068136275,15720.0,1.0,0,0 +285.57114228456913,15720.0,1.0,0,0 +285.9719438877755,15720.0,1.0,0,0 +286.37274549098197,15720.0,1.0,0,0 +286.7735470941884,15720.0,1.0,0,0 +287.1743486973948,15720.0,1.0,0,0 +287.5751503006012,15720.0,1.0,0,0 +287.97595190380764,15720.0,1.0,0,0 +288.37675350701403,15720.0,1.0,0,0 +288.7775551102204,15720.0,1.0,0,0 +289.17835671342687,15720.0,1.0,0,0 +289.5791583166333,15720.0,1.0,0,0 +289.97995991983964,15720.0,1.0,0,0 +290.3807615230461,15720.0,1.0,0,0 +290.78156312625254,15720.0,1.0,0,0 +291.1823647294589,15720.0,1.0,0,0 +291.5831663326653,15720.0,1.0,0,0 +291.98396793587176,15720.0,1.0,0,0 +292.38476953907815,15720.0,1.0,0,0 +292.78557114228454,15720.0,1.0,0,0 +293.186372745491,15720.0,1.0,0,0 +293.58717434869743,15720.0,1.0,0,0 +293.9879759519038,15720.0,1.0,0,0 +294.3887775551102,15720.0,1.0,0,0 +294.78957915831666,15720.0,1.0,0,0 +295.19038076152304,15720.0,1.0,0,0 +295.59118236472943,15720.0,1.0,0,0 +295.9919839679359,15720.0,1.0,0,0 +296.3927855711423,15720.0,1.0,0,0 +296.7935871743487,15720.0,1.0,0,0 +297.1943887775551,15720.0,1.0,0,0 +297.59519038076155,15720.0,1.0,0,0 +297.99599198396794,15720.0,1.0,0,0 +298.39679358717433,15720.0,1.0,0,0 +298.7975951903808,15720.0,1.0,0,0 +299.1983967935872,15720.0,1.0,0,0 +299.59919839679355,15720.0,1.0,0,0 +300.0,15720.0,1.0,0,0 diff --git a/pareto_knee_analysis.png b/pareto_knee_analysis.png new file mode 100644 index 0000000..364785d Binary files /dev/null and b/pareto_knee_analysis.png differ diff --git a/pareto_optimization.py b/pareto_optimization.py new file mode 100644 index 0000000..8aa1de9 --- /dev/null +++ b/pareto_optimization.py @@ -0,0 +1,1027 @@ +""" +Moon Colony Logistics - Pareto Front & Knee Point Analysis + +Multi-objective optimization for Space Elevator + Rocket combination: +- Objective 1: Minimize completion time T +- Objective 2: Minimize total energy E + +Finds the Pareto front and identifies the "knee point" as the optimal trade-off. +""" + +import numpy as np +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +from matplotlib import rcParams +from dataclasses import dataclass +from typing import List, Tuple, Dict, Optional +import pandas as pd + +# 设置字体 +rcParams['font.sans-serif'] = ['Arial Unicode MS', 'DejaVu Sans', 'SimHei'] +rcParams['axes.unicode_minus'] = False + +# ============== 物理常数与参数 ============== +G0 = 9.81 # m/s² +OMEGA_EARTH = 7.27e-5 # rad/s +R_EARTH = 6.371e6 # m + +# 任务参数 +TOTAL_PAYLOAD = 100e6 # 100 million metric tons + +# 太空电梯参数 +NUM_ELEVATORS = 3 +ELEVATOR_CAPACITY_PER_YEAR = 179000 # metric tons per elevator per year +TOTAL_ELEVATOR_CAPACITY = NUM_ELEVATORS * ELEVATOR_CAPACITY_PER_YEAR # 537,000 tons/year +ELEVATOR_SPECIFIC_ENERGY = 157.2e9 # J per metric ton (157.2 MJ/kg * 1000) + +# 火箭参数 +PAYLOAD_PER_LAUNCH = 125 # metric tons per launch +ISP = 450 +SPECIFIC_FUEL_ENERGY = 15.5e6 # J/kg +ALPHA = 0.10 +NUM_STAGES = 3 +DELTA_V_BASE = 13300 # m/s + + +# ============== 发射场定义 ============== +@dataclass +class LaunchSite: + name: str + short_name: str + latitude: float + max_launches_per_day: int = 1 + + @property + def abs_latitude(self) -> float: + return abs(self.latitude) + + @property + def delta_v_loss(self) -> float: + v_equator = OMEGA_EARTH * R_EARTH + v_site = OMEGA_EARTH * R_EARTH * np.cos(np.radians(self.abs_latitude)) + return v_equator - v_site + + @property + def total_delta_v(self) -> float: + return DELTA_V_BASE + self.delta_v_loss + + +LAUNCH_SITES = sorted([ + LaunchSite("Kourou (French Guiana)", "Kourou", 5.2), + LaunchSite("Satish Dhawan (India)", "SDSC", 13.7), + LaunchSite("Boca Chica (Texas)", "Texas", 26.0), + LaunchSite("Cape Canaveral (Florida)", "Florida", 28.5), + LaunchSite("Vandenberg (California)", "California", 34.7), + LaunchSite("Wallops (Virginia)", "Virginia", 37.8), + LaunchSite("Taiyuan (China)", "Taiyuan", 38.8), + LaunchSite("Mahia (New Zealand)", "Mahia", 39.3), + LaunchSite("Baikonur (Kazakhstan)", "Baikonur", 45.6), + LaunchSite("Kodiak (Alaska)", "Alaska", 57.4), +], key=lambda x: x.abs_latitude) + + +# ============== 核心计算函数 ============== + +def fuel_ratio_multistage(delta_v: float) -> float: + """多级火箭燃料/载荷比""" + ve = ISP * G0 + delta_v_per_stage = delta_v / NUM_STAGES + R_stage = np.exp(delta_v_per_stage / ve) + + denominator = 1 - ALPHA * (R_stage - 1) + if denominator <= 0: + return np.inf + + k_stage = (R_stage - 1) / denominator + + total_fuel_ratio = 0 + remaining_ratio = 1.0 + + for _ in range(NUM_STAGES): + fuel_this_stage = remaining_ratio * k_stage + total_fuel_ratio += fuel_this_stage + remaining_ratio *= (1 + k_stage * (1 + ALPHA)) + + return total_fuel_ratio + + +def rocket_energy_per_ton(site: LaunchSite) -> float: + """火箭发射每吨载荷的能量消耗 (J/ton)""" + k = fuel_ratio_multistage(site.total_delta_v) + fuel_per_ton = k * 1000 # kg fuel per metric ton payload + return fuel_per_ton * SPECIFIC_FUEL_ENERGY + + +def calculate_scenario(completion_years: float) -> Optional[Dict]: + """ + 计算给定完成年限下的最优方案(电梯优先+低纬火箭) + + 返回: 方案详情字典,如果无法完成则返回None + """ + # 太空电梯运输量 + elevator_payload = min(TOTAL_ELEVATOR_CAPACITY * completion_years, TOTAL_PAYLOAD) + elevator_energy = elevator_payload * ELEVATOR_SPECIFIC_ENERGY + + # 剩余需要火箭运输的载荷 + remaining_payload = TOTAL_PAYLOAD - elevator_payload + + if remaining_payload <= 0: + # 完全由电梯完成 + return { + 'years': completion_years, + 'elevator_payload': elevator_payload, + 'rocket_payload': 0, + 'elevator_energy_PJ': elevator_energy / 1e15, + 'rocket_energy_PJ': 0, + 'total_energy_PJ': elevator_energy / 1e15, + 'rocket_launches': 0, + 'sites_used': 0, + 'elevator_fraction': 1.0, + } + + # 需要火箭发射 + rocket_launches_needed = int(np.ceil(remaining_payload / PAYLOAD_PER_LAUNCH)) + + # 按纬度优先分配火箭发射 + days_available = completion_years * 365 + max_launches_per_site = int(days_available) + + # 检查是否能在规定时间内完成 + total_rocket_capacity = len(LAUNCH_SITES) * max_launches_per_site * PAYLOAD_PER_LAUNCH + if remaining_payload > total_rocket_capacity: + return None # 无法完成 + + rocket_energy = 0 + sites_used = 0 + remaining_launches = rocket_launches_needed + + for site in LAUNCH_SITES: + if remaining_launches <= 0: + break + allocated = min(remaining_launches, max_launches_per_site) + rocket_energy += rocket_energy_per_ton(site) * PAYLOAD_PER_LAUNCH * allocated + remaining_launches -= allocated + if allocated > 0: + sites_used += 1 + + rocket_payload = rocket_launches_needed * PAYLOAD_PER_LAUNCH + total_energy = elevator_energy + rocket_energy + + return { + 'years': completion_years, + 'elevator_payload': elevator_payload, + 'rocket_payload': rocket_payload, + 'elevator_energy_PJ': elevator_energy / 1e15, + 'rocket_energy_PJ': rocket_energy / 1e15, + 'total_energy_PJ': total_energy / 1e15, + 'rocket_launches': rocket_launches_needed, + 'sites_used': sites_used, + 'elevator_fraction': elevator_payload / TOTAL_PAYLOAD, + } + + +# ============== Pareto 前沿计算 ============== + +def generate_pareto_front( + year_min: float = 100, + year_max: float = 300, + num_points: int = 500 +) -> pd.DataFrame: + """ + 生成 Pareto 前沿数据 + + 对于此问题,由于"时间越长→能量越低"是单调关系, + 所有可行解都在Pareto前沿上(这是一个双目标单调权衡问题)。 + """ + years_range = np.linspace(year_min, year_max, num_points) + + results = [] + for years in years_range: + scenario = calculate_scenario(years) + if scenario is not None: + results.append({ + 'years': years, + 'energy_PJ': scenario['total_energy_PJ'], + 'elevator_fraction': scenario['elevator_fraction'], + 'sites_used': scenario['sites_used'], + 'rocket_launches': scenario['rocket_launches'], + }) + + return pd.DataFrame(results) + + +# ============== 膝点检测算法 ============== + +def find_knee_point_max_curvature(df: pd.DataFrame) -> int: + """ + 方法1: 最大曲率法 + + 在归一化后的 (T, E) 曲线上找曲率最大的点。 + 曲率 κ = |y''| / (1 + y'^2)^(3/2) + """ + # 归一化 + T = df['years'].values + E = df['energy_PJ'].values + + T_norm = (T - T.min()) / (T.max() - T.min()) + E_norm = (E - E.min()) / (E.max() - E.min()) + + # 计算一阶和二阶导数(使用有限差分) + dE = np.gradient(E_norm, T_norm) + d2E = np.gradient(dE, T_norm) + + # 计算曲率 + curvature = np.abs(d2E) / np.power(1 + dE**2, 1.5) + + # 排除边界点 + curvature[:5] = 0 + curvature[-5:] = 0 + + knee_idx = np.argmax(curvature) + return knee_idx + + +def find_knee_point_max_distance(df: pd.DataFrame) -> int: + """ + 方法2: 最大距离法(L-method / Kneedle思想) + + 找到距离"起点-终点连线"最远的点。 + 这通常对应于"边际收益递减"最明显的拐点。 + """ + T = df['years'].values + E = df['energy_PJ'].values + + # 归一化到 [0, 1] + T_norm = (T - T.min()) / (T.max() - T.min()) + E_norm = (E - E.min()) / (E.max() - E.min()) + + # 起点和终点 + p1 = np.array([T_norm[0], E_norm[0]]) + p2 = np.array([T_norm[-1], E_norm[-1]]) + + # 计算每个点到直线 p1-p2 的距离 + line_vec = p2 - p1 + line_len = np.linalg.norm(line_vec) + line_unit = line_vec / line_len + + distances = [] + for i in range(len(T)): + point = np.array([T_norm[i], E_norm[i]]) + point_vec = point - p1 + # 投影长度 + proj_len = np.dot(point_vec, line_unit) + # 垂直距离 + proj_point = p1 + proj_len * line_unit + dist = np.linalg.norm(point - proj_point) + distances.append(dist) + + distances = np.array(distances) + knee_idx = np.argmax(distances) + return knee_idx + + +def find_knee_point_marginal(df: pd.DataFrame, threshold: float = 0.1) -> int: + """ + 方法3: 边际收益法 + + 找到"多投入1年时间,能量下降比例"开始低于阈值的点。 + 即:|dE/dT| / |E| < threshold 的第一个点 + """ + T = df['years'].values + E = df['energy_PJ'].values + + # 计算边际能量变化率 + dE_dT = np.gradient(E, T) + + # 相对变化率 + marginal_ratio = np.abs(dE_dT) / E + + # 找到第一个低于阈值的点(跳过前面几个点) + for i in range(10, len(marginal_ratio)): + if marginal_ratio[i] < threshold: + return i + + return len(df) // 2 # 默认返回中点 + + +def find_knee_point_elbow(df: pd.DataFrame) -> int: + """ + 方法4: 肘部法则(Elbow Method) + + 类似K-means的肘部法则,找到曲线"弯曲"最明显的点。 + 使用二阶导数的符号变化或绝对值。 + """ + T = df['years'].values + E = df['energy_PJ'].values + + # 归一化 + T_norm = (T - T.min()) / (T.max() - T.min() + 1e-10) + E_norm = (E - E.min()) / (E.max() - E.min() + 1e-10) + + # 计算二阶差分 + d2E = np.diff(np.diff(E_norm)) + + # 找到绝对值最大的点 + knee_idx = np.argmax(np.abs(d2E)) + 1 + return min(knee_idx, len(df) - 1) + + +# ============== 综合膝点分析 ============== + +def analyze_knee_points(df: pd.DataFrame) -> Dict: + """ + 使用多种方法找膝点,并综合给出推荐 + """ + methods = { + 'max_curvature': find_knee_point_max_curvature(df), + 'max_distance': find_knee_point_max_distance(df), + 'elbow': find_knee_point_elbow(df), + } + + # 每种方法对应的年份和能量 + results = {} + for method, idx in methods.items(): + results[method] = { + 'index': idx, + 'years': df.iloc[idx]['years'], + 'energy_PJ': df.iloc[idx]['energy_PJ'], + 'elevator_fraction': df.iloc[idx]['elevator_fraction'], + 'sites_used': df.iloc[idx]['sites_used'], + } + + # 推荐:使用最大距离法(最稳健) + recommended = results['max_distance'] + + return { + 'methods': results, + 'recommended': recommended, + } + + +# ============== 可视化 ============== + +def plot_pareto_analysis( + df: pd.DataFrame, + knee_analysis: Dict, + save_path: str = '/Volumes/Files/code/mm/20260130_b/pareto_knee_analysis.png' +): + """ + 绘制 Pareto 前沿与膝点分析图 + """ + fig, axes = plt.subplots(2, 2, figsize=(16, 14)) + + # ========== 图1: Pareto 前沿(时间 vs 能量) ========== + ax1 = axes[0, 0] + + ax1.plot(df['years'], df['energy_PJ'], 'b-', linewidth=2, label='Pareto Front') + ax1.fill_between(df['years'], df['energy_PJ'], alpha=0.2) + + # 标记各方法找到的膝点 + colors = {'max_curvature': 'red', 'max_distance': 'green', 'elbow': 'orange'} + markers = {'max_curvature': 'o', 'max_distance': 's', 'elbow': '^'} + + for method, data in knee_analysis['methods'].items(): + ax1.scatter(data['years'], data['energy_PJ'], + c=colors[method], marker=markers[method], s=150, zorder=5, + label=f'{method}: {data["years"]:.0f}y, {data["energy_PJ"]:.0f}PJ') + + # 标记推荐点 + rec = knee_analysis['recommended'] + ax1.axvline(x=rec['years'], color='green', linestyle='--', alpha=0.7) + ax1.axhline(y=rec['energy_PJ'], color='green', linestyle='--', alpha=0.7) + + ax1.set_xlabel('Completion Time (years)', fontsize=12) + ax1.set_ylabel('Total Energy (PJ)', fontsize=12) + ax1.set_title('Pareto Front: Time vs Energy Trade-off\n(Knee Points by Different Methods)', fontsize=13) + ax1.legend(loc='upper right', fontsize=9) + ax1.grid(True, alpha=0.3) + + # ========== 图2: 归一化 Pareto 前沿 + 距离可视化 ========== + ax2 = axes[0, 1] + + T = df['years'].values + E = df['energy_PJ'].values + T_norm = (T - T.min()) / (T.max() - T.min()) + E_norm = (E - E.min()) / (E.max() - E.min()) + + ax2.plot(T_norm, E_norm, 'b-', linewidth=2, label='Normalized Pareto Front') + + # 画起点-终点连线 + ax2.plot([T_norm[0], T_norm[-1]], [E_norm[0], E_norm[-1]], + 'r--', linewidth=1.5, label='Baseline (Start-End)') + + # 标记最大距离点 + knee_idx = knee_analysis['methods']['max_distance']['index'] + ax2.scatter(T_norm[knee_idx], E_norm[knee_idx], + c='green', marker='s', s=200, zorder=5, label='Knee Point (Max Distance)') + + # 画垂直距离线 + p1 = np.array([T_norm[0], E_norm[0]]) + p2 = np.array([T_norm[-1], E_norm[-1]]) + line_vec = p2 - p1 + line_unit = line_vec / np.linalg.norm(line_vec) + point = np.array([T_norm[knee_idx], E_norm[knee_idx]]) + proj_len = np.dot(point - p1, line_unit) + proj_point = p1 + proj_len * line_unit + ax2.plot([T_norm[knee_idx], proj_point[0]], [E_norm[knee_idx], proj_point[1]], + 'g-', linewidth=2, label='Max Distance') + + ax2.set_xlabel('Normalized Time', fontsize=12) + ax2.set_ylabel('Normalized Energy', fontsize=12) + ax2.set_title('Knee Point Detection\n(Maximum Distance to Baseline)', fontsize=13) + ax2.legend(fontsize=9) + ax2.grid(True, alpha=0.3) + ax2.set_xlim(-0.05, 1.05) + ax2.set_ylim(-0.05, 1.05) + + # ========== 图3: 边际收益分析 ========== + ax3 = axes[1, 0] + + # 计算边际能量变化 + dE_dT = np.gradient(E, T) + marginal_savings = -dE_dT # 每多1年节省的能量 + + ax3.plot(T, marginal_savings, 'purple', linewidth=2) + ax3.fill_between(T, marginal_savings, alpha=0.2, color='purple') + + # 标记推荐点 + ax3.axvline(x=rec['years'], color='green', linestyle='--', linewidth=2, + label=f'Recommended: {rec["years"]:.0f} years') + + ax3.set_xlabel('Completion Time (years)', fontsize=12) + ax3.set_ylabel('Marginal Energy Saving (PJ/year)', fontsize=12) + ax3.set_title('Marginal Benefit Analysis\n(Energy Saved per Additional Year)', fontsize=13) + ax3.legend() + ax3.grid(True, alpha=0.3) + + # ========== 图4: 方案构成随时间变化 ========== + ax4 = axes[1, 1] + + ax4.fill_between(df['years'], 0, df['elevator_fraction'] * 100, + alpha=0.7, color='green', label='Space Elevator') + ax4.fill_between(df['years'], df['elevator_fraction'] * 100, 100, + alpha=0.7, color='red', label='Rockets') + + # 标记推荐点 + ax4.axvline(x=rec['years'], color='black', linestyle='--', linewidth=2, + label=f'Recommended: {rec["years"]:.0f}y ({rec["elevator_fraction"]*100:.0f}% elevator)') + + ax4.set_xlabel('Completion Time (years)', fontsize=12) + ax4.set_ylabel('Payload Share (%)', fontsize=12) + ax4.set_title('Payload Distribution at Different Timelines', fontsize=13) + ax4.legend(loc='center right') + ax4.set_ylim(0, 100) + ax4.grid(True, alpha=0.3) + + plt.suptitle('Pareto Optimization: Space Elevator + Rocket Combination\n' + f'Recommended Knee Point: {rec["years"]:.0f} years, {rec["energy_PJ"]:.0f} PJ', + fontsize=15, y=1.02) + + plt.tight_layout() + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"Pareto分析图已保存至: {save_path}") + + return fig + + +def plot_decision_recommendation( + df: pd.DataFrame, + knee_analysis: Dict, + save_path: str = '/Volumes/Files/code/mm/20260130_b/decision_recommendation.png' +): + """ + 绘制决策建议图(更适合放在报告中) + """ + rec = knee_analysis['recommended'] + + fig, ax = plt.subplots(figsize=(12, 8)) + + # 主曲线 + ax.plot(df['years'], df['energy_PJ'], 'b-', linewidth=3, label='Energy-Time Trade-off') + + # 填充区域 + ax.fill_between(df['years'], df['energy_PJ'], alpha=0.15, color='blue') + + # 标记推荐点 + ax.scatter(rec['years'], rec['energy_PJ'], c='red', s=300, zorder=5, + marker='*', edgecolors='black', linewidth=2) + + # 标注推荐点 + ax.annotate(f"RECOMMENDED\n{rec['years']:.0f} years\n{rec['energy_PJ']:.0f} PJ\n" + f"({rec['elevator_fraction']*100:.0f}% Elevator)", + xy=(rec['years'], rec['energy_PJ']), + xytext=(rec['years'] + 30, rec['energy_PJ'] + 3000), + fontsize=12, fontweight='bold', + bbox=dict(boxstyle='round,pad=0.5', facecolor='yellow', alpha=0.8), + arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.2', + color='black', lw=2)) + + # 标记几个关键时间点 + key_points = [ + (100, 'Minimum\nfeasible'), + (186, 'Elevator-only\nfeasible'), + (219, 'Rocket-only\nfeasible'), + ] + + for year, label in key_points: + if year >= df['years'].min() and year <= df['years'].max(): + idx = np.argmin(np.abs(df['years'] - year)) + energy = df.iloc[idx]['energy_PJ'] + ax.axvline(x=year, color='gray', linestyle=':', alpha=0.7) + ax.text(year, ax.get_ylim()[1] * 0.95, label, ha='center', fontsize=9, + bbox=dict(facecolor='white', alpha=0.7)) + + ax.set_xlabel('Completion Time (years)', fontsize=14) + ax.set_ylabel('Total Energy Consumption (PJ)', fontsize=14) + ax.set_title('Optimal Decision Point for Moon Colony Construction\n' + '(100 Million Metric Tons Payload)', fontsize=16) + ax.grid(True, alpha=0.3) + + # 添加文字说明 + textstr = (f"Knee Point Analysis Result:\n" + f"• Optimal Timeline: {rec['years']:.0f} years\n" + f"• Total Energy: {rec['energy_PJ']:.0f} PJ\n" + f"• Elevator Share: {rec['elevator_fraction']*100:.1f}%\n" + f"• Rocket Sites Used: {rec['sites_used']}") + + props = dict(boxstyle='round', facecolor='lightgreen', alpha=0.8) + ax.text(0.98, 0.55, textstr, transform=ax.transAxes, fontsize=11, + verticalalignment='top', horizontalalignment='right', bbox=props) + + plt.tight_layout() + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"决策建议图已保存至: {save_path}") + + return fig + + +def print_analysis_report(df: pd.DataFrame, knee_analysis: Dict): + """ + 打印详细分析报告 + """ + print("\n" + "=" * 80) + print("PARETO FRONT & KNEE POINT ANALYSIS REPORT") + print("Moon Colony Logistics Optimization") + print("=" * 80) + + print(f"\n1. PROBLEM DEFINITION") + print(f" - Total payload: {TOTAL_PAYLOAD/1e6:.0f} million metric tons") + print(f" - Elevator capacity: {TOTAL_ELEVATOR_CAPACITY:,} tons/year") + print(f" - Rocket capacity: {len(LAUNCH_SITES) * 365 * PAYLOAD_PER_LAUNCH:,} tons/year") + + print(f"\n2. PARETO FRONT RANGE") + print(f" - Minimum feasible time: {df['years'].min():.1f} years") + print(f" - Analysis range: {df['years'].min():.0f} - {df['years'].max():.0f} years") + print(f" - Energy range: {df['energy_PJ'].min():.0f} - {df['energy_PJ'].max():.0f} PJ") + + print(f"\n3. KNEE POINT DETECTION (Multiple Methods)") + print("-" * 60) + print(f" {'Method':<20} {'Years':>10} {'Energy (PJ)':>15} {'Elev. %':>10}") + print("-" * 60) + + for method, data in knee_analysis['methods'].items(): + print(f" {method:<20} {data['years']:>10.0f} {data['energy_PJ']:>15.0f} " + f"{data['elevator_fraction']*100:>9.1f}%") + + print("-" * 60) + + rec = knee_analysis['recommended'] + print(f"\n4. RECOMMENDED OPTIMAL POINT (Max Distance Method)") + print(f" ┌─────────────────────────────────────────────┐") + print(f" │ Completion Time: {rec['years']:>10.0f} years │") + print(f" │ Total Energy: {rec['energy_PJ']:>10.0f} PJ │") + print(f" │ Elevator Share: {rec['elevator_fraction']*100:>10.1f} % │") + print(f" │ Rocket Sites Used: {rec['sites_used']:>10.0f} │") + print(f" └─────────────────────────────────────────────┘") + + print(f"\n5. INTERPRETATION") + print(f" At the recommended {rec['years']:.0f}-year timeline:") + + # 计算方案详情 + scenario = calculate_scenario(rec['years']) + print(f" - Space Elevator delivers: {scenario['elevator_payload']/1e6:.1f}M tons") + print(f" - Rockets deliver: {scenario['rocket_payload']/1e6:.1f}M tons") + print(f" - Total rocket launches: {scenario['rocket_launches']:,}") + + # 与极端方案对比 + min_time_scenario = calculate_scenario(df['years'].min() + 1) + max_time_idx = len(df) - 1 + + print(f"\n Compared to minimum-time ({df['years'].min():.0f}y):") + if min_time_scenario: + time_diff = rec['years'] - df['years'].min() + energy_diff = min_time_scenario['total_energy_PJ'] - rec['energy_PJ'] + print(f" - Extra time: +{time_diff:.0f} years") + print(f" - Energy saved: {energy_diff:.0f} PJ ({energy_diff/min_time_scenario['total_energy_PJ']*100:.1f}%)") + + print(f"\n Compared to elevator-only (~186y):") + elevator_only_energy = TOTAL_PAYLOAD * ELEVATOR_SPECIFIC_ENERGY / 1e15 + energy_overhead = rec['energy_PJ'] - elevator_only_energy + time_saved = 186 - rec['years'] + print(f" - Time saved: {time_saved:.0f} years") + print(f" - Extra energy: {energy_overhead:.0f} PJ ({energy_overhead/elevator_only_energy*100:.1f}%)") + + print("\n" + "=" * 80) + print("CONCLUSION: The knee point represents the optimal trade-off where") + print("extending the timeline further yields diminishing energy savings.") + print("=" * 80) + + +# ============== 组合方案范围内的膝点分析 ============== + +def analyze_combined_range(): + """ + 在组合方案有效范围内(101-186年)进行膝点分析 + 186年后电梯可独立完成,曲线平坦,无意义 + """ + # 计算电梯独立完成的时间 + elevator_only_years = TOTAL_PAYLOAD / TOTAL_ELEVATOR_CAPACITY + print(f"电梯独立完成需要: {elevator_only_years:.1f} 年") + + # 在组合方案范围内生成Pareto前沿 + year_min = 101 + year_max = elevator_only_years # ~186年 + + df = generate_pareto_front(year_min=year_min, year_max=year_max, num_points=300) + + return df, year_min, year_max + + +def plot_combined_range_analysis( + df: pd.DataFrame, + knee_analysis: Dict, + year_range: Tuple[float, float], + save_path: str = '/Volumes/Files/code/mm/20260130_b/pareto_combined_range.png' +): + """ + 绘制组合方案范围内的Pareto分析图 + """ + fig, axes = plt.subplots(2, 2, figsize=(16, 14)) + + year_min, year_max = year_range + + # ========== 图1: Pareto 前沿(时间 vs 能量) ========== + ax1 = axes[0, 0] + + ax1.plot(df['years'], df['energy_PJ'], 'b-', linewidth=2.5, label='Pareto Front') + ax1.fill_between(df['years'], df['energy_PJ'], alpha=0.2) + + # 标记各方法找到的膝点 + colors = {'max_curvature': 'red', 'max_distance': 'green', 'elbow': 'orange'} + markers = {'max_curvature': 'o', 'max_distance': 's', 'elbow': '^'} + labels = {'max_curvature': 'Max Curvature', 'max_distance': 'Max Distance', 'elbow': 'Elbow'} + + for method, data in knee_analysis['methods'].items(): + ax1.scatter(data['years'], data['energy_PJ'], + c=colors[method], marker=markers[method], s=200, zorder=5, + edgecolors='black', linewidth=1.5, + label=f'{labels[method]}: {data["years"]:.0f}y, {data["energy_PJ"]:.0f}PJ') + + # 标记推荐点 + rec = knee_analysis['recommended'] + ax1.axvline(x=rec['years'], color='green', linestyle='--', alpha=0.7, linewidth=2) + ax1.axhline(y=rec['energy_PJ'], color='green', linestyle='--', alpha=0.7, linewidth=2) + + ax1.set_xlabel('Completion Time (years)', fontsize=12) + ax1.set_ylabel('Total Energy (PJ)', fontsize=12) + ax1.set_title(f'Pareto Front: Combined Scenario ({year_min:.0f}-{year_max:.0f} years)\n' + f'Knee Points by Different Methods', fontsize=13) + ax1.legend(loc='upper right', fontsize=10) + ax1.grid(True, alpha=0.3) + + # ========== 图2: 归一化 + 距离可视化 ========== + ax2 = axes[0, 1] + + T = df['years'].values + E = df['energy_PJ'].values + T_norm = (T - T.min()) / (T.max() - T.min()) + E_norm = (E - E.min()) / (E.max() - E.min()) + + ax2.plot(T_norm, E_norm, 'b-', linewidth=2.5, label='Normalized Pareto Front') + + # 画起点-终点连线 + ax2.plot([T_norm[0], T_norm[-1]], [E_norm[0], E_norm[-1]], + 'r--', linewidth=2, label='Baseline (Start→End)') + + # 标记最大距离点 + knee_idx = knee_analysis['methods']['max_distance']['index'] + ax2.scatter(T_norm[knee_idx], E_norm[knee_idx], + c='green', marker='s', s=250, zorder=5, edgecolors='black', linewidth=2, + label='Knee Point') + + # 画垂直距离线 + p1 = np.array([T_norm[0], E_norm[0]]) + p2 = np.array([T_norm[-1], E_norm[-1]]) + line_vec = p2 - p1 + line_unit = line_vec / np.linalg.norm(line_vec) + point = np.array([T_norm[knee_idx], E_norm[knee_idx]]) + proj_len = np.dot(point - p1, line_unit) + proj_point = p1 + proj_len * line_unit + ax2.plot([T_norm[knee_idx], proj_point[0]], [E_norm[knee_idx], proj_point[1]], + 'g-', linewidth=3, label=f'Max Distance') + + ax2.set_xlabel('Normalized Time (0=min, 1=max)', fontsize=12) + ax2.set_ylabel('Normalized Energy (0=min, 1=max)', fontsize=12) + ax2.set_title('Knee Point Detection\n(Maximum Distance to Baseline)', fontsize=13) + ax2.legend(fontsize=10) + ax2.grid(True, alpha=0.3) + ax2.set_xlim(-0.05, 1.05) + ax2.set_ylim(-0.05, 1.05) + + # ========== 图3: 曲率分析 ========== + ax3 = axes[1, 0] + + # 计算曲率 + dE = np.gradient(E_norm, T_norm) + d2E = np.gradient(dE, T_norm) + curvature = np.abs(d2E) / np.power(1 + dE**2, 1.5) + + ax3.plot(T, curvature, 'purple', linewidth=2) + ax3.fill_between(T, curvature, alpha=0.2, color='purple') + + # 标记最大曲率点 + curv_idx = knee_analysis['methods']['max_curvature']['index'] + ax3.scatter(T[curv_idx], curvature[curv_idx], c='red', s=200, zorder=5, + marker='o', edgecolors='black', linewidth=2, + label=f'Max Curvature: {T[curv_idx]:.0f} years') + + ax3.set_xlabel('Completion Time (years)', fontsize=12) + ax3.set_ylabel('Curvature κ', fontsize=12) + ax3.set_title('Curvature Analysis\n(Higher curvature = sharper bend)', fontsize=13) + ax3.legend(fontsize=10) + ax3.grid(True, alpha=0.3) + + # ========== 图4: 边际收益 + 方案构成 ========== + ax4 = axes[1, 1] + + # 边际节省 + dE_dT = np.gradient(E, T) + marginal_savings = -dE_dT # 每多1年节省的能量 + + ax4_twin = ax4.twinx() + + # 边际收益曲线 + line1, = ax4.plot(T, marginal_savings, 'b-', linewidth=2, label='Marginal Saving (PJ/year)') + ax4.fill_between(T, marginal_savings, alpha=0.15, color='blue') + + # 电梯占比 + line2, = ax4_twin.plot(df['years'], df['elevator_fraction'] * 100, 'g--', linewidth=2, + label='Elevator Share (%)') + + # 标记推荐点 + ax4.axvline(x=rec['years'], color='red', linestyle=':', linewidth=2, + label=f'Recommended: {rec["years"]:.0f}y') + + ax4.set_xlabel('Completion Time (years)', fontsize=12) + ax4.set_ylabel('Marginal Energy Saving (PJ/year)', fontsize=12, color='blue') + ax4_twin.set_ylabel('Elevator Share (%)', fontsize=12, color='green') + ax4.set_title('Marginal Benefit & Payload Distribution', fontsize=13) + + # 合并图例 + lines = [line1, line2] + labels = [l.get_label() for l in lines] + ax4.legend(lines, labels, loc='upper right', fontsize=10) + ax4.grid(True, alpha=0.3) + + plt.suptitle(f'Pareto Optimization: Combined Scenario (Elevator + Rockets)\n' + f'Analysis Range: {year_min:.0f} - {year_max:.0f} years | ' + f'Recommended: {rec["years"]:.0f} years, {rec["energy_PJ"]:.0f} PJ', + fontsize=15, y=1.02) + + plt.tight_layout() + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"组合方案Pareto分析图已保存至: {save_path}") + + return fig + + +def plot_combined_decision( + df: pd.DataFrame, + knee_analysis: Dict, + year_range: Tuple[float, float], + save_path: str = '/Volumes/Files/code/mm/20260130_b/combined_decision.png' +): + """ + 绘制组合方案决策图 + """ + rec = knee_analysis['recommended'] + year_min, year_max = year_range + + fig, ax = plt.subplots(figsize=(14, 9)) + + T = df['years'].values + E = df['energy_PJ'].values + + # 主曲线 + ax.plot(T, E, 'b-', linewidth=3, label='Energy-Time Trade-off') + ax.fill_between(T, E, alpha=0.15, color='blue') + + # 标记三种方法的膝点 + methods_info = knee_analysis['methods'] + + # 最大距离法(推荐) + ax.scatter(methods_info['max_distance']['years'], methods_info['max_distance']['energy_PJ'], + c='green', s=400, zorder=5, marker='*', edgecolors='black', linewidth=2) + + # 最大曲率法 + ax.scatter(methods_info['max_curvature']['years'], methods_info['max_curvature']['energy_PJ'], + c='red', s=200, zorder=5, marker='o', edgecolors='black', linewidth=1.5) + + # 肘部法 + ax.scatter(methods_info['elbow']['years'], methods_info['elbow']['energy_PJ'], + c='orange', s=200, zorder=5, marker='^', edgecolors='black', linewidth=1.5) + + # 标注推荐点 + ax.annotate(f"★ RECOMMENDED (Max Distance)\n" + f"{rec['years']:.0f} years | {rec['energy_PJ']:.0f} PJ\n" + f"Elevator: {rec['elevator_fraction']*100:.0f}%", + xy=(rec['years'], rec['energy_PJ']), + xytext=(rec['years'] + 15, rec['energy_PJ'] + 2500), + fontsize=12, fontweight='bold', + bbox=dict(boxstyle='round,pad=0.5', facecolor='lightgreen', alpha=0.9), + arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.2', + color='green', lw=2)) + + # 标注最大曲率点(如果不同) + if abs(methods_info['max_curvature']['years'] - rec['years']) > 5: + mc = methods_info['max_curvature'] + ax.annotate(f"● Max Curvature\n{mc['years']:.0f}y | {mc['energy_PJ']:.0f}PJ", + xy=(mc['years'], mc['energy_PJ']), + xytext=(mc['years'] - 20, mc['energy_PJ'] - 2000), + fontsize=10, + bbox=dict(boxstyle='round,pad=0.3', facecolor='lightyellow', alpha=0.8), + arrowprops=dict(arrowstyle='->', color='red', lw=1.5)) + + # 标记端点 + ax.scatter(T[0], E[0], c='red', s=150, marker='D', edgecolors='black', zorder=5) + ax.annotate(f'Fastest\n{T[0]:.0f}y, {E[0]:.0f}PJ', + xy=(T[0], E[0]), xytext=(T[0]+5, E[0]-1500), + fontsize=10, ha='left') + + ax.scatter(T[-1], E[-1], c='green', s=150, marker='D', edgecolors='black', zorder=5) + ax.annotate(f'Lowest Energy\n{T[-1]:.0f}y, {E[-1]:.0f}PJ', + xy=(T[-1], E[-1]), xytext=(T[-1]-25, E[-1]+1500), + fontsize=10, ha='right') + + ax.set_xlabel('Completion Time (years)', fontsize=14) + ax.set_ylabel('Total Energy Consumption (PJ)', fontsize=14) + ax.set_title(f'Optimal Decision Point for Combined Scenario\n' + f'(Elevator + Rockets, {year_min:.0f}-{year_max:.0f} years)', fontsize=16) + ax.grid(True, alpha=0.3) + + # 图例 + from matplotlib.lines import Line2D + legend_elements = [ + Line2D([0], [0], marker='*', color='w', markerfacecolor='green', markersize=20, + markeredgecolor='black', label='Max Distance (Recommended)'), + Line2D([0], [0], marker='o', color='w', markerfacecolor='red', markersize=12, + markeredgecolor='black', label='Max Curvature'), + Line2D([0], [0], marker='^', color='w', markerfacecolor='orange', markersize=12, + markeredgecolor='black', label='Elbow Method'), + ] + ax.legend(handles=legend_elements, loc='upper right', fontsize=11) + + # 右侧信息框 + scenario = calculate_scenario(rec['years']) + textstr = (f"Knee Point Analysis\n" + f"─────────────────\n" + f"Optimal Time: {rec['years']:.0f} years\n" + f"Total Energy: {rec['energy_PJ']:.0f} PJ\n" + f"─────────────────\n" + f"Elevator: {scenario['elevator_payload']/1e6:.1f}M tons\n" + f"Rockets: {scenario['rocket_payload']/1e6:.1f}M tons\n" + f"Launches: {scenario['rocket_launches']:,}\n" + f"Sites Used: {scenario['sites_used']}") + + props = dict(boxstyle='round', facecolor='white', alpha=0.9, edgecolor='gray') + ax.text(0.02, 0.35, textstr, transform=ax.transAxes, fontsize=11, + verticalalignment='top', horizontalalignment='left', bbox=props, + family='monospace') + + plt.tight_layout() + plt.savefig(save_path, dpi=150, bbox_inches='tight') + print(f"组合方案决策图已保存至: {save_path}") + + return fig + + +def print_combined_range_report(df: pd.DataFrame, knee_analysis: Dict, year_range: Tuple[float, float]): + """ + 打印组合方案范围内的分析报告 + """ + year_min, year_max = year_range + + print("\n" + "=" * 80) + print("COMBINED SCENARIO PARETO ANALYSIS (Elevator + Rockets)") + print(f"Analysis Range: {year_min:.0f} - {year_max:.0f} years") + print("=" * 80) + + print(f"\n1. RANGE DEFINITION") + print(f" - Minimum time (all resources): {year_min:.0f} years") + print(f" - Elevator-only time: {year_max:.0f} years") + print(f" - This range requires BOTH elevator and rockets") + + print(f"\n2. PARETO FRONT STATISTICS") + print(f" - Energy at fastest ({year_min:.0f}y): {df['energy_PJ'].max():.0f} PJ") + print(f" - Energy at slowest ({year_max:.0f}y): {df['energy_PJ'].min():.0f} PJ") + print(f" - Energy reduction: {df['energy_PJ'].max() - df['energy_PJ'].min():.0f} PJ " + f"({(1 - df['energy_PJ'].min()/df['energy_PJ'].max())*100:.1f}%)") + + print(f"\n3. KNEE POINT DETECTION RESULTS") + print("-" * 70) + print(f" {'Method':<20} {'Years':>8} {'Energy(PJ)':>12} {'Elev%':>8} {'Rocket%':>8}") + print("-" * 70) + + for method, data in knee_analysis['methods'].items(): + rocket_pct = (1 - data['elevator_fraction']) * 100 + print(f" {method:<20} {data['years']:>8.0f} {data['energy_PJ']:>12.0f} " + f"{data['elevator_fraction']*100:>7.1f}% {rocket_pct:>7.1f}%") + + print("-" * 70) + + rec = knee_analysis['recommended'] + scenario = calculate_scenario(rec['years']) + + print(f"\n4. RECOMMENDED OPTIMAL POINT") + print(f" ╔═══════════════════════════════════════════════════════╗") + print(f" ║ Method: Max Distance (most robust) ║") + print(f" ║ Completion Time: {rec['years']:>6.0f} years ║") + print(f" ║ Total Energy: {rec['energy_PJ']:>6.0f} PJ ║") + print(f" ║ Elevator Payload: {scenario['elevator_payload']/1e6:>6.1f} M tons ({rec['elevator_fraction']*100:.0f}%) ║") + print(f" ║ Rocket Payload: {scenario['rocket_payload']/1e6:>6.1f} M tons ({(1-rec['elevator_fraction'])*100:.0f}%) ║") + print(f" ║ Total Launches: {scenario['rocket_launches']:>6,} ║") + print(f" ║ Sites Required: {scenario['sites_used']:>6} ║") + print(f" ╚═══════════════════════════════════════════════════════╝") + + print(f"\n5. TRADE-OFF ANALYSIS") + + # 与最快方案对比 + fast_scenario = calculate_scenario(year_min + 0.5) + if fast_scenario: + time_gain = rec['years'] - year_min + energy_save = fast_scenario['total_energy_PJ'] - rec['energy_PJ'] + print(f"\n vs. Fastest ({year_min:.0f}y):") + print(f" - Extra time: +{time_gain:.0f} years") + print(f" - Energy saved: {energy_save:.0f} PJ ({energy_save/fast_scenario['total_energy_PJ']*100:.1f}%)") + print(f" - Efficiency: {energy_save/time_gain:.1f} PJ saved per extra year") + + # 与纯电梯对比 + elev_scenario = calculate_scenario(year_max - 0.5) + if elev_scenario: + time_save = year_max - rec['years'] + energy_cost = rec['energy_PJ'] - elev_scenario['total_energy_PJ'] + print(f"\n vs. Elevator-only ({year_max:.0f}y):") + print(f" - Time saved: {time_save:.0f} years") + print(f" - Extra energy: {energy_cost:.0f} PJ ({energy_cost/elev_scenario['total_energy_PJ']*100:.1f}%)") + print(f" - Cost: {energy_cost/time_save:.1f} PJ per year saved") + + print("\n" + "=" * 80) + print("INTERPRETATION:") + print(f"The knee point at {rec['years']:.0f} years represents the optimal trade-off") + print("where further extending the timeline yields diminishing returns,") + print("while shortening it incurs disproportionately higher energy costs.") + print("=" * 80) + + +# ============== 主程序 ============== + +if __name__ == "__main__": + print("=" * 80) + print("PARETO FRONT & KNEE POINT OPTIMIZATION") + print("Space Elevator + Rocket Combination for Moon Colony") + print("=" * 80) + + # ===== 分析1: 组合方案范围 (101-186年) ===== + print("\n" + "=" * 80) + print("ANALYSIS 1: Combined Scenario Range (Elevator + Rockets Required)") + print("=" * 80) + + df_combined, year_min, year_max = analyze_combined_range() + knee_combined = analyze_knee_points(df_combined) + + print_combined_range_report(df_combined, knee_combined, (year_min, year_max)) + + plot_combined_range_analysis(df_combined, knee_combined, (year_min, year_max)) + plot_combined_decision(df_combined, knee_combined, (year_min, year_max)) + + # ===== 分析2: 完整范围 (100-300年) ===== + print("\n" + "=" * 80) + print("ANALYSIS 2: Full Range (100-300 years)") + print("=" * 80) + + df_full = generate_pareto_front(year_min=100, year_max=300, num_points=500) + knee_full = analyze_knee_points(df_full) + + print_analysis_report(df_full, knee_full) + plot_pareto_analysis(df_full, knee_full) + plot_decision_recommendation(df_full, knee_full) + + # 保存数据 + df_combined.to_csv('/Volumes/Files/code/mm/20260130_b/pareto_combined_range.csv', index=False) + df_full.to_csv('/Volumes/Files/code/mm/20260130_b/pareto_front_data.csv', index=False) + print("\n数据已保存至 CSV 文件") + + print("\n" + "=" * 80) + print("分析完成!") + print("=" * 80)