Public
Edited
Jul 20, 2024
2 forks
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
math = {
let math = await require("mathjs@13.0.3")

function timeRange(t0, tf, h){
let t = [t0]
h = h ? h : math.divide(math.subtract(tf, t0), 4)
let n = 0
while(isNotDone(t[n], h, tf)){
h = trimStep(t[n], h, tf)
t.push(math.add(t[n],h))
n ++
}
return t.length > 1 ? t : []
}

function isNotDone(t_n, h, tf){
// Checks if the time has reached the final time in the direction of the time step
if(math.isPositive(h)){
// If time step is positive, return true if
return math.smaller(t_n, tf)
}
else if(math.isNegative(h)){
return math.smaller(tf, t_n)
}
}

function trimStep(t_n, h, tf){
const next = math.add(t_n,h)
if(math.isPositive(h)){
if(math.larger(next, tf)){
h = math.subtract(tf, t_n)
}
}
else if(math.isNegative(h)){
if(math.smaller(next, tf)){
h = math.subtract(tf, t_n)
}
}
return h
}
function odeEuler (f, T, y0) {
// https://mathworld.wolfram.com/EulerForwardMethod.html
const t = timeRange(...T)
const N = t.length - 1 // number of times the method has to run

let y = [y0]

for (let n = 0; n < N; n++) {
const h = math.subtract(t[n + 1], t[n])
y.push(
math.add(
y[n],
math.dotMultiply(
h,
f(t[n], y[n])
)
)
)
}
return { t, y }
}
math.import({
odeEuler: math.typed('odeEuler', {
// As odeEuler requires function, Array, Array and returns an object with two arrays by default, this uses math.typed to do the convertions automatically
'function, Array, Array': odeEuler,
'function, Array, number|Unit': (f, T, y0) => {
const sol = odeEuler(f, T, [y0])
return { t: sol.t, y: sol.y.map(y => y[0]) }
},
'function, Matrix, Matrix': (f, T, y0) => {
const sol = odeEuler(f, T.toArray(), y0.toArray())
return { t: math.matrix(sol.t), y: math.matrix(sol.y) }
}
})
})
return math
}
Insert cell
function calc(expression) {
return math.evaluate(expression, scope);
}
Insert cell
stages = ['stage1', 'interstage', 'stage2', 'unpowered1', 'insertion', 'unpowered2']
Insert cell
stageLabel = ['Stage 1', 'Interstage', 'Stage 2', 'Unpowered 1', 'Insertion', 'Unpowered 2']
Insert cell
Insert cell
resultNames = stages.map(stageName => `result_${stageName}`)
Insert cell
rocket = {
let stages = []
results.forEach(
(result, i) =>
result.y.forEach(
(y, j) =>
stages.push(
{
stage : stageLabel[i],
t: result.t[j],
r: y[0],
v: y[1],
m: y[2],
phi: y[3],
gamma: y[4]
}
)
)
)
return stages
}
Insert cell
scope = new Map([
["v0", math.unit(v0, "m/s")], // Initial velocity (must be non-zero because ODE is ill-conditioned)
])
Insert cell
results = {
parsedExpressions.evaluate(scope)
return resultNames.map(resultName => {
const result = scope.get(resultName)
return {t: result.t.toArray(), y: result.y.toArray()}
})
}
Insert cell
parsedExpressions = math.parse(expressions)
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more