Publish v9.0.0

This commit is contained in:
Jos de Jong 2021-01-16 12:33:09 +01:00
parent 85572fa57e
commit 188c7240fa
31 changed files with 568 additions and 230 deletions

View File

@ -295,23 +295,35 @@ math.evaluate('0o77') // 63
math.evaluate('0xff') // 255
```
Non-decimal literals are parsed as 32 bit signed integers:
A word size suffix can be used to change the behavior of non decimal literal evaluation:
```js
math.evaluate('0xffffffff') // -1
math.evaluate('0xfffffffff') // SyntaxError: String "0xfffffffff" is out of range
math.evaluate('0xffi8') // -1
math.evaluate('0xffffffffi32') // -1
math.evaluate('0xfffffffffi32') // SyntaxError: String "0xfffffffff" is out of range
```
Numbers can be formatted as binary, octal, and hex strings using the functions
`bin`, `oct`, and `hex`:
Numbers can be formatted as binary, octal, and hex strings using the `notation` option of the `format` function:
```js
math.evaluate('bin(3)') // '0b11'
math.evaluate('oct(63)') // '0o77'
math.evaluate('hex(255)') // '0xff'
math.evaluate('hex(-1)') // '0xffffffff'
math.evaluate('hex(2.3)') // Error: Value must be an integer
math.evaluate('hex(2^35)') // Error: Value must be in range [-2^31, 2^31-1]
math.evaluate('format(3, {notation: "bin"})') // '0b11'
math.evaluate('format(63, {notation: "oct"})') // '0o77'
math.evaluate('format(255, {notation: "hex"})') // '0xff'
math.evaluate('format(-1, {notation: "hex"})') // '-0x1'
math.evaluate('format(2.3, {notation: "hex"})') // '0x2.4cccccccccccc'
```
The `format` function accepts a `wordSize` option to use in conjunction with the non binary notations:
```js
math.evaluate('format(-1, {notation: "hex", wordSize: 8})') // '0xffi8'
```
The functions `bin`, `oct`, and `hex` are shorthand for the `format` function with `notation` set accordingly:
```js
math.evaluate('bin(-1)') // '-0b1'
math.evaluate('bin(-1, 8)') // '0b11111111i8'
```
<h3 id="bignumbers">BigNumbers <a href="#bignumbers" title="Permalink">#</a></h3>

View File

@ -20,6 +20,7 @@ math.bin(value)
Parameter | Type | Description
--------- | ---- | -----------
`value` | number | Value to be stringified
`wordSize` | number | Optional word size (see `format`)
<h3 id="returns">Returns <a href="#returns" title="Permalink">#</a></h3>

View File

@ -41,6 +41,17 @@ math.format(value, callback)
`lower` and `upper` bounds, and uses exponential notation elsewhere.
Lower bound is included, upper bound is excluded.
For example '123.4' and '1.4e7'.
- 'bin', 'oct, or 'hex'
Format the number using binary, octal, or hexadecimal notation.
For example '0b1101' and '0x10fe'.
- `wordSize: number`
The word size in bits to use for formatting in binary, octal, or
hexadecimal notation. To be used only with 'bin', 'oct', or 'hex'
values for 'notation' option. When this option is defined the value
is formatted as a signed twos complement integer of the given word
size and the size suffix is appended to the output.
For example format(-1, {notation: 'hex', wordSize: 8}) === '0xffi8'.
Default value is undefined.
- `precision: number`
A number between 0 and 16 to round the digits of the number. In case
of notations 'exponential', 'engineering', and 'auto', `precision`

View File

@ -20,6 +20,7 @@ math.hex(value)
Parameter | Type | Description
--------- | ---- | -----------
`value` | number | Value to be stringified
`wordSize` | number | Optional word size (see `format`)
<h3 id="returns">Returns <a href="#returns" title="Permalink">#</a></h3>

View File

@ -20,6 +20,7 @@ math.oct(value)
Parameter | Type | Description
--------- | ---- | -----------
`value` | number | Value to be stringified
`wordSize` | number | Optional word size (see `format`)
<h3 id="returns">Returns <a href="#returns" title="Permalink">#</a></h3>

View File

@ -29,7 +29,7 @@ Math.js can be downloaded or linked from various content delivery networks:
<tbody>
<tr>
<td>unpkg</td>
<td><a href="https://unpkg.com/mathjs@8.1.1/">https://unpkg.com/mathjs@8.1.1/</a></td>
<td><a href="https://unpkg.com/mathjs@9.0.0/">https://unpkg.com/mathjs@9.0.0/</a></td>
</tr>
<tr>
<td>cdnjs</td>
@ -49,8 +49,8 @@ Math.js can be downloaded or linked from various content delivery networks:
Or download the full bundle directly from [unpkg](https://unpkg.com):
<p>
<a href="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js">
math.js (version 8.1.1, <span id="size">141 kB</span>, minified and gzipped)
<a href="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js">
math.js (version 9.0.0, <span id="size">141 kB</span>, minified and gzipped)
</a>
</p>

View File

@ -15,7 +15,7 @@
}
</style>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -24,7 +24,7 @@ File: [angle_configuration.html](angle_configuration.html) (click for a live dem
}
</style>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>math.js | basic usage</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -12,7 +12,7 @@ File: [basic_usage.html](basic_usage.html) (click for a live demo)
<head>
<meta charset="utf-8">
<title>math.js | basic usage</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>math.js | currency conversion</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<style>
body,

View File

@ -13,7 +13,7 @@ File: [currency_conversion.html](currency_conversion.html) (click for a live dem
<meta charset="utf-8">
<title>math.js | currency conversion</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<style>
body,

View File

@ -15,7 +15,7 @@
}
</style>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -24,7 +24,7 @@ File: [custom_separators.html](custom_separators.html) (click for a live demo)
}
</style>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
</head>
<body>

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>math.js | plot</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://cdn.plot.ly/plotly-1.35.2.min.js"></script>

View File

@ -12,7 +12,7 @@ File: [plot.html](plot.html) (click for a live demo)
<head>
<meta charset="utf-8">
<title>math.js | plot</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://cdn.plot.ly/plotly-1.35.2.min.js"></script>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>math.js | pretty printing with MathJax</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>

View File

@ -13,7 +13,7 @@ File: [pretty_printing_with_mathjax.html](pretty_printing_with_mathjax.html) (cl
<meta charset="utf-8">
<title>math.js | pretty printing with MathJax</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>math.js | printing HTML</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<style>
body {

View File

@ -13,7 +13,7 @@ File: [printing_html.html](printing_html.html) (click for a live demo)
<meta charset="utf-8">
<title>math.js | printing HTML</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<style>
body {

View File

@ -9,7 +9,7 @@
<script>
// load math.js using require.js
require(['https://unpkg.com/mathjs@8.1.1/lib/browser/math.js'], function (math) {
require(['https://unpkg.com/mathjs@9.0.0/lib/browser/math.js'], function (math) {
// evaluate some expression
const result = math.evaluate('1.2 * (2 + 4.5)')
document.write(result)

View File

@ -18,7 +18,7 @@ File: [requirejs_loading.html](requirejs_loading.html) (click for a live demo)
<script>
// load math.js using require.js
require(['https://unpkg.com/mathjs@8.1.1/lib/browser/math.js'], function (math) {
require(['https://unpkg.com/mathjs@9.0.0/lib/browser/math.js'], function (math) {
// evaluate some expression
const result = math.evaluate('1.2 * (2 + 4.5)')
document.write(result)

View File

@ -1,152 +1,301 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>math.js | rocket trajectory optimization</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<style>
body {
font-family: sans-serif;
}
#canvas-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 5%;
margin-top: 5%;
}
#canvas-grid>div {
overflow: hidden;
}
</style>
</head>
<body>
<h1>Rocket trajectory optimization</h1>
<p>
This example simulates the ascent stage of the Apollo Lunar Module modeled using a system of ordinary differential equations.
This example simulates the launch of a SpaceX Falcon 9 modeled using a system of ordinary differential equations.
</p>
<canvas id=canvas1 width=1600 height=400></canvas>
<canvas id=canvas2 width=800 height=400></canvas>
<canvas id="canvas" width="1600" height="600"></canvas>
<div id="canvas-grid"></div>
<script>
// Solve ODE `dx/dt = f(x,t), x(0) = x0` numerically.
function ndsolve(f, x0, dt, tmax) {
const n = f.size()[0] // Number of variables
const x = x0.clone() // Current values of variables
const dxdt = [] // Temporary variable to hold time-derivatives
const result = [] // Contains entire solution
let x = x0.clone() // Current values of variables
const result = [x] // Contains entire solution
const nsteps = math.divide(tmax, dt) // Number of time steps
for(let i=0; i<nsteps; i++) {
for (let i = 0; i < nsteps; i++) {
// Compute derivatives
for(let j=0; j<n; j++) {
dxdt[j] = f.get([j]).apply(null, x.toArray())
}
const dxdt = f.map(func => func(...x.toArray()))
// Euler method to compute next time step
for(let j=0; j<n; j++) {
x.set([j], math.add(x.get([j]), math.multiply(dxdt[j], dt)))
}
result.push(x.clone())
const dx = math.multiply(dxdt, dt)
x = math.add(x, dx)
result.push(x)
}
return math.matrix(result)
}
// Import the numerical ODE solver
math.import({ndsolve:ndsolve})
math.import({ ndsolve })
// Create a math.js context for our simulation. Everything else occurs in the context of the expression parser!
const sim = math.parser()
sim.evaluate("G = 6.67408e-11 m^3 kg^-1 s^-2") // Gravitational constant
sim.evaluate("mbody = 5.972e24 kg") // Mass of Earth
sim.evaluate("mu = G * mbody")
sim.evaluate("dt = 1.0 s") // Simulation timestep
sim.evaluate("tfinal = 162 s") // Simulation duration
sim.evaluate("T = 1710000 lbf * 0.9") // Engine thrust
sim.evaluate("mbody = 5.9724e24 kg") // Mass of Earth
sim.evaluate("mu = G * mbody") // Standard gravitational parameter
sim.evaluate("g0 = 9.80665 m/s^2") // Standard gravity: used for calculating prop consumption (dmdt)
sim.evaluate("isp = 290 s") // Specific impulse
sim.evaluate("gamma0 = 89.99883 deg") // Initial pitch angle (90 deg is vertical)
sim.evaluate("r0 = 6378.1370 km") // Equatorial radius of Earth
sim.evaluate("v0 = 10 m/s") // Initial velocity (must be non-zero because ODE is ill-conditioned)
sim.evaluate("r0 = 6371 km") // Mean radius of Earth
sim.evaluate("t0 = 0 s") // Simulation start
sim.evaluate("dt = 0.5 s") // Simulation timestep
sim.evaluate("tfinal = 149.5 s") // Simulation duration
sim.evaluate("isp_sea = 282 s") // Specific impulse (at sea level)
sim.evaluate("isp_vac = 311 s") // Specific impulse (in vacuum)
sim.evaluate("gamma0 = 89.99970 deg") // Initial pitch angle (90 deg is vertical)
sim.evaluate("v0 = 1 m/s") // Initial velocity (must be non-zero because ODE is ill-conditioned)
sim.evaluate("phi0 = 0 deg") // Initial orbital reference angle
sim.evaluate("m0 = 1207920 lbm + 30000 lbm") // Initial mass of rocket and fuel
sim.evaluate("m1 = 433100 kg") // First stage mass
sim.evaluate("m2 = 111500 kg") // Second stage mass
sim.evaluate("m3 = 1700 kg") // Third stage / fairing mass
sim.evaluate("mp = 5000 kg") // Payload mass
sim.evaluate("m0 = m1+m2+m3+mp") // Initial mass of rocket
sim.evaluate("dm = 2750 kg/s") // Mass flow rate
sim.evaluate("A = (3.66 m)^2 * pi") // Area of the rocket
sim.evaluate("dragCoef = 0.2") // Drag coefficient
// Define the equations of motion. It is important to maintain the same argument order for each of these functions.
sim.evaluate("drdt(r, v, m, phi, gamma) = v sin(gamma)")
sim.evaluate("dvdt(r, v, m, phi, gamma) = -mu / r^2 * sin(gamma) + T / m")
sim.evaluate("dmdt(r, v, m, phi, gamma) = -T/g0/isp")
sim.evaluate("dphidt(r, v, m, phi, gamma) = v/r * cos(gamma) * rad")
sim.evaluate("dgammadt(r, v, m, phi, gamma) = (1/r * (v - mu / (r v)) * cos(gamma)) * rad")
// Define the equations of motion. We just thrust into current direction of motion, e.g. making a gravity turn.
sim.evaluate("gravity(r) = mu / r^2")
sim.evaluate("angVel(r, v, gamma) = v/r * cos(gamma) * rad") // Angular velocity of rocket around moon
sim.evaluate("density(r) = 1.2250 kg/m^3 * exp(-g0 * (r - r0) / (83246.8 m^2/s^2))") // Assume constant temperature
sim.evaluate("drag(r, v) = 1/2 * density(r) .* v.^2 * A * dragCoef")
sim.evaluate("isp(r) = isp_vac + (isp_sea - isp_vac) * density(r)/density(r0)") // pressure ~ density for constant temperature
sim.evaluate("thrust(isp) = g0 * isp * dm")
// It is important to maintain the same argument order for each of these functions.
sim.evaluate("drdt(r, v, m, phi, gamma, t) = v sin(gamma)")
sim.evaluate("dvdt(r, v, m, phi, gamma, t) = - gravity(r) * sin(gamma) + (thrust(isp(r)) - drag(r, v)) / m")
sim.evaluate("dmdt(r, v, m, phi, gamma, t) = - dm")
sim.evaluate("dphidt(r, v, m, phi, gamma, t) = angVel(r, v, gamma)")
sim.evaluate("dgammadt(r, v, m, phi, gamma, t) = angVel(r, v, gamma) - gravity(r) * cos(gamma) / v * rad")
sim.evaluate("dtdt(r, v, m, phi, gamma, t) = 1")
// Again, remember to maintain the same variable order in the call to ndsolve.
sim.evaluate("result_stage1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], [r0, v0, m0, phi0, gamma0], dt, tfinal)")
// Remember to maintain the same variable order in the call to ndsolve.
sim.evaluate("result_stage1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], [r0, v0, m0, phi0, gamma0, t0], dt, tfinal)")
// Reset initial conditions for interstage flight
sim.evaluate("T = 0 lbf")
sim.evaluate("tfinal = 12 s")
sim.evaluate("x = flatten(result_stage1[result_stage1.size()[1],:])")
sim.evaluate("result_interstage = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
console.log(sim.evaluate("result_interstage[result_interstage.size()[1],3]").toString())
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 10 s")
sim.evaluate("x = flatten(result_stage1[end,:])")
sim.evaluate("x[3] = m2+m3+mp") // New mass after stage seperation
sim.evaluate("result_interstage = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for stage 2 flight
sim.evaluate("T = 210000 lbf")
sim.evaluate("isp = 348 s")
sim.evaluate("tfinal = 397 s")
sim.evaluate("x = flatten(result_interstage[result_interstage.size()[1],:])")
sim.evaluate("x[3] = 273600 lbm") // Lighten the rocket a bit since we discarded the first stage
sim.evaluate("result_stage2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
sim.evaluate("dm = 270.8 kg/s")
sim.evaluate("isp_vac = 348 s")
sim.evaluate("tfinal = 350 s")
sim.evaluate("x = flatten(result_interstage[end,:])")
sim.evaluate("result_stage2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for unpowered flight
sim.evaluate("T = 0 lbf")
sim.evaluate("tfinal = 60 s")
sim.evaluate("x = flatten(result_stage2[result_stage2.size()[1],:])")
sim.evaluate("result_unpowered = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 900 s")
sim.evaluate("dt = 10 s")
sim.evaluate("x = flatten(result_stage2[end,:])")
sim.evaluate("result_unpowered1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for final orbit insertion
sim.evaluate("dm = 270.8 kg/s")
sim.evaluate("tfinal = 39 s")
sim.evaluate("dt = 0.5 s")
sim.evaluate("x = flatten(result_unpowered1[end,:])")
sim.evaluate("result_insertion = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for unpowered flight
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 250 s")
sim.evaluate("dt = 10 s")
sim.evaluate("x = flatten(result_insertion[end,:])")
sim.evaluate("result_unpowered2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Extract the useful information from the results so it can be plotted
const data_stage1 = sim.evaluate("transpose(concat( transpose( result_stage1[:,4] - phi0) * r0 / rad / km, ( transpose(result_stage1[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_interstage = sim.evaluate("transpose(concat( transpose(result_interstage[:,4] - phi0) * r0 / rad / km, (transpose(result_interstage[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_stage2 = sim.evaluate("transpose(concat( transpose( result_stage2[:,4] - phi0) * r0 / rad / km, ( transpose(result_stage2[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_unpowered = sim.evaluate("transpose(concat( transpose( result_unpowered[:,4] - phi0) * r0 / rad / km, ( transpose(result_unpowered[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
// Now it's time to prepare results for plotting
const resultNames = ['stage1', 'interstage', 'stage2', 'unpowered1', 'insertion', 'unpowered2']
.map(stageName => `result_${stageName}`)
window['chart'] = new Chart(document.getElementById('canvas1'), {
// Concat result matrices
sim.set('result',
math.concat(
...resultNames.map(resultName =>
sim.evaluate(`${resultName}[:end-1, :]`) // Avoid overlap
),
0 // Concat in row-dimension
)
)
const mainDatasets = resultNames.map((resultName, i) => ({
label: resultName.slice(7),
data: sim.evaluate(
'concat('
+ `(${resultName}[:,4] - phi0) * r0 / rad / km,` // Surface distance from start (in km)
+ `(${resultName}[:,1] - r0) / km` // Height above surface (in km)
+ ')'
).toArray().map(([x, y]) => ({ x, y })),
borderColor: i % 2 ? '#999' : '#dc3912',
fill: false,
pointRadius: 0,
}))
new Chart(document.getElementById('canvas'), {
type: 'line',
data: {
datasets: [{
label: "Stage 1",
data: data_stage1,
fill: false,
borderColor: "red",
pointRadius: 0
}, {
label: "Interstage",
data: data_interstage,
fill: false,
borderColor: "green",
pointRadius: 0
}, {
label: "Stage 2",
data: data_stage2,
fill: false,
borderColor: "orange",
pointRadius: 0
}, {
label: "Unpowered",
data: data_unpowered,
fill: false,
borderColor: "blue",
pointRadius: 0
}]
},
options: {
data: { datasets: mainDatasets },
options: getMainChartOptions()
})
createChart([{
label: 'velocity (in m/s)',
data: sim.evaluate("result[:,[2,6]]")
.toArray()
.map(([v, t]) => ({ x: t.toNumber('s'), y: v.toNumber('m/s') }))
}])
createChart([{
label: 'height (in km)',
data: sim.evaluate("concat((result[:, 1] - r0), result[:, 6])")
.toArray()
.map(([r, t]) => ({ x: t.toNumber('s'), y: r.toNumber('km') })),
}])
createChart([{
label: 'gamma (in deg)',
data: sim.evaluate("result[:, [5,6]]")
.toArray()
.map(([gamma, t]) => ({ x: t.toNumber('s'), y: gamma.toNumber('deg') })),
}])
createChart([{
label: 'acceleration (in m/s^2)',
data: sim.evaluate("concat(diff(result[:, 2]) ./ diff(result[:, 6]), result[:end-1, 6])")
.toArray()
.map(([acc, t]) => ({ x: t.toNumber('s'), y: acc.toNumber('m/s^2') })),
}])
createChart([{
label: 'drag acceleration (in m/s^2)',
data: sim.evaluate("concat(drag(result[:, 1], result[:, 2]) ./ result[:, 3], result[:, 6])")
.toArray()
.map(([dragAcc, t]) => ({ x: t.toNumber('s'), y: dragAcc.toNumber('m/s^2') })),
}])
createChart(
[
{
data: sim.evaluate("result[:, [1,4]]")
.toArray()
.map(([r, phi]) => math.rotate([r.toNumber('km'), 0], phi))
.map(([x, y]) => ({ x, y })),
},
{
data: sim.evaluate("map(0:0.25:360, function(angle) = rotate([r0/km, 0], angle))")
.toArray()
.map(([x, y]) => ({ x, y })),
borderColor: "#999",
fill: true
}
],
getEarthChartOptions()
)
// Helper functions for plotting data (nothing to learn about math.js from here on)
function createChart(datasets, options = {}) {
const container = document.createElement("div")
document.querySelector("#canvas-grid").appendChild(container)
const canvas = document.createElement("canvas")
container.appendChild(canvas)
new Chart(canvas, {
type: 'line',
data: {
datasets: datasets.map(dataset => ({
borderColor: "#dc3912",
fill: false,
pointRadius: 0,
...dataset
}))
},
options: getChartOptions(options)
})
}
function getMainChartOptions() {
return {
scales: {
xAxes: [{
type: 'linear',
position: 'bottom'
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'surface distance travelled (in km)'
}
}],
yAxes: [{
type: 'linear',
scaleLabel: {
display: true,
labelString: 'height above surface (in km)'
}
}]
}
},
animation: false
}
}
})
function getChartOptions(options) {
return {
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'time (in s)'
}
}]
},
animation: false,
...options
}
}
function getEarthChartOptions() {
return {
aspectRatio: 1,
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
min: -8000,
max: 8000,
display: false
}],
yAxes: [{
type: 'linear',
min: -8000,
max: 8000,
display: false
}]
},
legend: { display: false }
}
}
</script>
</body>
</html>
</html>

View File

@ -9,157 +9,305 @@ File: [rocket_trajectory_optimization.html](rocket_trajectory_optimization.html)
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>math.js | rocket trajectory optimization</title>
<script src="https://unpkg.com/mathjs@8.1.1/lib/browser/math.js"></script>
<script src="https://unpkg.com/mathjs@9.0.0/lib/browser/math.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
<style>
body {
font-family: sans-serif;
}
#canvas-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 5%;
margin-top: 5%;
}
#canvas-grid>div {
overflow: hidden;
}
</style>
</head>
<body>
<h1>Rocket trajectory optimization</h1>
<p>
This example simulates the ascent stage of the Apollo Lunar Module modeled using a system of ordinary differential equations.
This example simulates the launch of a SpaceX Falcon 9 modeled using a system of ordinary differential equations.
</p>
<canvas id=canvas1 width=1600 height=400></canvas>
<canvas id=canvas2 width=800 height=400></canvas>
<canvas id="canvas" width="1600" height="600"></canvas>
<div id="canvas-grid"></div>
<script>
// Solve ODE `dx/dt = f(x,t), x(0) = x0` numerically.
function ndsolve(f, x0, dt, tmax) {
const n = f.size()[0] // Number of variables
const x = x0.clone() // Current values of variables
const dxdt = [] // Temporary variable to hold time-derivatives
const result = [] // Contains entire solution
let x = x0.clone() // Current values of variables
const result = [x] // Contains entire solution
const nsteps = math.divide(tmax, dt) // Number of time steps
for(let i=0; i<nsteps; i++) {
for (let i = 0; i < nsteps; i++) {
// Compute derivatives
for(let j=0; j<n; j++) {
dxdt[j] = f.get([j]).apply(null, x.toArray())
}
const dxdt = f.map(func => func(...x.toArray()))
// Euler method to compute next time step
for(let j=0; j<n; j++) {
x.set([j], math.add(x.get([j]), math.multiply(dxdt[j], dt)))
}
result.push(x.clone())
const dx = math.multiply(dxdt, dt)
x = math.add(x, dx)
result.push(x)
}
return math.matrix(result)
}
// Import the numerical ODE solver
math.import({ndsolve:ndsolve})
math.import({ ndsolve })
// Create a math.js context for our simulation. Everything else occurs in the context of the expression parser!
const sim = math.parser()
sim.evaluate("G = 6.67408e-11 m^3 kg^-1 s^-2") // Gravitational constant
sim.evaluate("mbody = 5.972e24 kg") // Mass of Earth
sim.evaluate("mu = G * mbody")
sim.evaluate("dt = 1.0 s") // Simulation timestep
sim.evaluate("tfinal = 162 s") // Simulation duration
sim.evaluate("T = 1710000 lbf * 0.9") // Engine thrust
sim.evaluate("mbody = 5.9724e24 kg") // Mass of Earth
sim.evaluate("mu = G * mbody") // Standard gravitational parameter
sim.evaluate("g0 = 9.80665 m/s^2") // Standard gravity: used for calculating prop consumption (dmdt)
sim.evaluate("isp = 290 s") // Specific impulse
sim.evaluate("gamma0 = 89.99883 deg") // Initial pitch angle (90 deg is vertical)
sim.evaluate("r0 = 6378.1370 km") // Equatorial radius of Earth
sim.evaluate("v0 = 10 m/s") // Initial velocity (must be non-zero because ODE is ill-conditioned)
sim.evaluate("r0 = 6371 km") // Mean radius of Earth
sim.evaluate("t0 = 0 s") // Simulation start
sim.evaluate("dt = 0.5 s") // Simulation timestep
sim.evaluate("tfinal = 149.5 s") // Simulation duration
sim.evaluate("isp_sea = 282 s") // Specific impulse (at sea level)
sim.evaluate("isp_vac = 311 s") // Specific impulse (in vacuum)
sim.evaluate("gamma0 = 89.99970 deg") // Initial pitch angle (90 deg is vertical)
sim.evaluate("v0 = 1 m/s") // Initial velocity (must be non-zero because ODE is ill-conditioned)
sim.evaluate("phi0 = 0 deg") // Initial orbital reference angle
sim.evaluate("m0 = 1207920 lbm + 30000 lbm") // Initial mass of rocket and fuel
sim.evaluate("m1 = 433100 kg") // First stage mass
sim.evaluate("m2 = 111500 kg") // Second stage mass
sim.evaluate("m3 = 1700 kg") // Third stage / fairing mass
sim.evaluate("mp = 5000 kg") // Payload mass
sim.evaluate("m0 = m1+m2+m3+mp") // Initial mass of rocket
sim.evaluate("dm = 2750 kg/s") // Mass flow rate
sim.evaluate("A = (3.66 m)^2 * pi") // Area of the rocket
sim.evaluate("dragCoef = 0.2") // Drag coefficient
// Define the equations of motion. It is important to maintain the same argument order for each of these functions.
sim.evaluate("drdt(r, v, m, phi, gamma) = v sin(gamma)")
sim.evaluate("dvdt(r, v, m, phi, gamma) = -mu / r^2 * sin(gamma) + T / m")
sim.evaluate("dmdt(r, v, m, phi, gamma) = -T/g0/isp")
sim.evaluate("dphidt(r, v, m, phi, gamma) = v/r * cos(gamma) * rad")
sim.evaluate("dgammadt(r, v, m, phi, gamma) = (1/r * (v - mu / (r v)) * cos(gamma)) * rad")
// Define the equations of motion. We just thrust into current direction of motion, e.g. making a gravity turn.
sim.evaluate("gravity(r) = mu / r^2")
sim.evaluate("angVel(r, v, gamma) = v/r * cos(gamma) * rad") // Angular velocity of rocket around moon
sim.evaluate("density(r) = 1.2250 kg/m^3 * exp(-g0 * (r - r0) / (83246.8 m^2/s^2))") // Assume constant temperature
sim.evaluate("drag(r, v) = 1/2 * density(r) .* v.^2 * A * dragCoef")
sim.evaluate("isp(r) = isp_vac + (isp_sea - isp_vac) * density(r)/density(r0)") // pressure ~ density for constant temperature
sim.evaluate("thrust(isp) = g0 * isp * dm")
// It is important to maintain the same argument order for each of these functions.
sim.evaluate("drdt(r, v, m, phi, gamma, t) = v sin(gamma)")
sim.evaluate("dvdt(r, v, m, phi, gamma, t) = - gravity(r) * sin(gamma) + (thrust(isp(r)) - drag(r, v)) / m")
sim.evaluate("dmdt(r, v, m, phi, gamma, t) = - dm")
sim.evaluate("dphidt(r, v, m, phi, gamma, t) = angVel(r, v, gamma)")
sim.evaluate("dgammadt(r, v, m, phi, gamma, t) = angVel(r, v, gamma) - gravity(r) * cos(gamma) / v * rad")
sim.evaluate("dtdt(r, v, m, phi, gamma, t) = 1")
// Again, remember to maintain the same variable order in the call to ndsolve.
sim.evaluate("result_stage1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], [r0, v0, m0, phi0, gamma0], dt, tfinal)")
// Remember to maintain the same variable order in the call to ndsolve.
sim.evaluate("result_stage1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], [r0, v0, m0, phi0, gamma0, t0], dt, tfinal)")
// Reset initial conditions for interstage flight
sim.evaluate("T = 0 lbf")
sim.evaluate("tfinal = 12 s")
sim.evaluate("x = flatten(result_stage1[result_stage1.size()[1],:])")
sim.evaluate("result_interstage = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
console.log(sim.evaluate("result_interstage[result_interstage.size()[1],3]").toString())
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 10 s")
sim.evaluate("x = flatten(result_stage1[end,:])")
sim.evaluate("x[3] = m2+m3+mp") // New mass after stage seperation
sim.evaluate("result_interstage = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for stage 2 flight
sim.evaluate("T = 210000 lbf")
sim.evaluate("isp = 348 s")
sim.evaluate("tfinal = 397 s")
sim.evaluate("x = flatten(result_interstage[result_interstage.size()[1],:])")
sim.evaluate("x[3] = 273600 lbm") // Lighten the rocket a bit since we discarded the first stage
sim.evaluate("result_stage2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
sim.evaluate("dm = 270.8 kg/s")
sim.evaluate("isp_vac = 348 s")
sim.evaluate("tfinal = 350 s")
sim.evaluate("x = flatten(result_interstage[end,:])")
sim.evaluate("result_stage2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for unpowered flight
sim.evaluate("T = 0 lbf")
sim.evaluate("tfinal = 60 s")
sim.evaluate("x = flatten(result_stage2[result_stage2.size()[1],:])")
sim.evaluate("result_unpowered = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt], x, dt, tfinal)")
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 900 s")
sim.evaluate("dt = 10 s")
sim.evaluate("x = flatten(result_stage2[end,:])")
sim.evaluate("result_unpowered1 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for final orbit insertion
sim.evaluate("dm = 270.8 kg/s")
sim.evaluate("tfinal = 39 s")
sim.evaluate("dt = 0.5 s")
sim.evaluate("x = flatten(result_unpowered1[end,:])")
sim.evaluate("result_insertion = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Reset initial conditions for unpowered flight
sim.evaluate("dm = 0 kg/s")
sim.evaluate("tfinal = 250 s")
sim.evaluate("dt = 10 s")
sim.evaluate("x = flatten(result_insertion[end,:])")
sim.evaluate("result_unpowered2 = ndsolve([drdt, dvdt, dmdt, dphidt, dgammadt, dtdt], x, dt, tfinal)")
// Extract the useful information from the results so it can be plotted
const data_stage1 = sim.evaluate("transpose(concat( transpose( result_stage1[:,4] - phi0) * r0 / rad / km, ( transpose(result_stage1[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_interstage = sim.evaluate("transpose(concat( transpose(result_interstage[:,4] - phi0) * r0 / rad / km, (transpose(result_interstage[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_stage2 = sim.evaluate("transpose(concat( transpose( result_stage2[:,4] - phi0) * r0 / rad / km, ( transpose(result_stage2[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
const data_unpowered = sim.evaluate("transpose(concat( transpose( result_unpowered[:,4] - phi0) * r0 / rad / km, ( transpose(result_unpowered[:,1]) - r0) / km, 1 ))").toArray().map(function(e) { return {x: e[0], y: e[1]} })
// Now it's time to prepare results for plotting
const resultNames = ['stage1', 'interstage', 'stage2', 'unpowered1', 'insertion', 'unpowered2']
.map(stageName => `result_${stageName}`)
window['chart'] = new Chart(document.getElementById('canvas1'), {
// Concat result matrices
sim.set('result',
math.concat(
...resultNames.map(resultName =>
sim.evaluate(`${resultName}[:end-1, :]`) // Avoid overlap
),
0 // Concat in row-dimension
)
)
const mainDatasets = resultNames.map((resultName, i) => ({
label: resultName.slice(7),
data: sim.evaluate(
'concat('
+ `(${resultName}[:,4] - phi0) * r0 / rad / km,` // Surface distance from start (in km)
+ `(${resultName}[:,1] - r0) / km` // Height above surface (in km)
+ ')'
).toArray().map(([x, y]) => ({ x, y })),
borderColor: i % 2 ? '#999' : '#dc3912',
fill: false,
pointRadius: 0,
}))
new Chart(document.getElementById('canvas'), {
type: 'line',
data: {
datasets: [{
label: "Stage 1",
data: data_stage1,
fill: false,
borderColor: "red",
pointRadius: 0
}, {
label: "Interstage",
data: data_interstage,
fill: false,
borderColor: "green",
pointRadius: 0
}, {
label: "Stage 2",
data: data_stage2,
fill: false,
borderColor: "orange",
pointRadius: 0
}, {
label: "Unpowered",
data: data_unpowered,
fill: false,
borderColor: "blue",
pointRadius: 0
}]
},
options: {
data: { datasets: mainDatasets },
options: getMainChartOptions()
})
createChart([{
label: 'velocity (in m/s)',
data: sim.evaluate("result[:,[2,6]]")
.toArray()
.map(([v, t]) => ({ x: t.toNumber('s'), y: v.toNumber('m/s') }))
}])
createChart([{
label: 'height (in km)',
data: sim.evaluate("concat((result[:, 1] - r0), result[:, 6])")
.toArray()
.map(([r, t]) => ({ x: t.toNumber('s'), y: r.toNumber('km') })),
}])
createChart([{
label: 'gamma (in deg)',
data: sim.evaluate("result[:, [5,6]]")
.toArray()
.map(([gamma, t]) => ({ x: t.toNumber('s'), y: gamma.toNumber('deg') })),
}])
createChart([{
label: 'acceleration (in m/s^2)',
data: sim.evaluate("concat(diff(result[:, 2]) ./ diff(result[:, 6]), result[:end-1, 6])")
.toArray()
.map(([acc, t]) => ({ x: t.toNumber('s'), y: acc.toNumber('m/s^2') })),
}])
createChart([{
label: 'drag acceleration (in m/s^2)',
data: sim.evaluate("concat(drag(result[:, 1], result[:, 2]) ./ result[:, 3], result[:, 6])")
.toArray()
.map(([dragAcc, t]) => ({ x: t.toNumber('s'), y: dragAcc.toNumber('m/s^2') })),
}])
createChart(
[
{
data: sim.evaluate("result[:, [1,4]]")
.toArray()
.map(([r, phi]) => math.rotate([r.toNumber('km'), 0], phi))
.map(([x, y]) => ({ x, y })),
},
{
data: sim.evaluate("map(0:0.25:360, function(angle) = rotate([r0/km, 0], angle))")
.toArray()
.map(([x, y]) => ({ x, y })),
borderColor: "#999",
fill: true
}
],
getEarthChartOptions()
)
// Helper functions for plotting data (nothing to learn about math.js from here on)
function createChart(datasets, options = {}) {
const container = document.createElement("div")
document.querySelector("#canvas-grid").appendChild(container)
const canvas = document.createElement("canvas")
container.appendChild(canvas)
new Chart(canvas, {
type: 'line',
data: {
datasets: datasets.map(dataset => ({
borderColor: "#dc3912",
fill: false,
pointRadius: 0,
...dataset
}))
},
options: getChartOptions(options)
})
}
function getMainChartOptions() {
return {
scales: {
xAxes: [{
type: 'linear',
position: 'bottom'
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'surface distance travelled (in km)'
}
}],
yAxes: [{
type: 'linear',
scaleLabel: {
display: true,
labelString: 'height above surface (in km)'
}
}]
}
},
animation: false
}
}
})
function getChartOptions(options) {
return {
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
scaleLabel: {
display: true,
labelString: 'time (in s)'
}
}]
},
animation: false,
...options
}
}
function getEarthChartOptions() {
return {
aspectRatio: 1,
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
min: -8000,
max: 8000,
display: false
}],
yAxes: [{
type: 'linear',
min: -8000,
max: 8000,
display: false
}]
},
legend: { display: false }
}
}
</script>
</body>
</html>
</html>
```
<!-- Note: This file is automatically generated. Changes made in this file will be overridden. -->

View File

@ -92,7 +92,7 @@ File: [webworkers.html](webworkers.html) (click for a live demo)
File: [worker.js](worker.js)
```js
importScripts('https://unpkg.com/mathjs@8.1.1/lib/browser/math.js')
importScripts('https://unpkg.com/mathjs@9.0.0/lib/browser/math.js')
// create a parser
const parser = self.math.parser()

View File

@ -1,4 +1,4 @@
importScripts('https://unpkg.com/mathjs@8.1.1/lib/browser/math.js')
importScripts('https://unpkg.com/mathjs@9.0.0/lib/browser/math.js')
// create a parser
const parser = self.math.parser()

View File

@ -4,6 +4,21 @@ layout: default
<h1 id="history">History <a href="#history" title="Permalink">#</a></h1>
<h1 id="20210116-version-900">2021-01-16, version 9.0.0 <a href="#20210116-version-900" title="Permalink">#</a></h1>
- Improved support for bin, hex, and oct literals. See <a href="https://github.com/josdejong/mathjs/issues/1996">#1996</a>. Thanks <a href="https://github.com/clnhlzmn">@clnhlzmn</a>.
- **Breaking change**: parse literals with prefixes `0b`, `0c`, and `0x` are
now unsigned by default. To parse them as signed, you have to specify a
suffix specifying the word size such as `i16` or `i32`.
- Function `format` now supports more notations: `bin`, 'hex', and `oct`,
for example `format(255, {notation: "hex"})`.
- The functions `format`, `bin`, `hex`, `oct` now allow specifying a wordSize,
like `bin(10, 32)` and `format(10, {notation: "bin", wordSize: 32})`.
- BigNumber support for the bin, hex, and oct literals.
- Extended and improved the example rocket_trajectory_optimization.html.
Thanks <a href="https://github.com/Josef37">@Josef37</a>.
<h1 id="20201230-version-811">2020-12-30, version 8.1.1 <a href="#20201230-version-811" title="Permalink">#</a></h1>
- Improved the performance of parsing and evaluating units a lot, see <a href="https://github.com/josdejong/mathjs/issues/2065">#2065</a>.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

12
package-lock.json generated
View File

@ -1902,9 +1902,9 @@
"dev": true
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
"interpret": {
@ -2319,9 +2319,9 @@
}
},
"mathjs": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-8.1.1.tgz",
"integrity": "sha512-b3TX3EgiZObujjwb8lZnTDLUuivC2jar4ZBjmGJ4stFYCDXx/DNwx5yry5t/z65p9mvejyZel1qoeR05KtChcQ==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.0.0.tgz",
"integrity": "sha512-ZfNz90wyed1MJUQOfL+Jr9IWG6vevVPM+cFf93sV568lbY8w2obEv5Ba7e7+Ylg5Zv0PTPtEY5CbqivHw/AxGQ==",
"requires": {
"complex.js": "^2.0.11",
"decimal.js": "^10.2.1",

View File

@ -6,7 +6,7 @@
"url": "https://github.com/josdejong/mathjs.git"
},
"dependencies": {
"mathjs": "8.1.1"
"mathjs": "9.0.0"
},
"devDependencies": {
"fancy-log": "1.3.3",