Title: {Design A Starship for the Sublight Universe} Class: {blog} Date: {Wed May 26 13:54:15 EDT 2021} Content-Type: {html} Format: {clay} date: {Wed May 26 13:54:15 EDT 2021} owner: 619eb03b-0f7d-490f-a2ac-9eb72e4c789d uuid: 57750880-cd29-477d-8498-5f6b79723d30 --- BEGIN CONTENT --- set HEAD [my clay delegate head] $HEAD tag link href /content/57750880-cd29-477d-8498-5f6b79723d30/style.css type text/css $HEAD tag script src /content/57750880-cd29-477d-8498-5f6b79723d30/formulas.js type text/javascript $HEAD tag style content { .collapsible { background-color: #777; color: white; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: left; outline: none; font-size: 15px; } .active, .collapsible:hover { background-color: #555; } .collapsible:after { content: '\002B'; color: white; font-weight: bold; float: right; margin-left: 5px; } .active:after { content: "\2212"; } .collapsecontent { padding: 0 18px; max-height: 0; overflow: hidden; transition: max-height 0.2s ease-out; background-color: #f1f1f1; } } set javascript_buffer { var coll = document.getElementsByClassName("collapsible"); var i; for (i = 0; i < coll.length; i++) { coll[i].addEventListener("click", function() { this.classList.toggle("active"); var content = this.nextElementSibling; if (content.style.maxHeight){ content.style.maxHeight = null; } else { content.style.maxHeight = content.scrollHeight + "px"; } }); } window.vessel = {}; var obj; window.vessel.screenInput = function (name) { obj=this[name]=new Object(); obj.vessel=this; obj.name=name; obj.get = function () { var theInput= document.getElementById(this.name); this.value=parseFloat(theInput.value); return this.value; }; obj.put = function (newvalue) { var theInput= document.getElementById(this.name); this.value=parseFloat(newvalue); theInput.value = this.value; }; obj.calculate = function () { }; return obj; }; window.vessel.screenVariable = function (name) { obj=this[name]=new Object(); obj.vessel=this; obj.name=name; obj.get = function () { return this.value; }; obj.calculate = function () { }; obj.put = function (newvalue) { this.value=newvalue; try { var divtag = document.getElementById(this.name); divtag.innerHTML=parseFloat(newvalue); } catch { console.log("Screen element for " + this.name + " no present"); } }; return obj; }; window.vessel.restore = function (saveData) { Object.keys(saveData).forEach(function (pn) { try { window.vessel[pn].put(saveData[pn]); } catch { // Variable doesn't exist. Create it. window.vessel.screenVariable(pn,saveData[pn]); } }, saveData); }; window.vessel.save = function () { // Return a code string that will create a new instance containing our // own data. var ownData = {}; Object.keys(this).forEach(function (pn) { try { ownData[pn] = this[pn].get(); } catch { // Skip non-conforming variables } }, this); return ownData; }; window.vessel.DownloadJson = function () { var exportName = "sublight_vessel" var exportObj = this.save(); var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj)); var downloadAnchorNode = document.createElement('a'); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", exportName + ".json"); document.body.appendChild(downloadAnchorNode); // required for firefox downloadAnchorNode.click(); downloadAnchorNode.remove(); } function uploadJson(id, callback) { document.getElementById(id).onchange = function(evt) { try { let files = evt.target.files; if (!files.length) { alert('No file selected!'); return; } let file = files[0]; let reader = new FileReader(); const self = this; reader.onload = (event) => { callback(event.target.result); }; reader.readAsText(file); } catch (err) { console.error(err); } } } uploadJson('importJson', function (json) { console.log(json); window.vessel.restore(JSON.parse(json)); }); } my tag button class collapsible content {Introduction} set DIV [my tag div id wrap class collapsecontent] $DIV tag content {

I have been working with spreadsheets to estimate the mass and performance of spacecraft in the Sublight Universe. But over time, those spreadsheets have become increasingly hard to maintain. Also, being Microsoft Excel, strange things happen when I'm not looking at them.

After having yet another starship go pumpkin, I have decided to formalize my starship performance estimator into an easy-to-use browser application.

Rocket equations are pretty complicated. So before we start fiddling with exponets and running non-linear equations backwards, we should nail down all of the details that we don't intend to change. Ships are build for a purpose, and that purpose takes precedence over every other consideration.

} my tag div content { } proc ScreenVariable {T variable info} { upvar 1 javascript_buffer javascript_buffer set R [$T tag tr] $R column $variable if {![dict exists $info desc]} { $R tag th content $variable } else { $R tag th content [dict get $info desc] } set type real if {[dict exists $info type]} { set type [dict get $info type] } set default 0 if {[dict exists $info default]} { set default [dict get $info default] } set args [dict create type number id $variable name $variable] append javascript_buffer \n [string map [list %variable% $variable] {obj=window.vessel.screenVariable('%variable%');}] [$R tag td] tag div id $variable name $variable switch $type { text { append javascript_buffer { obj.get = function () { var theInput= document.getElementById(this.name); this.value=theInput.value; return this.value; }; obj.put = function (newvalue) { var theInput= document.getElementById(this.name); this.value=newvalue; theInput.value = this.value; }; } } } if {[dict exists $info calculate]} { [$R tag td] tag input type submit value {Calculate} onclick "window.vessel.$variable.calculate()\;" set body [dict get $info calculate] append javascript_buffer \n [string map [list %default% $default %variable% $variable %body% $body] {obj.calculate = function() {%body%};}] } else { [$R tag td] content " \;" } if {[dict exists $info units]} { $R tag td content [dict get $info units] } append javascript_buffer \n [string map [list %default% $default %variable% $variable] {obj.put(%default%);}] } proc ScreenInput {T variable info} { upvar 1 javascript_buffer javascript_buffer set R [$T tag tr] $R column $variable if {![dict exists $info desc]} { $R tag th content $variable } else { $R tag th content [dict get $info desc] } set type real if {[dict exists $info type]} { set type [dict get $info type] } set default 0 if {[dict exists $info default]} { set default [dict get $info default] } if {![string is double $default]} { set default "\'$default\'" } set args [dict create type number id $variable name $variable] append javascript_buffer \n [string map [list %variable% $variable] {obj=window.vessel.screenInput('%variable%');}] switch $type { text { dict set args type text append javascript_buffer { obj.get = function () { var theInput= document.getElementById(this.name); this.value=theInput.value; return this.value; }; obj.put = function (newvalue) { var theInput= document.getElementById(this.name); this.value=newvalue; theInput.value = this.value; }; } [$R tag td] tag input {*}$args } real { if {![dict exists $info input_param step]} { dict set info input_param step 0.1 } [$R tag td] tag input {*}$args } select { set SEL [[$R tag td] tag select {*}$args] foreach {enum desc} [dict get $info options] { $SEL tag option value $enum content $desc } append javascript buffer { obj.get = function() { var theInput= document.getElementById(this.name); if(theInput.selectedIndex==-1) return; return theInput.options[theInput.selectedIndex]; }; obj.put = function(newvalue) { var theInput= document.getElementById(this.name); var found = 0; this.value=newvalue; for (var i = 1; i < theInput.options; i++) { var ivalue = theInput.options[theInput.selectedIndex]; if(newvalue==ivalue) { found = 1; theInput.selectedIndex=i; } } if(!found) { var option = document.createElement("option"); option.text = newvalue; theInput.add(option); } }; } } boolean { dict set args type checkbox if {$default} { dict set args checked 1 } append javascript_buffer { obj.get = function() { var theInput= document.getElementById(this.name); return theInput.checked; }; obj.put = function(newvalue) { var theInput= document.getElementById(this.name); if(newvalue>0) { return theInput.checked=false; } else { return theInput.checked=true; } }; } [$R tag td] tag input {*}$args } default { [$R tag td] tag input {*}$args } } if {[dict exists $info calculate]} { [$R tag td] tag input type submit value {Calculate} onclick "window.vessel.$variable.calculate()\;" set body [dict get $info calculate] append javascript_buffer \n [string map [list %default% $default %variable% $variable %body% $body] {obj.calculate = function() {%body%};}] } else { [$R tag td] content " \;" } if {[dict exists $info units]} { $R tag td content [dict get $info units] } append javascript_buffer \n [string map [list %default% $default %variable% $variable] {obj.put(%default%);}] } set DIV [my tag div] $DIV tag p {Gross statistics for the vessel} set FORMNAME cargo set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T project_name { type text desc {A name for this project} units {tons} default {Fusion Starship} } ScreenVariable $T vehicle_wet_mass { type real desc {Total Launch Mass (m0)} units kg } ScreenVariable $T vehicle_dry_mass { type real desc {Total Launch Mass of vehicle (minus propellent) at launch (mf)} units kg } ScreenVariable $T vehicle_empty_mass { type real desc {Mass of Vehicle without propellent, fuel, cargo, or stores} units {kg} } ScreenVariable $T vehicle_total_deltav { type real desc {Total deltaV} units m/s } ScreenVariable $T vehicle_thrust_stages { type integer desc {Number of thrust stages} } ScreenVariable $T vehicle_volume { type real desc {Total enclosed volume} units {m3} } ScreenVariable $T vehicle_endurace { type int desc {Mission Length} units {days} } ScreenVariable $T vehicle_compliment { type int desc {Souls on board} units {} } my tag h1 {Step 1: Select Mission a/o Payload} my tag a href /content/57750880-cd29-477d-8498-5f6b79723d30/mission.clay content {Mission Calculation Details} target "_blank" my tag button class collapsible content {Voyage Parameters} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {Basic paramaters of how far you are flying, if this is a one-way trip, and how fast do you need to get there. Because of the nature of rocket math, it's best to work our stages backward. Stage 0 is always when the rocket is arriving at its final destination.} $DIV tag a href http://www.etoyoc.com/content/7fa323ff-a305-416c-8ce3-6347a007a0b5 content {Quick guide to rocket science} target "_blank" $DIV tag p { A mission with voyage_return=false has 2 stages: } $DIV tag p {A mission with voyage_return=true has 5 stages: } set FORMNAME cruise set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T structural_density { type real desc {Estimate of structure per unit volume required.} units {tons} default {kg/m3} default 173 } ScreenInput $T voyage_distance { type real desc {Distance of mission} units {au} default 4 input_param {step 0.1} } ScreenInput $T voyage_return { type boolean desc {True: Vessel returns to origin (5 stage), False: One way trip (2 stage)} units {true/false} default 1 input_param {min 0 max 1} } ScreenInput $T voyage_days { type integer desc {How many days will the crew be travelling for each "leg"} units {days} default 14 } ScreenInput $T voyage_loiter_days { type integer desc {Number of days vessel will loiter in mission area} units {days} default 0 } append javascript_buffer { function calculateVoyage() { window.vessel.vehicle_dry_mass.calculate(); var voyage_dry_mass_estimate = window.vessel.vehicle_dry_mass.get(); var voyage_distance = window.vessel.voyage_distance.get(); var voyage_return = window.vessel.voyage_return.get(); var voyage_days = window.vessel.voyage_days.get(); var voyage_loiter_days = window.vessel.voyage_loiter_days.get(); var day_total = 0; var travel_seconds = 0; var thrust_stages = 0; if(voyage_return) { day_total = (voyage_days*2); day_total += voyage_loiter_days; thrust_stages=4; } else { day_total = voyage_days; thrust_stages=2; } travel_seconds = voyage_days * 3600*24; var deltav = (voyage_distance * 149597900000 / travel_seconds); var total_deltav=deltav*thrust_stages; window.vessel.vehicle_total_deltav.put(total_deltav); window.vessel.vehicle_dry_mass.put(voyage_dry_mass_estimate); window.vessel.vehicle_thrust_stages.put(thrust_stages); window.vessel.vehicle_endurace.put(day_total); } } my tag input type submit value {Calculate} onclick "calculateVoyage()\;" my tag button class collapsible content {Mission Equipment} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {If this is a science or reconnaissance mission, what sort of equipment are you dragging along, and how long will you be observing/patrolling/spying.} set FORMNAME missioneqpt set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T mission_equipment_mass { type real desc {Mission Equipment Mass} units kg default 0 calculate { var value=this.vessel.mission_equipment_volume.get()* this.vessel.mission_equipment_rho.get(); this.put(value); this.vessel.payload_mass.calculate(); } } ScreenInput $T mission_equipment_volume { type real desc {Mission Equipment Volume} units {k3} default 0 calculate { var value=this.vessel.mission_equipment_mass.get()/ this.vessel.mission_equipment_rho.get(); this.put(value); this.vessel.payload_mass.calculate(); } } ScreenInput $T mission_equipment_rho { type real desc {Density of Mission Gear} units {kg/m3} default 4000 calculate { var value=this.vessel.mission_equipment_mass.get() / this.vessel.mission_equipment_volume.get(); this.put(value); } } ScreenInput $T mission_equipment_constant_power { type real desc {How much power does this equipment draw for the entire voyage} units {kwh/day} default 0 } ScreenInput $T mission_equipment_loiter_power { type real desc {How much power does this equipment draw while the vessel is in the loiter stage?} units {kwh/day} default 0 } my tag button class collapsible content {Cargo Hauling} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {If this mission carrying cargo to be dropped off, or perhaps ordinance to be released?} set FORMNAME cargo set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T cargo_release_release_stage { type integer desc {What stage is the cargo released?} default 3 } ScreenInput $T cargo_release_mass { desc {Cargo Mass} units {kg} default 0 calculate { var value=this.vessel.cargo_release_volume.get()* this.vessel.cargo_release_rho.get(); this.put(value); this.vessel.payload_mass.calculate(); } } ScreenInput $T cargo_release_volume { desc {Cargo Volume} units {m3} default 0 calculate { var value=this.vessel.cargo_release_mass.get() / this.vessel.cargo_release_rho.get(); this.put(value); this.vessel.payload_mass.calculate(); } } ScreenInput $T cargo_release_rho { desc {Density of Cargo} units {kg/m3} default 1100 calculate { var value=this.vessel.cargo_release_mass.get() / this.vessel.cargo_release_volume.get(); this.put(value); this.vessel.payload_mass.calculate(); } } ScreenInput $T cargo_constant_power { desc {Power required to maintain cargo} units {kwh/day} } my tag button class collapsible content {Towing / Salvage / Resource Collecting} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {Does your mission involve picking something up and bringing it back? Perhaps mining an asteroid or towing another vessel?} set FORMNAME resource set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T cargo_collect_stage { desc {Stage to collect material} default 3 } ScreenInput $T cargo_collect_mass { desc {Collect Mass - Materials collected from remote site} units {kg} default 0 calculate { var value=this.vessel.cargo_collect_volume.get()* this.vessel.cargo_collect_rho.get(); this.put(value); } } ScreenInput $T cargo_collect_volume { desc {Collect Volume} units {m3} default 0 calculate { var value=this.vessel.cargo_collect_mass.get() / this.vessel.cargo_collect_rho.get(); this.put(value); } } ScreenInput $T cargo_collect_rho { desc {Density of collected material} units {kg/m3} default 1100 calculate { var value=this.vessel.cargo_collect_mass.get() / this.vessel.cargo_collect_volume.get(); this.put(value); } } ScreenVariable $T payload_structure { desc {Mass of structure to hold payload} units {kg} } ScreenVariable $T payload_volume { desc {Max Total Volume for payload} units {m3} } ScreenVariable $T payload_mass { desc {Max Total Mass of Payload (minus structure)} units {kg} } my tag input type submit value {Calculate Payload} onclick "calculatePayload()\;" append javascript_buffer { function calculatePayload() { var payload_volume = 0; var volume; var payload_mass = 0; var mass; var structure_density = this.vessel.structural_density.get(); volume = this.vessel.mission_equipment_volume.get(); if (volume < 0) { this.vessel.mission_equipment_volume.calculate(); volume = this.vessel.mission_equipment_volume.get(); } payload_volume += volume; mass = this.vessel.mission_equipment_mass.get(); if (mass < 0) { this.vessel.mission_equipment_mass.calculate(); mass = this.vessel.mission_equipment_mass.get(); } payload_mass += mass; var cargo_release_stage = this.vessel.cargo_release_release_stage.get(); var collect_stage = this.vessel.cargo_collect_stage.get(); var volume_cargo = this.vessel.cargo_release_volume.get(); if (volume_cargo <= 0) { this.vessel.cargo_release_volume.calculate(); volume_cargo = this.vessel.cargo_release_volume.get(); } var volume_collect = this.vessel.cargo_collect_volume.get(); if (volume_collect <= 0) { this.vessel.cargo_collect_volume.calculate(); volume_collect = this.vessel.cargo_collect_volume.get(); } if(cargo_release_stage > collect_stage) { payload_volume += volume_cargo + volume_collect; } else { if(volume_collect > volume_cargo) { payload_volume += volume_collect; } else { payload_volume += volume_cargo; } } var mass_cargo = this.vessel.cargo_release_mass.get(); if (mass_cargo < 0) { this.vessel.cargo_release_mass.calculate(); mass_cargo = this.vessel.cargo_release_mass.get(); } var mass_collect = this.vessel.cargo_collect_mass.get(); if (mass_collect < 0) { this.vessel.cargo_collect_mass.calculate(); mass_collect = this.vessel.cargo_collect_mass.get(); } if(cargo_release_stage > collect_stage) { payload_mass += mass_cargo + mass_collect; } else { if(mass_collect > mass_cargo) { payload_mass += mass_collect; } else { payload_mass += mass_cargo; } } var structure_mass = payload_volume * structure_density; this.vessel.payload_mass.put(payload_mass); this.vessel.payload_volume.put(payload_volume); this.vessel.payload_structure.put(structure_mass); } } my tag button class collapsible content {Passengers and Crew} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {How many people are going to be along for the voyage?} $DIV tag a href /content/57750880-cd29-477d-8498-5f6b79723d30/crew.clay content {Crew Calculation Details} target "_blank" set FORMNAME crewform set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenInput $T souls_aboard { desc {Number of souls aboard} units {people} default 12 } ScreenInput $T survival_endurance { desc {Number of days the crew are sustained in case of loss of main power} units {days} default 14 } ScreenInput $T habitat_volume_per_capita { type real desc {Volume per soul aboard} units {m3} default 75 } ScreenInput $T waste_water_recycle_factor { type real desc {Percentage of waste water that is recycled} units {0.0-1.0} default 0.5 } ScreenInput $T incinerate_trash { type boolean desc {Incinerate trash and sewage} default 1 } ScreenInput $T potable_propellent { type boolean desc {Crew potable water can be drawn from propellent tanks} default 1 } ScreenInput $T wastewater_propellent { type boolean desc {Waste water contributes to propellent} default 1 } ScreenVariable $T total_accommodation_volume { type real desc {Total volume enclosed by the accommodation} units {m3} } ScreenVariable $T total_accommodation_mass { type real desc {Total volume enclosed by the accommodation} units kg } ScreenVariable $T accommodation_habitat_volume { type real desc {Living Quarters} units {m3} } ScreenVariable $T accommodation_habitat_mass { type real desc {Mass of life support equipment and fixtures} units kg } ScreenVariable $T accommodation_atmosphere_mass { type real desc {Mass of cabin atmosphere and compressed reserves} units kg } ScreenVariable $T accommodation_structure_mass { type real desc {Mass of the accommodation structure} units kg } ScreenVariable $T accommodation_daily_power { type real desc {Power usage of habitat} units kwh/day } ScreenInput $T rotational_radius { type real desc {Outer radius of rotational habitat} units {m} default 50 } ScreenInput $T rotational_gravity { type real desc {Force of rotational gravity for cruise. If 0 flywheel omitted.} units {G} default 0.25 } my tag input type submit value {Calculate Habitat} onclick "calculate_accommodation()\;" $DIV tag table id accommodation_report name accommodation_report border 1 append javascript_buffer { function TableRow(table) { var row; var col; var idx=-1; row=table.insertRow(); for (var i = 1; i < arguments.length; i++) { col=row.insertCell(i-1); col.innerHTML = arguments[i]; } } function calculate_accommodation() { var table = document.getElementById("accommodation_report"); table.innerHTML=""; var structure_density = this.vessel.structural_density.get(); var population=this.vessel.souls_aboard.get(); var endurance=this.vessel.vehicle_endurace.get(); var survival_endurance = this.vessel.survival_endurance.get(); var waste_f = this.vessel.waste_water_recycle_factor.get(); if (endurance < survival_endurance) { endurance=survival_endurance; } var total_accommodation_volume = 0; var total_accommodation_mass = 0; this.vessel.vehicle_compliment.put(population); TableRow(table,"Souls Aboard:",population); TableRow(table,"Habitat Endurace:",endurance,"days"); TableRow(table,"Survival Endurace:",survival_endurance,"days"); var incinerate_trash = this.vessel.incinerate_trash.get(); var potable_propellent = this.vessel.potable_propellent.get(); var wastewater_propellent = this.vessel.wastewater_propellent.get(); var row=table.insertRow(); var col=row.insertCell(0); col.colSpan=10; col.headers=1; col.innerHTML = "Policies"; TableRow(table,"incinerate_trash",incinerate_trash); TableRow(table,"potable_propellent",potable_propellent); TableRow(table,"wastewater_propellent",wastewater_propellent); // Calculate the inhabited structure itself var hv_percapita = this.vessel.habitat_volume_per_capita.get(); var hab_volume=population*hv_percapita; var hab_floor_area = hab_volume/3; var hab_mass = hab_floor_area * 310; total_accommodation_volume += hab_volume; total_accommodation_mass += hab_mass; // Calculate the life support equipment var hab_eqpt_mass = population*750; var hab_eqpt_volume = hab_eqpt_mass/2000; total_accommodation_volume += hab_eqpt_volume; total_accommodation_mass += hab_eqpt_mass; // Calculate the mass and volume of stored food var food_mass = population*survival_endurance*(0.83); food_mass += population*(endurance-survival_endurance)*(0.83); var food_volume = food_mass/1000; total_accommodation_volume += food_volume; total_accommodation_mass += food_mass; // Calculate the mass and volume of potable water // We always have a tank with survival_endurance worth of water var potable_water_mass = population*survival_endurance*11; // We also need at place to dump at least survival_endurance worth of sewage var sewage_mass = population*survival_endurance*(11+0.15); // And a place for survival_endurance worth of trash var trash_mass = population*survival_endurance*(0.12); // From here we have to look at the policies that were selected // Calculate sewage tank size if(incinerate_trash || wastewater_propellent) { // Assume we collect some sort of waste that for whatever reason can't go in // the engine sewage_mass += population*(endurance-survival_endurance)*(11+0.15)*0.01; } else { sewage_mass += population*(endurance-survival_endurance)*(11+0.15)*(1.0-waste_f); } var sewage_volume = sewage_mass / 1100; // Calculate potable_water tank size if(potable_propellent) { // Water is topped off from the propellent supply } else { potable_water_mass += population*(endurance-survival_endurance)*(11)*(1.0-waste_f); } var potable_water_volume = potable_water_mass/1000; if(incinerate_trash) { // Assume we collect some sort of waste that for whatever reason can't go in // the engine trash_mass += population*(endurance-survival_endurance)*(0.12)*0.01; } else { trash_mass += population*(endurance-survival_endurance)*(0.12); } var trash_volume = trash_mass / 1100; total_accommodation_volume += potable_water_volume + sewage_volume + trash_volume; total_accommodation_mass += potable_water_mass; var atmosphere_mass = total_accommodation_volume * 1.225 * 3; var accommodation_structure_mass = (total_accommodation_volume-hab_volume) * structure_density; var accommodation_daily_power = 34*population; // Provide 24 hours of electrical power // J/kg ratio Based on lithium/air batteries // https://www.allaboutbatteries.com/battery-energy.html // Assuming that in a survival sitation the system can run on 25% power var accommodation_backup_battery_mass = accommodation_daily_power * 1000 / 200; var accommodation_backup_battery_volume = accommodation_daily_power / 12; total_accommodation_mass += accommodation_backup_battery_mass; total_accommodation_volume += accommodation_backup_battery_volume; var row=table.insertRow(); var col=row.insertCell(0); col.colSpan=10; col.headers=1; col.innerHTML = "Volume Allocation"; TableRow(table,"","main living area",hab_volume,"m3"); TableRow(table,"","support equipment",hab_eqpt_volume,"m3"); TableRow(table,"","auxiliary power",accommodation_backup_battery_volume,"m3"); TableRow(table,"","stored food",food_volume,"m3"); TableRow(table,"","potable water tanks",potable_water_volume,"m3"); TableRow(table,"","sewage tanks",sewage_volume,"m3"); TableRow(table,"","trash stowage",trash_volume,"m3"); TableRow(table,"total","",total_accommodation_volume,"m3"); var row=table.insertRow(); var col=row.insertCell(0); col.colSpan=10; col.headers=1; col.innerHTML = "Mass Allocation"; TableRow(table,"","living area structure and fixtures",hab_mass,"kg"); TableRow(table,"","life support equipment",hab_eqpt_mass,"kg"); TableRow(table,"","auxiliary power",accommodation_backup_battery_mass,"kg"); TableRow(table,"","stored food",food_mass,"kg","start"); TableRow(table,"","stored potable water",potable_water_mass,"kg","start"); TableRow(table,"","sewage",sewage_mass,"kg","end"); TableRow(table,"","trash",trash_mass,"kg","end"); TableRow(table,"","atmosphere + 10 changes",atmosphere_mass,"kg","end"); TableRow(table,"","storage structures",accommodation_structure_mass,"kg","end"); TableRow(table,"total","",total_accommodation_mass,"kg"); total_accommodation_mass += accommodation_structure_mass + atmosphere_mass ; this.vessel.accommodation_atmosphere_mass.put(atmosphere_mass); this.vessel.accommodation_habitat_volume.put(hab_volume); this.vessel.accommodation_habitat_mass.put(hab_mass+hab_eqpt_mass); this.vessel.accommodation_structure_mass.put(accommodation_structure_mass); this.vessel.accommodation_daily_power.put(accommodation_daily_power); //this.vessel.provision_mass.put(pmass); this.vessel.total_accommodation_volume.put(total_accommodation_volume); this.vessel.total_accommodation_mass.put(total_accommodation_mass); //stageElement.innerHTML += massReport; //stageElement.innerHTML += volumeReport; //stageElement.innerHTML += ""; } } my tag button class collapsible content {Design a Reactor} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {Here is where we spec out the reactor and engines to use} set FORMNAME reactor set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenVariable $T vehicle_dry_mass_without_engines { type real desc {The dry mass of the ship so far, minus the engineering plant.} units {kg} default 100000 calculate { var dry_mass_ne = 0; calculateVoyage(); calculatePayload(); calculate_accommodation(); dry_mass_ne+=this.vessel.payload_mass.get(); dry_mass_ne+=this.vessel.payload_structure.get(); dry_mass_ne+=this.vessel.total_accommodation_mass.get(); this.put(dry_mass_ne); } } ScreenInput $T vehicle_dry_mass_estimate { type real desc {Design estimate of the mass of the vehicle, minus propellent} units {kg} default 1000000 input_param {step 10000} } ScreenVariable $T power_systems_total { type real desc {Peak demand of vessel's internal systems} units {W} default 100000 calculate { var total_kwh=0; total_kwh += this.vessel.mission_equipment_constant_power.get(); total_kwh += this.vessel.mission_equipment_loiter_power.get(); total_kwh += this.vessel.cargo_constant_power.get(); total_kwh += this.vessel.accommodation_daily_power.get(); var total_power = total_kwh * 1000/ 24; this.put(total_power); } } ScreenInput $T design_accelleration { type real desc {Accelleration engines would impart on empty ship (9.8 = 1G)} units {m/s2} step {0.05} default 10 } ScreenVariable $T design_thrust { type real desc {Design Thrust of the vessel} units {N} default 100000 calculate { var dry_mass_est = window.vessel.vehicle_dry_mass_estimate.get(); var accell = window.vessel.design_accelleration.get(); this.put(dry_mass_est*accell); } } ScreenVariable $T power_propulsion_total { type real desc {Reactor output required to sustain max thrust} units {W} default 100000 calculate { this.vessel.design_thrust.calculate(); var power_thrust = this.vessel.design_thrust.get(); // Assume an 80 % conversion rate this.put(power_thrust/0.8); } } ScreenInput $T power_design_output { type real desc {Net power output of reactor} units {W} default 100000 calculate { this.vessel.power_systems_total.calculate(); this.vessel.power_propulsion_total.calculate(); var power_electric = this.vessel.power_systems_total.get(); var power_prop = this.vessel.power_propulsion_total.get(); // Add a 25% growth factor var value=(power_electric+power_prop)*1.1; this.put(value); } } ScreenInput $T power_reactor_wall_density { type real desc {Density of the material. (Default: Titanium)} units {kg/m3} default 4500 } ScreenInput $T power_reactor_wall_thickness { type real desc {Thickness of reactor wall. (Default: Titanium)} units {m} default 0.1 } ScreenInput $T power_reactor_wall_youngs_modulus { type real desc {Tensile strength of reactor wall. (Default: Titanium)} units {Pa} default 730000000000 } ScreenInput $T power_reactor_wall_yield_stress { type real desc {Tensile strength of reactor wall. (Default: Titanium)} units {Pa} default 120000000 } ScreenInput $T power_reactor_wall_safety_factor { type real desc {Safety factor for reactor wall} units {} default 3.5 } ScreenVariable $T power_reactor_mass { type real desc {Thermonuclear pellet size} units {kg} default 0 } ScreenVariable $T power_thermonuclear_pellet { type real desc {Thermonuclear pellet size} units {kg} default 0 } ScreenVariable $T power_thermonuclear_duration { type real desc {Detonation time of nuclear reaction} units {s} default 0 } ScreenInput $T power_thermonuclear_requency { type real desc {Time between detonations} units {hZ} default 1.0 } ScreenVariable $T power_reactor_inner_radius { type real desc {Inner radius of the reactor chamber} units {m} default 0 } ScreenVariable $T vessel_engineering_volume { type real desc {Volume of engineering plant} units {m3} default 0 } ScreenVariable $T vessel_engineering_mass { type real desc {Mass of engineering plant} units {kg} default 0 } ScreenVariable $T voyage_fusion_fuel { type real desc {Mass of fusion fuel for voyage} units {kg} default 0 } $DIV tag p $DIV tag div id reactor_report name reactor_report border 1 my tag input type submit value {Calculate Engineering} onclick "calculateReactor()\;" append javascript_buffer { function calculateReactor() { this.vessel.power_design_output.calculate(); var peak_output = this.vessel.power_design_output.get(); var power_thermonuclear_requency = this.vessel.power_thermonuclear_requency.get(); // Assume 60% loss between detonation and actual power production var energy_detonation = peak_output / power_thermonuclear_requency / 0.6; var power_kiloton = energy_detonation / 4184000000000; var total_volume =0; var total_mass = 0; // Calculate the dry mass minus the engineering plant this.vessel.vehicle_dry_mass_without_engines.calculate(); var dry_mass_ne = this.vessel.vehicle_dry_mass_without_engines.get();; var Y0 = power_kiloton; // Formulas from https://nuclearweaponsedproj.mit.edu/fireball-size-effects var duration = Math.pow(Y0, 0.45) * 0.2; var firballmin = (Math.pow(Y0, 0.4) * 90) * 0.3048; var detonation_velocity = firballmin / duration; // Fireball radius at breakaway for air bust (the fireball does not touch the ground) var firballair = (Math.pow(Y0, 0.4) * 110) * 0.3048; // Fireball radius at breakaway for contact surface burst (the fireball touch the ground) //var firballground = (Math.pow(Y0, 0.4) * 145) * 0.3048; // Approximate maximum height of burst for appreciable local fallout //var Height = (Math.pow(Y0, 0.4) * 180) * 0.3048; var force_detonation = energy_detonation; var reportElement = document.getElementById("reactor_report"); reportElement.innerHTML = ""; reportElement.innerHTML += "
Peak Output: " + peak_output + " W (" + power_kiloton + " kilotons of tnt)"; reportElement.innerHTML += "
Detonation speed: " + detonation_velocity + " m/s"; reportElement.innerHTML += "
Detonation Fireball radius " + firballmin + " M"; reportElement.innerHTML += "
Detonation Fireball air burst " + firballair + " M"; reportElement.innerHTML += "
Detonation Duration " + duration + " s"; reportElement.innerHTML += "
Detonation Energy " + energy_detonation + " J"; reportElement.innerHTML += "
Detonation Force " + force_detonation + " N"; // https://en.wikipedia.org/wiki/Nuclear_weapon_yield // https://en.wikipedia.org/wiki/Castle_Bravo // Based on Castle bravo test 400kg of Lithium deuteride (40% of the lithum as Lithium 6) // Burned 25% of the fuel. Ranges between 15% to 40% plausible // Yield: 15 megatons = 50 * 4184000000000000 joules // We'll go conservative and say our explosions are also 25% efficient var pellet_mass = peak_output * 4.780114722753346e-16 / 0.25; // https://pubchem.ncbi.nlm.nih.gov/compound/Lithium-deuteride#section=Chemical-and-Physical-Properties // Lithium Deuteride molecular mass 9.03010521, density 782.01 kg/m^3 var pellet_volume = pellet_mass / 782.01; reportElement.innerHTML += "
Calculated Pellet Size " + pellet_mass + " kg / " + pellet_volume + " m3"; this.vessel.power_thermonuclear_pellet.put(pellet_mass); this.vessel.power_thermonuclear_duration.put(duration); // https://apps.dtic.mil/dtic/tr/fulltext/u2/a507528.pdf // Calculate the properties of the reactor vessel var youngs_modulus = this.vessel.power_reactor_wall_youngs_modulus.get(); var yield_stress = this.vessel.power_reactor_wall_yield_stress.get(); var tensile_safety = this.vessel.power_reactor_wall_safety_factor.get(); var tensile_density = this.vessel.power_reactor_wall_density.get(); var wall_minimum_area = (force_detonation / yield_stress); var impulse_per_area = force_detonation/wall_minimum_area; // Solve for a sphere of that area var wall_radius = Math.sqrt(wall_minimum_area/(4*Math.PI)); this.vessel.power_reactor_inner_radius.put(wall_radius); var power_reactor_wall_thickness = this.vessel.power_reactor_wall_thickness.get(); var hoop_strain = impulse_per_area * wall_radius / (2*power_reactor_wall_thickness); var strain_ratio = hoop_strain*tensile_safety / (yield_stress); if (strain_ratio > 1.0 || strain_ratio < 0.5) { var wall_thickness = (impulse_per_area*tensile_safety)*wall_radius/(2*yield_stress) reportElement.innerHTML += "
Reactor Chamber wall thickness " + wall_thickness + " m"; var new_thick = wall_thickness * strain_ratio; if(new_thick>wall_thickness) { reportElement.innerHTML += "
Reactor wall too thin (strain ratio: "+ strain_ratio + ") _changed to " + new_thick + " m"; this.vessel.power_reactor_wall_thickness.put(new_thick); } } reportElement.innerHTML += "
Reactor Wall hoop strain " + hoop_strain + " (" + (hoop_strain/yield_stress)*100 + " of yield stress)"; reportElement.innerHTML += "
Reactor Chamber inner radius " + wall_radius + " m"; var reactor_volume = 4.0/3.0*Math.PI*wall_radius**3; reportElement.innerHTML += "
Reactor Chamber interior volume " + reactor_volume + " m"; var outer_volume = 4.0/3.0*Math.PI*(wall_radius+power_reactor_wall_thickness)**3; var reactor_mass = (outer_volume-reactor_volume)*tensile_density*0.9; reportElement.innerHTML += "
Reactor Chamber mass" + reactor_mass + " kg"; // Assume we need four times the volume as the reactor for support equipment total_volume += outer_volume*4; // Assume half that space is full of equipment at 4000 kg/m^3 total_mass += reactor_mass+(outer_volume*1.5*4000); var power_systems_total = this.vessel.power_systems_total.get(); // Based on the shipping manifest for a real 250mw steam plant, assuming a linear relationship // volume: 2600 m^3 // mass: 1100000 kg // output: 250000000 w // The steam plant operates on the waste heat from the propulsion system var generator_volume = 2600/250000000 * power_systems_total*1.5; var generator_mass = 1100000/250000000 * power_systems_total*1.5; reportElement.innerHTML += "
Electrical generator output: " + power_systems_total + " W"; reportElement.innerHTML += "
Electrical generator volume: " + generator_volume + " m3"; reportElement.innerHTML += "
Electrical generator mass: " + generator_mass + " kg"; total_volume += generator_volume; total_mass += generator_mass; var new_dry_mass = parseInt(total_mass)+parseInt(dry_mass_ne); // Add on another 5% for maneuvering system new_dry_mass += new_dry_mass*0.1; reportElement.innerHTML += "
Total engineering plant mass: " + total_mass + " kg"; reportElement.innerHTML += "
Total engineering plant volume: " + total_volume + " m3"; reportElement.innerHTML += "
New Dry Mass Estimate: " + new_dry_mass + " kg"; var vehicle_volume = total_volume + this.vessel.total_accommodation_volume.get()+this.vessel.payload_volume.get(); this.vessel.vessel_engineering_volume.put(total_volume); this.vessel.vessel_engineering_mass.put(total_mass); this.vessel.vehicle_dry_mass_estimate.put(new_dry_mass); this.vessel.vehicle_dry_mass.put(new_dry_mass); this.vessel.vehicle_dry_mass_calculated.put(new_dry_mass); this.vessel.vehicle_volume.put(vehicle_volume); } } my tag button class collapsible content {Full System Analysis} set DIV [my tag div id wrap class collapsecontent] $DIV tag p {Putting in the reactor, engines, and other fun stuff} my tag a href /content/57750880-cd29-477d-8498-5f6b79723d30/reactor.clay content {Reactor Calculation Details} target "_blank" $DIV tag p {We are playing a guessing game with physics. The heavier our space craft is, the more engine we will need to get it up to the speed we want it to go. But the curves on those equations are not-exactly easy to estimate. So we start with a guess, solve the equation, and then keep refining our guess until the calculated mass of the ship matches our hunch.} $DIV tag p {When estimating tonnage, here are some figures:} set ul [$DIV tag UL] foreach {mass desc} { 16000 {a greyhound bus} 36000 {a fully loaded semi-truck} 80000 {a fully loaded Boeing 737-800} 450000 {a fully loaded Boeing 747-8} 2800000 {a saturn V rocket} 4000000 {a modern naval frigate} 9500000 {a modern naval destroyer} 50000000 {a cruise ship} 100000000 {an aircraft carrier} 350000000 {the Empire State Building} 800000000 {the Pentagon} 6000000000 {Hoover Dam} 12100000000000000 {Lake Superior (the water at least)} } { $ul item "$mass - $desc" } set FORMNAME engineeringform set FORM [$DIV tag form action {} id $FORMNAME onsubmit "return false\;"] set T [[$FORM tag table class {table table-bordered table-striped}] tag tbody] ScreenVariable $T vehicle_dry_mass_calculated { type real desc {The final calculated dry mass of the vehicle with the engineering plant attached} units {kg} default 100000 } ScreenVariable $T propellent_flow_rate { type real desc {Rate of propellent expended} units {kg/s} default 0 } ScreenInput $T reserve_deltav { type real desc {DeltaV Reserved for Mission} units {m/s} default 0 } ScreenInput $T power_storage { type real desc {Auxiliary power stored in flywheels} units {kwh} default 2500 } ScreenVariable $T velocity_exhaust { type real desc {Effective exhaust velocity of propulsion technology} units {m/s} default 2000000.0 } ScreenVariable $T propellent_efficiency { type real desc {Efficiency of converting reactor power to propellent kinetic energy} units {kg/s} default 0.8 } $DIV tag p $DIV tag div id engineering_report name engineering_report border 1 my tag input type submit value {Calculate Engineering} onclick "calculateEngineering()\;" append javascript_buffer { // Water: specific_heat=1.33, molecular_mass=18 // molecular Hydrogen: specific_heat=1.41, molecular_mass=2 // atomic Hydrogen: specific_heat=1.41, molecular_mass=1 function vePropellent(temperature,molecular_mass,specific_heat) { } function calculateEngineering() { //var power_reactor_type = window.vessel.power_reactor_type.get(); //var propulsion_reactor_type = window.vessel.propulsion_reactor_type.get(); var propellent_flow_rate = window.vessel.propellent_flow_rate.get(); var reserve_deltav = window.vessel.reserve_deltav.get(); var dry_mass_est = window.vessel.vehicle_dry_mass_estimate.get(); var rotational_gravity = window.vessel.rotational_gravity.get(); var endurance=this.vessel.vehicle_endurace.get(); var reportElement = document.getElementById("engineering_report"); reportElement.innerHTML = ""; // Calculate the dry mass minus the engineering plant var dry_mass_ne = 0; calculateVoyage(); calculatePayload(); calculate_accommodation(); dry_mass_ne+=this.vessel.payload_mass.get(); dry_mass_ne+=this.vessel.payload_structure.get(); dry_mass_ne+=this.vessel.total_accommodation_mass.get(); window.vessel.vehicle_dry_mass_without_engines.put(dry_mass_ne); reportElement.innerHTML += "
Vehicle Dry Mass " + dry_mass_ne + " kg"; // reserve power // Based on Beacon BP- 400 Flywheel // https://www.energy.gov/sites/prod/files/2015/01/f19/02_2014Storage_PeerRev_Areseneaux_Jim_20MW_Flywheel_Energy_Storage_Plant_140918.pdf var kwh_to_kg = 25 / 1133.981; // Need to store survival_endurance worth or power to run life support equipment // Vehicle will need a reaction control system. This is based on the Control moment // gryo from sylab which had three 63.5 kg gyros for 90,610 kg of space station // Calculate a counter-rotating gyro // Assume vessel is rotating at 4 rpm var vessel_outer_radius = this.vessel.rotational_radius.get(); var vessel_rotation_rads = Math.sqrt(rotational_gravity*9.8/vessel_outer_radius); var vessel_rpm = vessel_rotation_rads*30/Math.PI; if (vessel_rpm > 4) { // Cap rotation at 4 rpm reportElement.innerHTML += "
Vessel Rotation capped at 4 rpm."; vessel_rpm=4.0; vessel_rotation_rads = vessel_rpm * Math.PI/30; } // Reaction control system based on skylab // 14000kg spacecraft controlled by 6 x 64 kg control wheels var reaction_control_system = dry_mass_est * (6*64)/14000; // stainless steel //var flywheel_material_density = 7900; //var flywheel_yield_strength = 520000000; // structural steel //var flywheel_material_density = 7800; //var flywheel_yield_strength = 250000000; // maraging steel // var flywheel_material_density = 8200; // var flywheel_yield_strength = 2450000000; // titanium //var flywheel_material_density = 4500; //var flywheel_yield_strength = 940000000; // Sci-Fi Material //var flywheel_material_density = 100; //var flywheel_yield_strength = 2450000000; var engineering_mass = 0; var engineering_volume = 0; // VE for diffe // Assuming water heated to 120,000,000 degress K- var ve = 281000.0; var total_deltav = this.vessel.vehicle_total_deltav.get(); var thrust_stages = this.vessel.vehicle_thrust_stages.get(); var vehicle_wet_mass_estimate = Math.exp(total_deltav/ve)*dry_mass_est; // Accellerate the vessel at 1 m/s/s var thrust = window.vessel.design_thrust.get(); var mdot = (thrust/ve); reportElement.innerHTML += "
Mass Flow Rate " + mdot + " kg/s" reportElement.innerHTML += "
Effective Exhaust Velocity of propellent " + ve + " m/s" window.vessel.propellent_flow_rate.put(mdot); window.vessel.velocity_exhaust.put(ve); var wet_mass = Math.exp(total_deltav/ve)*dry_mass_est; window.vessel.vehicle_wet_mass.put(wet_mass); var stage_deltav = total_deltav/thrust_stages; var payload_mass = dry_mass_est; var propellant_mass = 0; var fuel_mass = 0; var fuel_total=0; var voyage_days = window.vessel.voyage_days.get(); var voyage_distance = window.vessel.voyage_distance.get(); var voyage_distance_m = voyage_distance * 149597900000; var max_thrust = 0; for (var i = 0; i < thrust_stages; i++) { var stage_wet_mass = Math.exp(stage_deltav/ve)*payload_mass; propellant_mass = stage_wet_mass- payload_mass; var burn_time = (stage_wet_mass*ve/thrust)*(1-Math.exp(-stage_deltav/ve)); var burn_distance = stage_deltav*(stage_wet_mass/mdot)*(1-(payload_mass/stage_wet_mass))*(Math.log(stage_wet_mass/payload_mass)+1)/149597900000; reportElement.innerHTML += "

Stage " + (thrust_stages - i) + "

" reportElement.innerHTML += "
Deltav " + parseInt(stage_deltav) + " m/s"; reportElement.innerHTML += "
Start Mass " + parseInt(stage_wet_mass) + " kg"; reportElement.innerHTML += "
Final Mass " + parseInt(payload_mass) + " kg"; var vessel_moment = 0.5*payload_mass*(vessel_outer_radius**2); reportElement.innerHTML += "
Rotational moment of inertia " + vessel_moment + " "; var vessel_rotational_energy = vessel_moment*vessel_rotation_rads; reportElement.innerHTML += "
Vessel Rotational Energy: " + vessel_rotational_energy + " J + (at " + vessel_rpm + " rpm)"; reportElement.innerHTML += "
Propellent Mass " + parseInt(propellant_mass) + " kg"; reportElement.innerHTML += "
Mass FLow " + parseInt(mdot*1000)/1000.0 + " kg/s"; reportElement.innerHTML += "
Burn Time " + parseInt(burn_time/3600*1000)/1000.0 + " hours " + burn_time + " s"; reportElement.innerHTML += "
Burn Distance " + parseInt(burn_distance*1000)/1000.0 + " au"; payload_mass=stage_wet_mass; } // Sci-Fi Setting 5.05209031051451E-11 kg/kwh // to 2.10E+06 m/s // https://www.iter.org/sci/FusionFuels // 1000 mW/yr ~ 250 kg fuel // 3.5064E+07 W / kg // 223,000 } } my tag script type "text/javascript" content $javascript_buffer