Skip to content

Commit d0faabb

Browse files
authored
Merge pull request #2 from Brickworks/stress-bug-fixes
Stress bug fixes
2 parents 55f2ed8 + f4ad849 commit d0faabb

File tree

8 files changed

+344
-128
lines changed

8 files changed

+344
-128
lines changed

config/default.toml

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
[physics]
1+
[environment]
22
real_time = false
33
tick_rate_hz = 10.0
4-
max_elapsed_time_s = 600.0
5-
initial_altitude_m = 22_975.793
6-
initial_velocity_m_s = 16.0131
4+
max_elapsed_time_s = 10_000.0
5+
initial_altitude_m = 0.0
6+
initial_velocity_m_s = 0.0
77

88
[balloon]
99
material = "Rubber"
10-
thickness_m = 0.005
11-
mass_kg = 0.8
12-
barely_inflated_diameter_m = 1.0
10+
thickness_m = 0.0001 # thicker balloons have less stress
11+
barely_inflated_diameter_m = 1.5 # larger balloons have less strain
1312

1413
[balloon.lift_gas]
1514
species = "Helium"
16-
mass_kg = 1.5
15+
mass_kg = 0.3
1716

1817
[payload.bus]
1918
dry_mass_kg = 1.427
@@ -29,4 +28,4 @@ open_altitude_m = 18_000.0
2928
controller_path = "../mfc-apps/control_apps"
3029
vent_valve_mass_flow_kg_s = 0.007
3130
dump_valve_mass_flow_kg_s = 0.001
32-
ballast_mass_kg = 0.5
31+
ballast_mass_kg = 0.0

src/cli.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::path::PathBuf;
22

33
use clap::{Parser, Subcommand};
4-
use log::{info, error};
4+
use log::error;
55

66
use crate::simulator::{AsyncSim, Rate};
77

src/simulator/balloon.rs

+183-71
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,138 @@
66

77
extern crate libm;
88

9+
use log::debug;
910
use serde::Deserialize;
1011
use std::f32::consts::PI;
1112
use std::fmt;
1213

1314
use super::gas;
1415

16+
#[derive(Copy, Clone)]
1517
pub struct Balloon {
16-
pub intact: bool, // whether or not it has burst
17-
pub mass: f32, // balloon mass (kg)
18-
pub temperature: f32, // fail if surface temperature exceeds this (K)
19-
pub volume: f32, // internal volume of the balloon (m^3)
20-
pub drag_coeff: f32, // drag coefficient
21-
pub lift_gas: gas::GasVolume, // gas inside the balloon
22-
material: Material, // what the balloon is made of
23-
initial_volume: f32, // internal volume (m^3) at zero pressure
24-
skin_thickness: f32, // thickness of the skin of the balloon (m)
18+
pub intact: bool, // whether or not it has burst
19+
pub mass: f32, // balloon mass (kg)
20+
pub temperature: f32, // fail if surface temperature exceeds this (K)
21+
pub drag_coeff: f32, // drag coefficient
22+
pub lift_gas: gas::GasVolume, // gas inside the balloon
23+
pub material: Material, // what the balloon is made of
24+
pub skin_thickness: f32, // thickness of the skin of the balloon (m)
25+
unstretched_thickness: f32, // thickness of the skin of the balloon without stretch (m)
26+
unstretched_radius: f32, // radius of balloon without stretch (m)
27+
stress: f32,
28+
strain: f32,
2529
}
2630

2731
impl Balloon {
2832
pub fn new(
29-
material: Material, // material of balloon skin
30-
skin_thickness: f32, // balloon skin thickness (m) at zero pressure
33+
material: Material, // material of balloon skin
34+
skin_thickness: f32, // balloon skin thickness (m) at zero pressure
3135
barely_inflated_diameter: f32, // internal diameter (m) at zero pressure
3236
lift_gas: gas::GasVolume, // species of gas inside balloon
3337
) -> Self {
34-
let initial_radius = barely_inflated_diameter / 2.0;
35-
let initial_volume = spherical_volume(initial_radius);
36-
let mass = shell_volume(initial_radius, skin_thickness) * material.density;
38+
let unstretched_radius = barely_inflated_diameter / 2.0;
39+
let mass = shell_volume(unstretched_radius, skin_thickness) * material.density;
3740
Balloon {
3841
intact: true,
3942
mass,
4043
temperature: 293.0,
41-
volume: initial_volume,
4244
drag_coeff: 0.3,
4345
lift_gas,
4446
material,
45-
initial_volume,
4647
skin_thickness,
48+
unstretched_thickness: skin_thickness,
49+
unstretched_radius,
50+
stress: 0.0,
51+
strain: 1.0,
4752
}
4853
}
4954

55+
pub fn surface_area(self) -> f32 {
56+
sphere_surface_area(sphere_radius_from_volume(self.lift_gas.volume()))
57+
}
58+
59+
pub fn radius(self) -> f32 {
60+
sphere_radius_from_volume(self.volume())
61+
}
62+
63+
pub fn volume(self) -> f32 {
64+
self.lift_gas.volume()
65+
}
66+
67+
fn set_volume(&mut self, new_volume: f32) {
68+
self.lift_gas.set_volume(new_volume)
69+
}
70+
71+
pub fn pressure(&mut self) -> f32 {
72+
self.lift_gas.pressure()
73+
}
74+
75+
fn set_pressure(&mut self, new_pressure: f32) {
76+
self.lift_gas.set_pressure(new_pressure)
77+
}
78+
79+
fn set_thickness(&mut self, new_thickness: f32) {
80+
self.skin_thickness = new_thickness
81+
}
82+
83+
fn gage_pressure(self, external_pressure: f32) -> f32 {
84+
self.lift_gas.pressure() - external_pressure
85+
}
86+
87+
pub fn stress(self) -> f32 {
88+
self.stress
89+
}
90+
91+
fn set_stress(&mut self, external_pressure: f32) {
92+
// hoop stress (Pa) of thin-walled hollow sphere from internal pressure
93+
// https://en.wikipedia.org/wiki/Pressure_vessel#Stress_in_thin-walled_pressure_vessels
94+
// https://pkel015.connect.amazon.auckland.ac.nz/SolidMechanicsBooks/Part_I/BookSM_Part_I/07_ElasticityApplications/07_Elasticity_Applications_03_Presure_Vessels.pdf
95+
self.stress = self.gage_pressure(external_pressure) * self.radius() / (2.0 * self.skin_thickness);
96+
if self.stress > self.material.max_stress {
97+
self.burst(format!(
98+
"Hoop stress ({:?} Pa) exceeded maximum stress ({:?} Pa)",
99+
self.stress, self.material.max_stress
100+
));
101+
}
102+
}
103+
104+
pub fn strain(self) -> f32 {
105+
self.strain
106+
}
107+
108+
fn set_strain(&mut self) {
109+
// strain (%) of thin-walled hollow sphere from internal pressure
110+
// https://en.wikipedia.org/wiki/Pressure_vessel#Stress_in_thin-walled_pressure_vessels
111+
// https://pkel015.connect.amazon.auckland.ac.nz/SolidMechanicsBooks/Part_I/BookSM_Part_I/07_ElasticityApplications/07_Elasticity_Applications_03_Presure_Vessels.pdf
112+
self.strain = self.radius() / self.unstretched_radius;
113+
if self.strain > self.material.max_strain {
114+
self.burst(format!(
115+
"Tangential strain ({:?} %) exceeded maximum strain ({:?} %)",
116+
self.strain * 100.0,
117+
self.material.max_strain * 100.0
118+
));
119+
}
120+
}
121+
122+
fn radial_displacement(self, external_pressure: f32) -> f32 {
123+
// https://pkel015.connect.amazon.auckland.ac.nz/SolidMechanicsBooks/Part_I/BookSM_Part_I/07_ElasticityApplications/07_Elasticity_Applications_03_Presure_Vessels.pdf
124+
((1.0 - self.material.poissons_ratio) / self.material.elasticity)
125+
* ((self.gage_pressure(external_pressure) * libm::powf(self.radius(), 2.0)) / 2.0
126+
* self.skin_thickness)
127+
}
128+
129+
fn rebound(&mut self, radial_displacement: f32) -> f32 {
130+
// https://physics.stackexchange.com/questions/10372/inflating-a-balloon-expansion-resistance
131+
self.set_thickness(
132+
self.unstretched_thickness * libm::powf(self.unstretched_radius / self.radius(), 2.0),
133+
);
134+
2.0 * self.material.elasticity
135+
* radial_displacement
136+
* self.unstretched_thickness
137+
* self.unstretched_radius
138+
/ libm::powf(self.radius(), 3.0)
139+
}
140+
50141
pub fn stretch(&mut self, external_pressure: f32) {
51142
// stretch the balloon and/or compress the gas inside.
52143
// - the gas wants to be at the same pressure as ambient
@@ -57,59 +148,58 @@ impl Balloon {
57148
// - the balloon fails when it starts to plasticly deform, in other
58149
// words the balloon stretches as long as tangential stress is less
59150
// than the material's yield stress
151+
debug!(
152+
"current gage pressure: {:?}",
153+
self.gage_pressure(external_pressure)
154+
);
155+
156+
self.set_stress(external_pressure);
157+
self.set_strain();
60158

61-
let mut equilibrium_gas = self.lift_gas.clone();
62-
equilibrium_gas.set_pressure(external_pressure);
63-
64-
// percent elongation aka tangential strain (m/m)
65-
let original_radius = sphere_radius_from_volume(self.initial_volume);
66-
let equilibrium_radius = sphere_radius_from_volume(equilibrium_gas.volume());
67-
let elongation = (equilibrium_radius - original_radius) / original_radius;
68-
if elongation < self.material.max_elongation {
69-
self.volume = self.lift_gas.volume();
70-
self.lift_gas.set_pressure(external_pressure);
71-
} else {
72-
self.burst()
73-
// self.volume = spherical_volume(original_radius * self.material.max_elongation);
74-
// self.lift_gas.set_volume(self.volume);
159+
if self.intact {
160+
let delta_r = self.radial_displacement(external_pressure);
161+
debug!(
162+
"radius before stretch: {:?} delta_r: {:?}",
163+
self.radius(),
164+
delta_r
165+
);
166+
let internal_pressure = self.rebound(delta_r);
167+
self.set_pressure(internal_pressure + external_pressure);
168+
debug!("radius after stretch: {:?}", self.radius());
169+
debug!(
170+
"gage pressure after stretch: {:?}",
171+
self.gage_pressure(external_pressure)
172+
);
75173
}
76-
// let stress = tangential_stress(
77-
// self.lift_gas.pressure() - external_pressure,
78-
// self.volume,
79-
// self.balloon_thickness,
80-
// );
81-
// if stress > self.material.max_stress
82-
// {
83-
// self.burst();
84-
// }
85-
}
86-
87-
fn burst(&mut self) {
174+
175+
}
176+
177+
fn burst(&mut self, reason: String) {
88178
// Assert new balloon attributes to reflect that it has burst
89179
self.intact = false;
90-
self.volume = 0.0;
180+
self.set_volume(0.0);
91181
self.lift_gas.set_mass(0.0);
182+
log::warn!("The balloon has burst! Reason: {:?}", reason)
92183
}
93184
}
94185

95-
fn spherical_volume(radius: f32) -> f32 {
186+
fn sphere_volume(radius: f32) -> f32 {
96187
(4.0 / 3.0) * PI * libm::powf(radius, 3.0)
97188
}
98189

99190
fn shell_volume(internal_radius: f32, thickness: f32) -> f32 {
100191
let external_radius = internal_radius + thickness;
101-
let internal_volume = spherical_volume(internal_radius);
102-
let external_volume = spherical_volume(external_radius);
192+
let internal_volume = sphere_volume(internal_radius);
193+
let external_volume = sphere_volume(external_radius);
103194
external_volume - internal_volume
104195
}
105196

106197
fn sphere_radius_from_volume(volume: f32) -> f32 {
107198
libm::powf(volume, 1.0 / 3.0) / (4.0 / 3.0) * PI
108199
}
109200

110-
fn tangential_stress(pressure_difference: f32, internal_volume: f32, shell_thickness: f32) -> f32 {
111-
// tangential stress (Pa) of hollow sphere from internal pressure
112-
pressure_difference * sphere_radius_from_volume(internal_volume) / (2.0 * shell_thickness)
201+
fn sphere_surface_area(radius: f32) -> f32 {
202+
4.0 * PI * libm::powf(radius, 2.0)
113203
}
114204

115205
// ----------------------------------------------------------------------------
@@ -119,22 +209,26 @@ fn tangential_stress(pressure_difference: f32, internal_volume: f32, shell_thick
119209
// Source: https://www.matweb.com/
120210
// ----------------------------------------------------------------------------
121211

122-
#[derive(Clone, PartialEq)]
212+
#[derive(Copy, Clone, PartialEq)]
123213
pub struct Material {
124214
pub max_temperature: f32, // temperature (K) where the given material fails
125-
pub density: f32, // density (kg/m^3)
126-
pub emissivity: f32, // emissivity coefficient of the material for blackbody light
215+
pub density: f32, // density (kg/m^3)
216+
pub emissivity: f32, // how much thermal radiation is emitted
217+
pub absorptivity: f32, // how much thermal radiation is absorbed
127218
pub thermal_conductivity: f32, // thermal conductivity (W/mK) of the material at room temperature
128-
pub max_elongation: f32, // elongation at failure (decimal, unitless) 1 = original size
129-
pub max_stress: f32, // tangential stress at failure (Pa)
219+
pub specific_heat: f32, // J/kgK
220+
pub poissons_ratio: f32, // ratio of change in width for a given change in length
221+
pub elasticity: f32, // Youngs Modulus aka Modulus of Elasticity (Pa)
222+
pub max_strain: f32, // elongation at failure (decimal, unitless) 1 = original size
223+
pub max_stress: f32, // tangential stress at failure (Pa)
130224
}
131225

132226
impl Material {
133227
pub fn new(material_type: MaterialType) -> Self {
134228
match material_type {
135229
MaterialType::Rubber => RUBBER,
136-
MaterialType::LowDensityPolyethylene => LOW_DENSITY_POLYETHYLENE,
137-
_ => NOTHING
230+
MaterialType::LDPE | MaterialType::LowDensityPolyethylene => LOW_DENSITY_POLYETHYLENE,
231+
_ => NOTHING,
138232
}
139233
}
140234
}
@@ -144,6 +238,7 @@ pub enum MaterialType {
144238
// Species of gas with a known molar mass (kg/mol)
145239
Nothing,
146240
Rubber,
241+
LDPE,
147242
LowDensityPolyethylene,
148243
}
149244

@@ -152,7 +247,9 @@ impl fmt::Display for MaterialType {
152247
match *self {
153248
MaterialType::Nothing => write!(f, "nothing"),
154249
MaterialType::Rubber => write!(f, "rubber"),
155-
MaterialType::LowDensityPolyethylene => write!(f, "low-density polyethylene (LDPE)"),
250+
MaterialType::LDPE | MaterialType::LowDensityPolyethylene => {
251+
write!(f, "low-density polyethylene (LDPE)")
252+
}
156253
}
157254
}
158255
}
@@ -162,27 +259,42 @@ pub const NOTHING: Material = Material {
162259
max_temperature: f32::INFINITY,
163260
density: 0.0,
164261
emissivity: 1.0,
262+
absorptivity: 0.0,
165263
thermal_conductivity: f32::INFINITY,
166-
max_elongation: f32::INFINITY,
264+
specific_heat: 0.0,
265+
poissons_ratio: 0.5,
266+
elasticity: f32::INFINITY,
267+
max_strain: f32::INFINITY,
167268
max_stress: f32::INFINITY,
168269
};
169270

170271
pub const RUBBER: Material = Material {
171-
// Natural Rubber, Vulcanized (NR, IR, Polyisoprene)
172-
max_temperature: 400.0,
173-
density: 950.0,
272+
// Nitrile Butadiene Rubber
273+
// https://designerdata.nl/materials/plastics/rubbers/nitrile-butadiene-rubber
274+
max_temperature: 385.0,
275+
density: 1000.0,
174276
emissivity: 0.86,
175-
thermal_conductivity: 0.34,
176-
max_elongation: 8.0,
177-
max_stress: 150_000_000.0,
277+
absorptivity: 0.86,
278+
thermal_conductivity: 0.25,
279+
specific_heat: 1490.0,
280+
poissons_ratio: 0.5,
281+
elasticity: 4_000_000.0,
282+
// max_strain: 8.0,
283+
max_strain: 8.0,
284+
max_stress: 25_000_000.0,
178285
};
179286

180287
pub const LOW_DENSITY_POLYETHYLENE: Material = Material {
181-
// Low Density Polyethylene (LDPE), Film Grade
182-
max_temperature: 380.0,
183-
density: 910.0,
288+
// Low Density Polyethylene (LDPE)
289+
// https://designerdata.nl/materials/plastics/thermo-plastics/low-density-polyethylene
290+
max_temperature: 348.0,
291+
density: 919.0,
184292
emissivity: 0.94,
185-
thermal_conductivity: 0.15,
186-
max_elongation: 1.0,
187-
max_stress: 300_000_000.0,
188-
};
293+
absorptivity: 0.94,
294+
thermal_conductivity: 0.3175,
295+
specific_heat: 2600.0,
296+
poissons_ratio: 0.5,
297+
elasticity: 300_000_000.0,
298+
max_strain: 6.25,
299+
max_stress: 10_000_000.0,
300+
};

0 commit comments

Comments
 (0)