From 0f2d35c33f28c790bc22569eb727e47350bd2dc4 Mon Sep 17 00:00:00 2001 From: Nicolas Gomez Date: Wed, 6 Nov 2024 14:23:21 -0500 Subject: [PATCH 1/4] better cabin sizing documentation revived resizing tests updated cabin figure with better seats --- docs/make.jl | 3 +- docs/src/assets/cabin_layout.svg | 1009 +++++++++++++++++++++++++++ docs/src/structures/cabin_sizing.md | 68 ++ src/IO/read_input.jl | 3 +- src/structures/size_cabin.jl | 25 +- src/structures/update_fuse.jl | 11 +- test/unit_test_structures.jl | 85 +-- 7 files changed, 1150 insertions(+), 54 deletions(-) create mode 100644 docs/src/assets/cabin_layout.svg create mode 100644 docs/src/structures/cabin_sizing.md diff --git a/docs/make.jl b/docs/make.jl index 12e344e1..44a2a6f2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -31,7 +31,8 @@ makedocs( "atmos/atmos.md" ], "Structures" => Any["structures/wing.md", - "structures/fuselage.md" + "structures/fuselage.md", + "structures/cabin_sizing.md" ], "Cryogenic tanks" => Any["cryo_tank/cryotank.md", "cryo_tank/fueltanks.md" diff --git a/docs/src/assets/cabin_layout.svg b/docs/src/assets/cabin_layout.svg new file mode 100644 index 00000000..6ea823c0 --- /dev/null +++ b/docs/src/assets/cabin_layout.svg @@ -0,0 +1,1009 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 𝜃 + + 1 + + 𝜃 + + 2 + + + 𝑑 + + 𝑓𝑙𝑜𝑜𝑟𝑠 + + + + 𝑠𝑒𝑎𝑡 + + + + + + 𝑑 + + 𝑐𝑎𝑟𝑔𝑜 + + + 𝑑 + + 𝑐𝑎𝑏𝑖𝑛 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/structures/cabin_sizing.md b/docs/src/structures/cabin_sizing.md new file mode 100644 index 00000000..062f5d2c --- /dev/null +++ b/docs/src/structures/cabin_sizing.md @@ -0,0 +1,68 @@ +# [Cabin sizing](@id cabin_sizing) +The TASOPT input file requires the user to specify a radius and general fuselage layout, including the start and end of the pressure vessel, and the start and end of the cylindrical portions. Whether this provided layout is used or not depends on the flag `calculate_cabin_length` in the `Fuselage.Geometry` input field: this flag is `False` by default, implying that the fuselage length and layout does not get recalculated for the input radius. However, if the flag is set to `True`, the function `update_fuse_for_pax!()` is used to update the fuselage length and layout to accommodate the desired number of passengers. + +```@docs +structures.update_fuse_for_pax! +``` + +To resize the fuselage, information about the number of passengers and seat properties are needed. The number of passengers used the resize the fuselage is set in the input file as `exit_limit` in `Mission`; this is assumed to be the maximum number of people that could fit in the cabin in an all-economy layout. The goal then is to determine the minimum cabin length corresponding to the specified radius and seat properties: seat pitch, width and height. Two key functions are used for this purpose: + +```@docs +structures.find_floor_angles +``` + +This function aims to determine the angular placement of the cabin floor inside the fuselage cross-section. If the cabin has a single deck, this is the angle that maximizes the cabin width. If the aircraft is a double-decker, the main (lower) cabin could be at any angle; the main-cabin angle must then be specified so that the upper-cabin angle can be calculated from the cabin height. The double-decker geometry problem is more involved and is described in the subsequent section. + +If the fuselage has a single deck, the angular position of the cabin floor is then used by the following function to calculate the maximum width corresponding to that section. For example, if the seat height is 0, the angular placement would be 0 and the maximum width of a single bubble fuselage is ``2R_{fuse}``. When the seat height is greater than 0, the effective cabin width is the distance between the top or bottom of the seats in the cabin, whichever is smallest. + +```@docs +structures.find_cabin_width +``` + +Once the geometric properties of the floor and cabin width have been determined, the seats are placed in the cabin so as to determine the total cabin length. A 10 ft offset from the front of the cabin is assumed. First, the number of seats abreast is determined by + +```@docs +structures.findSeatsAbreast +``` + +This function assumes an aisle width of 20 inches and a fuselage offset of 6 inches, as seats do not start at the edge of the fuselage. Once the number of seats abreast is known, the number of rows is calculated from the exit-limit number of passengers and the cabin length is calculated from the seat pitch, taking emergency exits into account. + +When the cabin length has been determined, the function `update_fuse_for_pax!` proceeds to update the position of all the structural elements that have to be specified by the user, such as the cylinder and pressure vessel ends, horizontal and vertical tail placements, etc. To do this, the relative distances between these elements specified by the user are maintained. For example, the distance from the end of the fuselage to the horizontal and vertical tails is the same in the resized aircraft as in the user-defined parameters. + +## Double-decker aircraft +When the cabin has two decks, determining the best layout is more involved as the position of the lower floor and the passenger split between the lower and upper cabins is not known in advance. In TASOPT, this is solved as an optimization problem: the optimization variables are the number of passengers in the lower cabin and the lower-cabin floor angle. The geometry of the cabin is shown in the figure below. + +![cabin](../assets/cabin_layout.svg) + +The objective function to be minimized is the cabin length, which is the maximum of the lengths of the upper and lower cabins. An optimal design is expected to be one in which both cabins have similar lengths. Constraints are applied on the minimum cargo bay height, ``d_{cargo}`` (by default 1.626 m), and on the minimum height of the upper cabin, ``d_{cabin}`` (by default 2 m). The objective function is + +```@docs +structures.find_double_decker_cabin_length +``` +This function returns the maximum of the lengths of either cabins (which is used as the objective function), and the number of seats abreast in the main cabin for a later check (ignored in the optimization process). The optimization is done using the NLopt.jl optimization suite, with the function + +```@docs +structures.optimize_double_decker_cabin +``` + +Since there are only two optimization variables, this function uses the `GN_AGS` global optimizer. Once the minimum cabin length has been determined, the fuselage and component layouts get updated in `update_fuse_for_pax!()` as in the single deck case. + +## Determining radius from seats abreast +In some circumstances, it may be of interest to calculate the minimum fuselage radius that results in a given number of seats abreast. If multiple fuselage radii are being tested, it is likely that the ones providing best performance are those with the narrowest cabin for a given number of seats abreast (and therefore cabin length). + +In TASOPT, this problem is solved as an optimization problem: the goal is to find the minimum fuselage radius such that the seats abreast in the main cabin are exactly equal to the desired number. This is done in the function + +```@docs +structures.find_minimum_radius_for_seats_per_row +``` + +In this function, two optimizers are used. First, the global optimizer `GN_DIRECT` is used to find the approximate location of the solution with up to 500 function evaluations. This optimizer cannot handle equality constraints directly; instead, the constraint is introduced with a penalty function. The objective function is simply +```math +f = R_{fuse}+10^3 \Delta s, +``` +where ``\Delta s`` is the difference between the desired and actual seats per row for a given radius. The function that computes this difference for a given radius is +```@docs +structures.check_seats_per_row_diff +``` + +Once the approximate solution has been found, a local optimizer `LN_NELDERMEAD` is used to polish off the solution at a lower computational cost. Finally, a check is done to verify that the solution meets the equality constraint. diff --git a/src/IO/read_input.jl b/src/IO/read_input.jl index cd5ad477..7f483dc2 100644 --- a/src/IO/read_input.jl +++ b/src/IO/read_input.jl @@ -142,8 +142,9 @@ if !ac_type_fixed end maxpax = readmis("max_payload_in_pax_equivalent") #This represents the maximum aircraft payload in equivalent number of pax + #Part of this payload may be carried in the aircraft's cargo hold pax = readmis("pax") -exitlimit = readmis("exit_limit") +exitlimit = readmis("exit_limit") #Maximum number of pax that could fit in cabin in an all-economy layout despax = pax[1] #Design number of passengers if despax > maxpax error("Design mission has higher payload weight than maximum aircraft payload!") diff --git a/src/structures/size_cabin.jl b/src/structures/size_cabin.jl index 5dcc48aa..03a78291 100644 --- a/src/structures/size_cabin.jl +++ b/src/structures/size_cabin.jl @@ -9,6 +9,7 @@ length. **Inputs:** - `pax::Float64`: design number of passengers - `cabin_width::Float64`: width of cabin (m). + - `seat_pitch::Float64`: longitudinal distance between seats (m). - `seat_width::Float64`: width of one seat (m). - `aisle_halfwidth::Float64`: half the width of an aisle (m). - `fuse_offset::Float64`: distance from outside of fuselage to edge of closest window seat (m). @@ -21,7 +22,7 @@ length. function place_cabin_seats(pax, cabin_width, seat_pitch = 30.0*in_to_m, seat_width = 19.0*in_to_m, aisle_halfwidth = 10.0*in_to_m, fuse_offset = 6.0*in_to_m) - cabin_offset = 10 * ft_to_m #Distance to the front and back of seats + cabin_offset = 10 * ft_to_m #Distance to the front of seats #TODO the hardcoded 10 ft is not elegant seats_per_row = findSeatsAbreast(cabin_width, seat_width, aisle_halfwidth, fuse_offset) @@ -49,6 +50,22 @@ function place_cabin_seats(pax, cabin_width, seat_pitch = 30.0*in_to_m, return lcabin, xseats, seats_per_row end # function place_cabin_seats +""" + findSeatsAbreast(cabin_width, + seat_width = 19.0*in_to_m, aisle_halfwidth = 10.0*in_to_m, fuse_offset = 6.0*in_to_m) + +Function to find the number of seats abreast that can fit in a given cabin width. + +!!! details "🔃 Inputs and Outputs" + **Inputs:** + - `cabin_width::Float64`: width of cabin (m). + - `seat_width::Float64`: width of one seat (m). + - `aisle_halfwidth::Float64`: half the width of an aisle (m). + - `fuse_offset::Float64`: distance from outside of fuselage to edge of closest window seat (m). + + **Outputs:** + - `seats_per_row::Float64`: number of seats per row. +""" function findSeatsAbreast(cabin_width, seat_width = 19.0*in_to_m, aisle_halfwidth = 10.0*in_to_m, fuse_offset = 6.0*in_to_m) @@ -202,6 +219,7 @@ passengers on each deck. **Outputs:** - `maxl::Float64`: required cabin length (m) + - `pax_per_row_main::Float64`: number of seats abreast in lower cabin """ function find_double_decker_cabin_length(x::Vector{Float64}, fuse) seat_pitch = fuse.cabin.seat_pitch @@ -282,8 +300,8 @@ end """ optimize_double_decker_cabin(fuse) -This function can be used to optimize the passenger distribution across two decks in a double decker aircraft. -If the cross-section is circular, it also optimizes the deck layouts. +This function can be used to optimize the deck layouts and passenger distribution in a double decker aircraft. + !!! details "🔃 Inputs and Outputs" **Inputs:** - `parg::Vector{Float64}`: vector with aircraft geometric and mass parameters @@ -291,6 +309,7 @@ If the cross-section is circular, it also optimizes the deck layouts. **Outputs:** - `xopt::Vector{Float64}`: vector with optimization results + - `seats_per_row_main::Float64`: number of seats abreast in lower cabin """ function optimize_double_decker_cabin(fuse) dRfuse = fuse.layout.bubble_lower_downward_shift diff --git a/src/structures/update_fuse.jl b/src/structures/update_fuse.jl index 3a61b599..d1eaf9f3 100644 --- a/src/structures/update_fuse.jl +++ b/src/structures/update_fuse.jl @@ -145,13 +145,13 @@ This function calculates the minimum radius required to have a desired number of !!! details "🔃 Inputs and Outputs" **Inputs:** - - `seats_per_row::Float64`: number of seats per row in main cabin (lower deck if double decker) + - `seats_per_row::Int64`: number of seats per row in main cabin (lower deck if double decker) - `ac_base::aircraft`: aircraft object **Outputs:** - `R::Float64`: minimum radius for desired number of seats per row (m) """ -function find_minimum_radius_for_seats_per_row(seats_per_row, ac_base) +function find_minimum_radius_for_seats_per_row(seats_per_row::Int64, ac_base) ac = deepcopy(ac_base) #Copy input ac to avoid modifying it obj(x, grad) = x[1] + 1e3 * abs(check_seats_per_row_diff(seats_per_row, x, ac)) #Objective function is the radius plus a big penalty if constraint is not met @@ -173,7 +173,7 @@ function find_minimum_radius_for_seats_per_row(seats_per_row, ac_base) (minf,xopt,ret) = NLopt.optimize(opt, initial_x) #Solve optimization problem #Next, use local optimizer to polish off optimum - opt = Opt(:LN_NELDERMEAD, length(initial_x)) #Use a + opt = Opt(:LN_NELDERMEAD, length(initial_x)) #Use a local optimizer opt.lower_bounds = [0.0] opt.upper_bounds = [5.0] opt.min_objective = obj @@ -217,7 +217,4 @@ function check_seats_per_row_diff(seats_per_row, x, ac) catch return 1.0 end -end - -# [fuselage.layout.x_end_cylinder, parg[igxwbox], fuselage.layout.x_pressure_shell_aft, fuselage.layout.x_cone_end, -# fuselage.APU.x, fuselage.layout.x_end, fuselage.HPE_sys.x, parg[igxhbox], parg[igxvbox],parg[igxeng],parg[igdxcabin]] \ No newline at end of file +end \ No newline at end of file diff --git a/test/unit_test_structures.jl b/test/unit_test_structures.jl index b65492ed..25f2cba7 100644 --- a/test/unit_test_structures.jl +++ b/test/unit_test_structures.jl @@ -255,14 +255,10 @@ fuselage.layout.x_cone_end = fuselage.layout.x_cone_end * 0.52484 pari = zeros(iitotal) pari[iinftanks] = 1 -# parg_orig = deepcopy(parg) -# deleteat!(parg_orig, parg_orig .== 0) - #Update fuel tank length and check changes parg[iglftank] = 5.0 TASOPT.update_fuse!(fuselage, pari, parg) -# parg_check = [43.40480000000001, 6.096, 35.175200000000004, 36.699200000000005, 41.27120000000001, 14.52032532088372, 16.04432532088372, 40.50920000000001, 39.137600000000006, 42.18560000000001, 21.660776608000003, 5.0, 23.4696, 1.5239999999999991, 18.716634144] update_fuse_out = [fuselage.layout.x_end_cylinder, fuselage.layout.x_pressure_shell_aft, fuselage.layout.x_cone_end, @@ -295,42 +291,47 @@ parg[igxeng]] update_fuse_out_test = [29.5656, 31.0896, 18.716634144, 36.57600000000001, 37.79520000000001, 9.82323826413696, 34.89960000000001, 33.528000000000006, 14.52032532088372] @test all(isapprox.(update_fuse_out, update_fuse_out_test)) -# #Test cabin resizing -# parg = zeros(igtotal) -# fuselage.layout.x_start_cylinder = 6.096 -# fuselage.layout.x_end_cylinder = 29.5656 -# fuselage.layout.x_pressure_shell_aft = 31.0896 -# parg[igdxcabin] = 23.4696 -# parg[igdxeng2wbox] = 1.5239999999999991 -# fuselage.APU.r = [36.576, 0.0, 0.0] -# fuselage.layout.x_end = 37.7952 -# fuselage.layout.x_cone_end = 35.6616 -# parg[igxhbox ] = 34.8996 -# parg[igxvbox ] = 33.528 -# parg[igxwbox] = 16.04432532088372 -# parg[igxeng] = parg[igxwbox] - parg[igdxeng2wbox] -# fuselage.layout.x_cone_end = fuselage.layout.x_cone_end * 0.52484 - -# parg[igseatpitch] = 0.762 -# parg[igseatwidth] = 0.4826 -# parg[igaislehalfwidth] = 0.254 -# parg[igWpaymax] = 219964.5779 -# fuselage.layout.radius - -# pari = zeros(iitotal) -# pari[iidoubledeck] = 0 - -# parm = zeros(imtotal) -# parm[imWperpax,1] = 956.36773 - -# fuse_tank = TASOPT.fuselage_tank() - -# TASOPT.update_fuse_for_pax!(pari, parg, fuselage, fuse_tank) - -# parg_check = [47.091600000000014, 6.096, 38.86200000000001, 40.38600000000001, 44.95800000000001, 18.460895740194808, 19.98489574019481, 44.19600000000001, 42.82440000000001, 45.87240000000001, 23.595756720000004, 1.9558, 219964.5779, 32.76600000000001, 1.5239999999999991, 0.762, 0.4826, 0.254] -# parg_nz = deepcopy(parg) -# deleteat!(parg_nz, parg_nz .== 0) -# for (i,item) in enumerate(parg_nz) #For every nonzero element in parg -# @test parg_nz[i] ≈ parg_check[i] -# end +#Test cabin resizing +parg = zeros(igtotal) +fuselage.layout.x_start_cylinder = 6.096 +fuselage.layout.x_end_cylinder = 29.5656 +fuselage.layout.x_pressure_shell_aft = 31.0896 +fuselage.layout.l_cabin_cylinder = 23.4696 +parg[igdxeng2wbox] = 1.5239999999999991 +fuselage.APU.r = [36.576, 0.0, 0.0] +fuselage.layout.x_end = 37.7952 +fuselage.layout.x_cone_end = 35.6616 +parg[igxhbox ] = 34.8996 +parg[igxvbox ] = 33.528 +parg[igxwbox] = 16.04432532088372 +parg[igxeng] = parg[igxwbox] - parg[igdxeng2wbox] +fuselage.layout.x_cone_end = fuselage.layout.x_cone_end * 0.52484 + +parg[igseatpitch] = 0.762 +parg[igseatwidth] = 0.4826 +parg[igaislehalfwidth] = 0.254 +parg[igWpaymax] = 219964.5779 +fuselage.layout.cross_section.radius = 2.5 #Change radius to 2.5 m + +pari = zeros(iitotal) +pari[iidoubledeck] = 0 + +fuse_tank = TASOPT.fuselage_tank() + +TASOPT.update_fuse_for_pax!(pari, parg, fuselage, fuse_tank) + +parg_check = [14.584924835954398, 16.108924835954397, 35.05200000000001, 33.680400000000006, 219964.5779, 1.5239999999999991, 0.762, 0.4826, 0.254] +parg_nz = deepcopy(parg) +deleteat!(parg_nz, parg_nz .== 0) +for (i,item) in enumerate(parg_nz) #For every nonzero element in parg + @test parg_nz[i] ≈ parg_check[i] +end + +@test fuselage.layout.x_pressure_shell_aft ≈ 31.24200000000001 +@test fuselage.layout.x_cone_end ≈ 18.86903414400001 +@test fuselage.layout.x_end ≈ 37.94760000000001 + +#Test minimum radius calculation +Rmin = TASOPT.structures.find_minimum_radius_for_seats_per_row(5, ac_test) +@test Rmin ≈ 1.7113052179793784 end \ No newline at end of file From 245774869d2a3f719b78bb360c207cb7a69f57c4 Mon Sep 17 00:00:00 2001 From: Nicolas Gomez Date: Mon, 18 Nov 2024 11:51:52 -0500 Subject: [PATCH 2/4] updated cabin docs text structure --- docs/src/structures/cabin_sizing.md | 108 ++++++++++++++++------------ src/IO/read_input.jl | 2 +- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/docs/src/structures/cabin_sizing.md b/docs/src/structures/cabin_sizing.md index 062f5d2c..39f2d880 100644 --- a/docs/src/structures/cabin_sizing.md +++ b/docs/src/structures/cabin_sizing.md @@ -1,68 +1,82 @@ # [Cabin sizing](@id cabin_sizing) -The TASOPT input file requires the user to specify a radius and general fuselage layout, including the start and end of the pressure vessel, and the start and end of the cylindrical portions. Whether this provided layout is used or not depends on the flag `calculate_cabin_length` in the `Fuselage.Geometry` input field: this flag is `False` by default, implying that the fuselage length and layout does not get recalculated for the input radius. However, if the flag is set to `True`, the function `update_fuse_for_pax!()` is used to update the fuselage length and layout to accommodate the desired number of passengers. +When creating an aircraft input file, the fuselage geometry (e.g., pressure vessel start and end, cylindrical portion start and end, guesses for the wing and tail) has to be specified. The user is given the option to resize the fuselage if, for example, these parameters are not known or if the radius is to be optimized. This can be accomplished via the function `structures.update_fuse_for_pax!`. -```@docs -structures.update_fuse_for_pax! -``` +!!! details "📖 Theory - Cabin sizing" + The TASOPT input file requires the user to specify a radius and general fuselage layout, including the start and end of the pressure vessel, and the start and end of the cylindrical portions. Whether this provided layout is used or not depends on the flag `calculate_cabin_length` in the `Fuselage.Geometry` input field: this flag is `False` by default, implying that the fuselage length and layout does not get recalculated for the input radius. However, if the flag is set to `True`, the function `update_fuse_for_pax!()` is used to update the fuselage length and layout to accommodate the desired number of passengers. -To resize the fuselage, information about the number of passengers and seat properties are needed. The number of passengers used the resize the fuselage is set in the input file as `exit_limit` in `Mission`; this is assumed to be the maximum number of people that could fit in the cabin in an all-economy layout. The goal then is to determine the minimum cabin length corresponding to the specified radius and seat properties: seat pitch, width and height. Two key functions are used for this purpose: + ```@docs + structures.update_fuse_for_pax! + ``` -```@docs -structures.find_floor_angles -``` + To resize the fuselage, information about the number of passengers and seat properties are needed. The number of passengers used the resize the fuselage is set in the input file as `exit_limit` in `Mission`; this is assumed to be the maximum number of people that could fit in the cabin in an all-economy layout. The goal then is to determine the minimum cabin length corresponding to the specified radius and seat properties: seat pitch, width and height. Two key functions are used for this purpose: -This function aims to determine the angular placement of the cabin floor inside the fuselage cross-section. If the cabin has a single deck, this is the angle that maximizes the cabin width. If the aircraft is a double-decker, the main (lower) cabin could be at any angle; the main-cabin angle must then be specified so that the upper-cabin angle can be calculated from the cabin height. The double-decker geometry problem is more involved and is described in the subsequent section. + ```@docs + structures.find_floor_angles + ``` -If the fuselage has a single deck, the angular position of the cabin floor is then used by the following function to calculate the maximum width corresponding to that section. For example, if the seat height is 0, the angular placement would be 0 and the maximum width of a single bubble fuselage is ``2R_{fuse}``. When the seat height is greater than 0, the effective cabin width is the distance between the top or bottom of the seats in the cabin, whichever is smallest. + This function aims to determine the angular placement of the cabin floor inside the fuselage cross-section. If the cabin has a single deck, this is the angle that maximizes the cabin width. If the aircraft is a double-decker, the main (lower) cabin could be at any angle; the main-cabin angle must then be specified so that the upper-cabin angle can be calculated from the cabin height. The double-decker geometry problem is more involved and is described in the subsequent section. -```@docs -structures.find_cabin_width -``` + If the fuselage has a single deck, the angular position of the cabin floor is then used by the following function to calculate the maximum width corresponding to that section. For example, if the seat height is 0, the angular placement would be 0 and the maximum width of a single bubble fuselage is ``2R_{fuse}``. When the seat height is greater than 0, the effective cabin width is the distance between the top or bottom of the seats in the cabin, whichever is smallest. -Once the geometric properties of the floor and cabin width have been determined, the seats are placed in the cabin so as to determine the total cabin length. A 10 ft offset from the front of the cabin is assumed. First, the number of seats abreast is determined by + ```@docs + structures.find_cabin_width + ``` -```@docs -structures.findSeatsAbreast -``` + Once the geometric properties of the floor and cabin width have been determined, the seats are placed in the cabin so as to determine the total cabin length. A 10 ft offset from the front of the cabin is assumed. First, the number of seats abreast is determined by -This function assumes an aisle width of 20 inches and a fuselage offset of 6 inches, as seats do not start at the edge of the fuselage. Once the number of seats abreast is known, the number of rows is calculated from the exit-limit number of passengers and the cabin length is calculated from the seat pitch, taking emergency exits into account. + ```@docs + structures.findSeatsAbreast + ``` -When the cabin length has been determined, the function `update_fuse_for_pax!` proceeds to update the position of all the structural elements that have to be specified by the user, such as the cylinder and pressure vessel ends, horizontal and vertical tail placements, etc. To do this, the relative distances between these elements specified by the user are maintained. For example, the distance from the end of the fuselage to the horizontal and vertical tails is the same in the resized aircraft as in the user-defined parameters. + This function assumes an aisle width of 20 inches and a fuselage offset of 6 inches, as seats do not start at the edge of the fuselage. Once the number of seats abreast is known, the number of rows is calculated from the exit-limit number of passengers and the cabin length is calculated from the seat pitch, taking emergency exits into account. -## Double-decker aircraft -When the cabin has two decks, determining the best layout is more involved as the position of the lower floor and the passenger split between the lower and upper cabins is not known in advance. In TASOPT, this is solved as an optimization problem: the optimization variables are the number of passengers in the lower cabin and the lower-cabin floor angle. The geometry of the cabin is shown in the figure below. + When the cabin length has been determined, the function `update_fuse_for_pax!` proceeds to update the position of all the structural elements that have to be specified by the user, such as the cylinder and pressure vessel ends, horizontal and vertical tail placements, etc. To do this, the relative distances between these elements specified by the user are maintained. For example, the distance from the end of the fuselage to the horizontal and vertical tails is the same in the resized aircraft as in the user-defined parameters. -![cabin](../assets/cabin_layout.svg) + ## Double-decker aircraft + When the cabin has two decks, determining the best layout is more involved as the position of the lower floor and the passenger split between the lower and upper cabins is not known in advance. In TASOPT, this is solved as an optimization problem: the optimization variables are the number of passengers in the lower cabin and the lower-cabin floor angle. The geometry of the cabin is shown in the figure below. -The objective function to be minimized is the cabin length, which is the maximum of the lengths of the upper and lower cabins. An optimal design is expected to be one in which both cabins have similar lengths. Constraints are applied on the minimum cargo bay height, ``d_{cargo}`` (by default 1.626 m), and on the minimum height of the upper cabin, ``d_{cabin}`` (by default 2 m). The objective function is + ![cabin](../assets/cabin_layout.svg) -```@docs -structures.find_double_decker_cabin_length -``` -This function returns the maximum of the lengths of either cabins (which is used as the objective function), and the number of seats abreast in the main cabin for a later check (ignored in the optimization process). The optimization is done using the NLopt.jl optimization suite, with the function + The objective function to be minimized is the cabin length, which is the maximum of the lengths of the upper and lower cabins. An optimal design is expected to be one in which both cabins have similar lengths. Constraints are applied on the minimum cargo bay height, ``d_{cargo}`` (by default 1.626 m), and on the minimum height of the upper cabin, ``d_{cabin}`` (by default 2 m). The objective function is -```@docs -structures.optimize_double_decker_cabin -``` + ```@docs + structures.find_double_decker_cabin_length + ``` + This function returns the maximum of the lengths of either cabins (which is used as the objective function), and the number of seats abreast in the main cabin for a later check (ignored in the optimization process). The optimization is done using the NLopt.jl optimization suite, with the function -Since there are only two optimization variables, this function uses the `GN_AGS` global optimizer. Once the minimum cabin length has been determined, the fuselage and component layouts get updated in `update_fuse_for_pax!()` as in the single deck case. + ```@docs + structures.optimize_double_decker_cabin + ``` -## Determining radius from seats abreast -In some circumstances, it may be of interest to calculate the minimum fuselage radius that results in a given number of seats abreast. If multiple fuselage radii are being tested, it is likely that the ones providing best performance are those with the narrowest cabin for a given number of seats abreast (and therefore cabin length). + Since there are only two optimization variables, this function uses the `GN_AGS` global optimizer. Once the minimum cabin length has been determined, the fuselage and component layouts get updated in `update_fuse_for_pax!()` as in the single deck case. -In TASOPT, this problem is solved as an optimization problem: the goal is to find the minimum fuselage radius such that the seats abreast in the main cabin are exactly equal to the desired number. This is done in the function + ## Determining radius from seats abreast + In some circumstances, it may be of interest to calculate the minimum fuselage radius that results in a given number of seats abreast. If multiple fuselage radii are being tested, it is likely that the ones providing best performance are those with the narrowest cabin for a given number of seats abreast (and therefore cabin length). -```@docs -structures.find_minimum_radius_for_seats_per_row -``` - -In this function, two optimizers are used. First, the global optimizer `GN_DIRECT` is used to find the approximate location of the solution with up to 500 function evaluations. This optimizer cannot handle equality constraints directly; instead, the constraint is introduced with a penalty function. The objective function is simply -```math -f = R_{fuse}+10^3 \Delta s, -``` -where ``\Delta s`` is the difference between the desired and actual seats per row for a given radius. The function that computes this difference for a given radius is -```@docs -structures.check_seats_per_row_diff -``` + In TASOPT, this problem is solved as an optimization problem: the goal is to find the minimum fuselage radius such that the seats abreast in the main cabin are exactly equal to the desired number. This is done in the function + + ```@docs + structures.find_minimum_radius_for_seats_per_row + ``` -Once the approximate solution has been found, a local optimizer `LN_NELDERMEAD` is used to polish off the solution at a lower computational cost. Finally, a check is done to verify that the solution meets the equality constraint. + In this function, two optimizers are used. First, the global optimizer `GN_DIRECT` is used to find the approximate location of the solution with up to 500 function evaluations. This optimizer cannot handle equality constraints directly; instead, the constraint is introduced with a penalty function. The objective function is simply + ```math + f = R_{fuse}+10^3 \Delta s, + ``` + where ``\Delta s`` is the difference between the desired and actual seats per row for a given radius. The function that computes this difference for a given radius is + ```@docs + structures.check_seats_per_row_diff + ``` + + Once the approximate solution has been found, a local optimizer `LN_NELDERMEAD` is used to polish off the solution at a lower computational cost. Finally, a check is done to verify that the solution meets the equality constraint. + +```@docs +TASOPT.structures.update_fuse_for_pax! +TASOPT.structures.find_floor_angles +TASOPT.structures.find_cabin_width +TASOPT.structures.findSeatsAbreast +TASOPT.structures.find_double_decker_cabin_length +TASOPT.structures.optimize_double_decker_cabin +TASOPT.structures.find_minimum_radius_for_seats_per_row +TASOPT.structures.check_seats_per_row_diff +``` \ No newline at end of file diff --git a/src/IO/read_input.jl b/src/IO/read_input.jl index 7f483dc2..0fa03697 100644 --- a/src/IO/read_input.jl +++ b/src/IO/read_input.jl @@ -142,7 +142,7 @@ if !ac_type_fixed end maxpax = readmis("max_payload_in_pax_equivalent") #This represents the maximum aircraft payload in equivalent number of pax - #Part of this payload may be carried in the aircraft's cargo hold + #This may exceed the seatable capacity to account for belly cargo pax = readmis("pax") exitlimit = readmis("exit_limit") #Maximum number of pax that could fit in cabin in an all-economy layout despax = pax[1] #Design number of passengers From a48679e1ec4c5c121be8c93e4b987017627bf830 Mon Sep 17 00:00:00 2001 From: "Jonas J. Gonzalez" Date: Mon, 18 Nov 2024 15:19:18 -0500 Subject: [PATCH 3/4] cabin_sizing.md edits, added fxn links, some reformatting --- docs/src/structures/cabin_sizing.md | 66 ++++++++--------------------- 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/docs/src/structures/cabin_sizing.md b/docs/src/structures/cabin_sizing.md index 39f2d880..347b7575 100644 --- a/docs/src/structures/cabin_sizing.md +++ b/docs/src/structures/cabin_sizing.md @@ -1,74 +1,44 @@ # [Cabin sizing](@id cabin_sizing) -When creating an aircraft input file, the fuselage geometry (e.g., pressure vessel start and end, cylindrical portion start and end, guesses for the wing and tail) has to be specified. The user is given the option to resize the fuselage if, for example, these parameters are not known or if the radius is to be optimized. This can be accomplished via the function `structures.update_fuse_for_pax!`. +When creating an aircraft input file, the fuselage geometry has to be specified (i.e., the pressure vessel start and end locations, cylindrical portion start and end locations and radius, guesses for the wing and tail). The user is given the option to resize the fuselage if, for example, these parameters are not known or if the radius is to be optimized. This can be accomplished via [`structures.update_fuse_for_pax!()`](@ref). +## Theory !!! details "📖 Theory - Cabin sizing" - The TASOPT input file requires the user to specify a radius and general fuselage layout, including the start and end of the pressure vessel, and the start and end of the cylindrical portions. Whether this provided layout is used or not depends on the flag `calculate_cabin_length` in the `Fuselage.Geometry` input field: this flag is `False` by default, implying that the fuselage length and layout does not get recalculated for the input radius. However, if the flag is set to `True`, the function `update_fuse_for_pax!()` is used to update the fuselage length and layout to accommodate the desired number of passengers. - - ```@docs - structures.update_fuse_for_pax! - ``` + The TASOPT input file requires the user to specify a radius and general fuselage layout, including the start and end of the pressure vessel, and the start and end of the cylindrical portions. Whether this provided layout is used or not depends on the flag `calculate_cabin_length` in the `Fuselage.Geometry` input field: this flag is `False` by default, implying that the fuselage length and layout does not get recalculated for the input radius. However, if the flag is set to `True`, the function [`structures.update_fuse_for_pax!()`](@ref) is used to update the fuselage length and layout to accommodate the desired number of passengers. To resize the fuselage, information about the number of passengers and seat properties are needed. The number of passengers used the resize the fuselage is set in the input file as `exit_limit` in `Mission`; this is assumed to be the maximum number of people that could fit in the cabin in an all-economy layout. The goal then is to determine the minimum cabin length corresponding to the specified radius and seat properties: seat pitch, width and height. Two key functions are used for this purpose: - ```@docs - structures.find_floor_angles - ``` - - This function aims to determine the angular placement of the cabin floor inside the fuselage cross-section. If the cabin has a single deck, this is the angle that maximizes the cabin width. If the aircraft is a double-decker, the main (lower) cabin could be at any angle; the main-cabin angle must then be specified so that the upper-cabin angle can be calculated from the cabin height. The double-decker geometry problem is more involved and is described in the subsequent section. - - If the fuselage has a single deck, the angular position of the cabin floor is then used by the following function to calculate the maximum width corresponding to that section. For example, if the seat height is 0, the angular placement would be 0 and the maximum width of a single bubble fuselage is ``2R_{fuse}``. When the seat height is greater than 0, the effective cabin width is the distance between the top or bottom of the seats in the cabin, whichever is smallest. - - ```@docs - structures.find_cabin_width - ``` - - Once the geometric properties of the floor and cabin width have been determined, the seats are placed in the cabin so as to determine the total cabin length. A 10 ft offset from the front of the cabin is assumed. First, the number of seats abreast is determined by - - ```@docs - structures.findSeatsAbreast - ``` + - [`structures.find_floor_angles()`](@ref): This function aims to determine the angular placement of the cabin floor inside the fuselage cross-section. If the cabin has a single deck, this is the angle that maximizes the cabin width. If the aircraft is a double-decker, the main (lower) cabin could be at any angle; the main-cabin angle must then be specified so that the upper-cabin angle can be calculated from the cabin height. The double-decker geometry problem is more involved and is described in the subsequent section. + + - [`structures.find_cabin_width()`](@ref) If the fuselage has a single deck, the angular position of the cabin floor is then used by the following function to calculate the maximum width corresponding to that section. For example, if the seat height is 0, the angular placement would be 0 and the maximum width of a single bubble fuselage is ``2R_{fuse}``. When the seat height is greater than 0, the effective cabin width is the distance between the top or bottom of the seats in the cabin, whichever is smallest. - This function assumes an aisle width of 20 inches and a fuselage offset of 6 inches, as seats do not start at the edge of the fuselage. Once the number of seats abreast is known, the number of rows is calculated from the exit-limit number of passengers and the cabin length is calculated from the seat pitch, taking emergency exits into account. + Once the geometric properties of the floor and cabin width have been determined, the seats are placed in the cabin so as to determine the total cabin length. A 10 ft offset from the front of the cabin is assumed. First, the number of seats abreast is determined by [`structures.findSeatsAbreast()`](@ref). This function assumes an aisle width of 20 inches and a fuselage offset of 6 inches, as seats do not start at the edge of the fuselage. Once the number of seats abreast is known, the number of rows is calculated from the exit-limit number of passengers and the cabin length is calculated from the seat pitch, taking emergency exits into account. - When the cabin length has been determined, the function `update_fuse_for_pax!` proceeds to update the position of all the structural elements that have to be specified by the user, such as the cylinder and pressure vessel ends, horizontal and vertical tail placements, etc. To do this, the relative distances between these elements specified by the user are maintained. For example, the distance from the end of the fuselage to the horizontal and vertical tails is the same in the resized aircraft as in the user-defined parameters. + When the cabin length has been determined, the [`structures.update_fuse_for_pax!()`](@ref) proceeds to update the position of all the structural elements that have to be specified by the user, such as the cylinder and pressure vessel ends, horizontal and vertical tail placements, etc. To do this, the relative distances between these elements specified by the user are maintained. For example, the distance from the end of the fuselage to the horizontal and vertical tails is the same in the resized aircraft as in the user-defined parameters. - ## Double-decker aircraft +!!! details "📖 Theory - Double-decker aircraft" When the cabin has two decks, determining the best layout is more involved as the position of the lower floor and the passenger split between the lower and upper cabins is not known in advance. In TASOPT, this is solved as an optimization problem: the optimization variables are the number of passengers in the lower cabin and the lower-cabin floor angle. The geometry of the cabin is shown in the figure below. ![cabin](../assets/cabin_layout.svg) - The objective function to be minimized is the cabin length, which is the maximum of the lengths of the upper and lower cabins. An optimal design is expected to be one in which both cabins have similar lengths. Constraints are applied on the minimum cargo bay height, ``d_{cargo}`` (by default 1.626 m), and on the minimum height of the upper cabin, ``d_{cabin}`` (by default 2 m). The objective function is - - ```@docs - structures.find_double_decker_cabin_length - ``` - This function returns the maximum of the lengths of either cabins (which is used as the objective function), and the number of seats abreast in the main cabin for a later check (ignored in the optimization process). The optimization is done using the NLopt.jl optimization suite, with the function - - ```@docs - structures.optimize_double_decker_cabin - ``` + The objective function to be minimized is the cabin length, which is the maximum of the lengths of the upper and lower cabins. An optimal design is expected to be one in which both cabins have similar lengths. Constraints are applied on the minimum cargo bay height, ``d_{cargo}`` (by default 1.626 m), and on the minimum height of the upper cabin, ``d_{cabin}`` (by default 2 m). The objective function is [`structures.find_double_decker_cabin_length()`](@ref), which returns the maximum of the lengths of either cabins (which is used as the objective function), and the number of seats abreast in the main cabin for a later check (ignored in the optimization process). The optimization is done using the `NLopt.jl` optimization suite via [`structures.optimize_double_decker_cabin()`](@ref). + + Since there are only two optimization variables, this function uses the `GN_AGS` global optimizer. Once the minimum cabin length has been determined, the fuselage and component layouts get updated in [`structures.update_fuse_for_pax!()`](@ref) as in the single deck case. - Since there are only two optimization variables, this function uses the `GN_AGS` global optimizer. Once the minimum cabin length has been determined, the fuselage and component layouts get updated in `update_fuse_for_pax!()` as in the single deck case. - - ## Determining radius from seats abreast +!!! details "📖 Theory - Fuselage radius from seats abreast" In some circumstances, it may be of interest to calculate the minimum fuselage radius that results in a given number of seats abreast. If multiple fuselage radii are being tested, it is likely that the ones providing best performance are those with the narrowest cabin for a given number of seats abreast (and therefore cabin length). - In TASOPT, this problem is solved as an optimization problem: the goal is to find the minimum fuselage radius such that the seats abreast in the main cabin are exactly equal to the desired number. This is done in the function - - ```@docs - structures.find_minimum_radius_for_seats_per_row - ``` + In TASOPT, this problem is solved as an optimization problem: the goal is to find the minimum fuselage radius such that the seats abreast in the main cabin are exactly equal to the desired number. This is done with `structures.find_minimum_radius_for_seats_per_row()`(@ref). In this function, two optimizers are used. First, the global optimizer `GN_DIRECT` is used to find the approximate location of the solution with up to 500 function evaluations. This optimizer cannot handle equality constraints directly; instead, the constraint is introduced with a penalty function. The objective function is simply ```math f = R_{fuse}+10^3 \Delta s, ``` - where ``\Delta s`` is the difference between the desired and actual seats per row for a given radius. The function that computes this difference for a given radius is - ```@docs - structures.check_seats_per_row_diff + where ``\Delta s`` is the difference between the desired and actual seats per row for a given radius. The function that computes this difference for a given radius is [`structures.check_seats_per_row_diff()`](@ref). ``` - Once the approximate solution has been found, a local optimizer `LN_NELDERMEAD` is used to polish off the solution at a lower computational cost. Finally, a check is done to verify that the solution meets the equality constraint. + Once the approximate solution has been found, a local optimizer `LN_NELDERMEAD` is used to polish off the solution at a lower computational cost. Finally, the solution is checked to verify it meets the equality constraint. + +## Functions ```@docs TASOPT.structures.update_fuse_for_pax! From b2b9db7223ba51e8daaad7d82857c2e32594d834 Mon Sep 17 00:00:00 2001 From: Nicolas Gomez Date: Thu, 21 Nov 2024 14:57:45 -0500 Subject: [PATCH 4/4] storing double decker angles for seat calcs --- src/structures/update_fuse.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/structures/update_fuse.jl b/src/structures/update_fuse.jl index d1eaf9f3..79aa4963 100644 --- a/src/structures/update_fuse.jl +++ b/src/structures/update_fuse.jl @@ -75,6 +75,7 @@ function update_fuse_for_pax!(pari, parg, fuse, fuse_tank) seat_width = fuse.cabin.seat_width aisle_halfwidth = fuse.cabin.aisle_halfwidth h_seat = fuse.cabin.seat_height + d_floor = fuse.cabin.floor_distance Rfuse = fuse.layout.radius dRfuse = fuse.layout.bubble_lower_downward_shift @@ -87,6 +88,9 @@ function update_fuse_for_pax!(pari, parg, fuse, fuse_tank) lcyl, _ = find_double_decker_cabin_length(xopt, fuse) #Total length is maximum of the two + #Store angles + fuse.cabin.floor_angle_main = xopt[2] + fuse.cabin.floor_angle_top = find_floor_angles(true, Rfuse, dRfuse, θ1 = xopt[2], h_seat = h_seat, d_floor = d_floor)[2] else θ = find_floor_angles(false, Rfuse, dRfuse, h_seat = h_seat) #Find the floor angle paxsize = fuse.cabin.exit_limit #maximum number of passengers