This commit is contained in:
Tom 2025-01-06 11:22:13 +00:00
parent 4296a98e9f
commit 76b2b417fd
33 changed files with 246 additions and 38 deletions

View File

@ -12,7 +12,7 @@
<meta property="og:type" content="website">
<!-- Theme tags -->
<meta name="theme-color" content="#e64c85">
<meta name="theme-color" content="#fcfcfc">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/apple-touch-icon.png">

View File

@ -0,0 +1,90 @@
---
title: Einstein summation is really nice.
layout: post
excerpt: Good mathematical notation is like a really good font.
mathjax: true
images: /assets/images/2024
image:
thumbnail:
assets:
alt:
---
Just a short thought. Lately I've been starting to read though these [lecture notes on astrophysical fluid dynamics][notes] and this morning I came across [this nice blogpost][blogpost] about numpy's `einsum` function. Both reminded how lovely einstein summation is as a mathematical notation.
Here's a quick intro to how it works and why it's cool.
So Einstein notation usually comes up when you're multiplying lots of high dimensional tensors together. Basically the idea is that you make summations implicit.
For example the definition of the product of two matrices $ A = BC $ can be written like this in terms of indices:
\\[ A_{ik} = \sum B_{ij} C_{jk} \\]
Here I've left off the sum limits because you usually know them from context or they can be defined later. For example here we could add that this equation only makes sense if A, B and C are of shapes `(m, l), (m, n) and (n, l)` respectively. So we know the sum should be over `n` indices.
Einstein summation notation is basically the statement "Whenever you see a pair of indices in an expression, pretend their is a sum over that index (over the appropriate range)". With this, the above equation becomes:
\\[ A_{ik} = B_{ij} C_{jk} \\]
You can find really good expositions of this online so I won't go into more basic detail here but here is a list of nice quality of life improvements you can add on top of this.
## Div, Grad, Curl
In Vector calculus you deal with a lot with partial derivatives $\tfrac{\partial}{\partial \alpha}$ where $\alpha$ can be x, y, z, or t. My first hack is that you should start writing your partial derivatives as $\partial_\alpha$ instead. Then div, grad and curl become quite succinct:
[Div](https://en.wikipedia.org/wiki/Divergence):
\\[ \nabla \cdot \vec{u} \rightarrow \partial_i A_i \\]
[Grad](https://en.wikipedia.org/wiki/Gradient):
\\[ \nabla \vec{u} \rightarrow \partial_i u_j \\]
[Curl](https://en.wikipedia.org/wiki/Curl_(mathematics)):
\\[ \nabla \times \vec{u} \rightarrow \epsilon_{ijk} \partial_j u_k \\]
Where these indices implicitly sum over x, y, and z.
That $\epsilon$ is the [Levi-Civita symbol](https://en.wikipedia.org/wiki/Levi-Civita_symbol).
## Spacetime
In relativity, space and time get put on a more equal footing. They're not the same exactly but it makes sense to start to let our indices run over both space and time. Though there are occasions where we would like to just run over space too. The first hack for this is we say that indices from the Greek alphabet should be read as running over t, x, y, and z while those from the Latin alphabet run over just the spatial indices as before.
This lets us take something like a continuity equation
\\[\partial_t \rho + \nabla \cdot \vec{u} = 0 \\]
which expresses the idea of conservation of 'stuff' if $\rho$ is a density field of stuff and $\vec{u}$ is its velocity field, and transform it to:
\\[ \partial_\mu v_\mu = 0 \\]
where $v = (\rho, \vec{u})$ is called a 4-vector and nicely packs up the very closely related concepts of $\rho$ i.e "how much stuff is right here" and $\vec{u}$ i.e "where is the stuff here flowing to?".
You can do similar tricks for many physical quantities such as [charge and current](https://en.wikipedia.org/wiki/Four-current), [the magnetic field and the electric field](https://en.wikipedia.org/wiki/Electromagnetic_tensor), [energy and momentum](https://en.wikipedia.org/wiki/Four-momentum) etc.
## Flat and curved spacetime
This next bit starts to touch on things beyond my ken but I'll gesture at it anyway.
When you start to talk about curved space and even spacetime it becomes useful to define a special two dimensional tensor called `the metric`. In flat spacetime the metric is called 'the Minkowski metric' and it's a diagonal matrix $\eta_{\mu\nu}$ with (1, -1, -1, -1) along the diagonal. It could also be (-1, 1, 1, 1) if you like.
Now for flat spacetime the metric really just helps us keep track of signs. The simplest place it crops up is that the 'spacetime interval' between two events in space and time is:
\\[ \delta s^2 = \delta t^2 - \delta x^2 - \delta y^2 - \delta z^2 = \delta_\mu \eta_{\mu\nu} \delta_\nu \\]
or
\\[ \delta s^2 = \vec{\delta} \cdot \eta \cdot \vec{\delta} \\]
For curved spacetimes, the metric gets more complicated and can have arbitrary off diagonal terms too which describe the curvature of spacetime and other effects.
The final trick is that this insertion of the metric in the middle of tensor contractions comes up so much that we can define a new notation just for it, we say that when you contract a superscript index with a subscript index, you have to insert the metric in between:
\\[\delta^\mu \delta_\nu = \delta_\mu \eta_{\mu\nu} \delta_\nu \\]
Now I've called these things hacks and tricks but they also connect to much deeper mathematical concepts such as [covariance and contravariance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors). This seems like it's usually the case with nice notation to be honest. It makes me things of the relationship between the derivative operator $\tfrac{d}{dt}$ and and the infinitesimal $dt$.
[notes]: https://arxiv.org/abs/1604.03835
[blogpost]: https://einsum.joelburget.com/

View File

@ -0,0 +1,35 @@
---
title:
layout: post
excerpt: A one sentence summary.
images: /assets/images/2024
image:
thumbnail:
assets:
alt:
---
## Subtitle
The first big project of the year was repainting this ladder up to our mezzanine bed. This ended up being so much more work than we expected, they say it's all in the surface prep and the surface prep here took ages with all the awkward corners.
There was one aspect that was fun with this which was that I made non-slip pads on the rungs by mixing the gloss paint with sand and painting over masked rectangle.
<figure>
<img src="{{page.images}}/train_view.jpeg">
<figcaption>Crossing the alps by train provided some good views and also some good gaming time on my then new Steam Deck.</figcaption>
</figure>
<figure class="two-wide">
<img src="{{page.images}}/ladder/late_into_the_night.jpeg">
<img src="{{page.images}}/ladder/finished.jpeg">
</figure>
<figure class="multiple">
<img src="{{page.images}}/chop_saw_dust_collector/print_preview.jpeg">
<img src="{{page.images}}/chop_saw_dust_collector/printing.jpeg">
<img src="{{page.images}}/chop_saw_dust_collector/installed.jpeg">
</figure>

View File

@ -6,6 +6,8 @@ permalink: /projects/ceramics
assets: /assets/projects/ceramics
models: /assets/projects/ceramics/models
img2024: /assets/images/2024/ceramics
img:
alt:
class: invertable
@ -14,7 +16,13 @@ social_image: /assets/projects/ceramics/thumbnail.png
model: /assets/projects/ceramics/pots/pots.glb
---
## Pots and stuff
## Pots
<figure class="two-wide">
<img src="{{page.img2024}}/bowls.jpeg">
<img src="{{page.img2024}}/finished_pots.jpeg">
<figcaption>Frilly plate complete with Camembert, though now we use it as a plate to rest spoons and spatulas while cooking.</figcaption>
</figure>
<figure>
<outline-model-viewer model = "{{page.models}}/pots/pots.glb" materials=keep mode=1 camera='{"position":[-5.155,2.5,-9.456],"rotation":[-2.883,-0.4851,-3.019],"zoom":268,"target":[0,0,0]}' ambient-light="6" directional-light="0.8">
@ -24,6 +32,24 @@ model: /assets/projects/ceramics/pots/pots.glb
<figcaption>A 3D scan of some pots.</figcaption>
</figure>
## Frilly Plate
This is one of my more successful ones in that I both like how it looks and we have a good use for it. The only thing I would do differently is apply the glaze a little thinner, I applied it quite thick and it has given an incredible homogeneous surface finish, almost to the point that in real life it looks like someone printed a fake texture onto it. I would rather have the glaze show a bit more texture and variation. I particularly like the effects you get at the edges of the glaze on a piece.
<figure class="two-wide">
<img src="{{page.img2024}}/frilly_plate_unglazed.jpeg">
<img src="{{page.img2024}}/frilly_plate_finished.jpeg">
<figcaption>Frilly plate complete with Camembert, though now we use it as a plate to rest spoons and spatulas while cooking.</figcaption>
</figure>
## Two Tone Cup
I also like how this one came out, it's the result of double dipping glazes.
<figure class="two-wide">
<img src="{{page.img2024}}/two_tone_cup.jpeg">
</figure>
## Soap Dispenser Tray
This was a bit of an experiment to see if I could make a kind of press mould by 3D printing it in two halves. You can see one half of the mold in the model below. Along with the soap dispensers that I was making it for.

View File

@ -21,9 +21,6 @@ head: |
---
<!-- {% include mastodon_post.html post_id = "111813225328398667" %}
{% include mastodon_post.html post_id = "111816310882560850" %} -->
I'm kinda fascinated by USB-C. It has it's issues but I can't help but love this magical omni-cable that holds the promise of hundreds of watts of power and gigagbits per second of bandwidth. It's reversible, you can negotiate the supply voltage (PD), send power in either direction, pipe PCIe or DisplayPort and talk to the cable itself.
@ -63,8 +60,9 @@ After initially thinking I would do some kind of charging tray type design I eve
This is what I've come up with so far, it's lasercut from 3mm ply (but I need to switch to 4mm because 3mm is a bit flimsy)
<figure style="max-width: 250px;">
<figure class="two-wide">
<img src="{{page.img.src}}">
<img src="/assets/images/2024/usbc_psu/case_test.jpeg">
</figure>
I've put a 240x240 pixel colour screen on the front to show metrics like total charge power, temperature and maybe daily energy use.
@ -87,21 +85,41 @@ There's an INA219 and a shunt resistor for current and voltage monitoring and a
For now I've broken the functionality for one channel out into a test board that I've sent off to JLCPB for manufacturing with and to be populated with SMT components. This ended up costing about 50 dollars for 5 boards. In future I want to have a go at doing the component placement and reflow myself.
<outline-model-viewer model = "{{page.assets}}/test_board.glb" true-color=true spin=true camera='{"position":[4.016,7.557,6.841],"rotation":[-0.8351,0.3753,0.3848],"zoom":241.86567243589988,"target":[0,0,0]}'>
<outline-model-viewer model = "{{page.assets}}/test_board.glb" materials=flat spin=true camera='{"position":[4.016,7.557,6.841],"rotation":[-0.8351,0.3753,0.3848],"zoom":241.86567243589988,"target":[0,0,0]}' ambient-light="5" directional-light="5">
<img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p>
</outline-model-viewer>
And here's the board as it arrived in the post.
<figure class="two-wide">
<img src="/assets/images/2024/usbc_psu/pcb_top.jpeg">
<img src="/assets/images/2024/usbc_psu/pcb_bottom.jpeg">
</figure>
At this point I realised I had ordered 2.54mm connectors instead of 2mm pitch connectors which I had swapped in at the last minute to reclaim some board space.
<figure>
<img src="/assets/images/2024/usbc_psu/soldered_up.jpeg">
</figure>
When those eventually arrived I soldered it up and attached it to the downstream USB-C power board that it will be monitoring. 24V goes into the terminal block on the right. I2C for current monitoring and a logic level enable pin go into the 5 pin connector in the bottom right.
I haven't had a chance to properly test this yet. Particularly, I'm anxious to find out how much power the MOSFET dissipates when its gate is driven at 3.3V and the downstream load is pulling 5A. If it's too much I'll stick in a level shifter and hope that driving it to 5V will be enough!
## Software
In other posts I've described how I made this simulator the test out possible GUIs for this thing.
In other posts I've described how I made this simulator the test out possible GUIs for this thing. This is the micropython code running in a slightly tweaked version of the micropython interpreter compiled to webassembly.
<!-- <usbc-power-supply-simulator disable-console disable-editor code="/assets/blog/micropython/demo.py"></usbc-power-supply-simulator> -->
<figure style="max-width: 250px;">
<img src="/assets/images/2024/bike_display/simulation.png">
<figcaption>I've replaced the live simulation with an image for now because it blocks the main JS thread, including the three JS animations, and I haven't found a fix for that yet.</figcaption>
</figure>
TODO: Add some knobs to the simulator so you can test different conditions such as overcurrent, overtemp, sleep, nightmode etc.
<!-- <usbc-power-supply-simulator disable-console disable-editor code="/assets/blog/micropython/demo.py"></usbc-power-supply-simulator>
-->
<!-- <outline-model-viewer model = "{{page.model}}" true-color=true spin=false camera='{"position":[-6.425,8.003,-3.751],"rotation":[-2.016,-0.6378,-2.246],"zoom":6784.844370099355,"target":[0.1581,-0.01497,0.07167]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p>

View File

@ -207,6 +207,7 @@ figure {
padding-bottom: 1em;
margin-left: auto;
margin-right: auto;
margin-bottom: 1em;
figcaption {
margin-top: 1em;
@ -218,7 +219,6 @@ figure > img,
figure > svg,
figure > canvas {
width: 100%;
margin-bottom: 1em;
border-radius: 10px;
}
@ -233,25 +233,37 @@ figure.two-wide {
justify-content: center;
gap: 1em;
margin-bottom: 1em;
img {
* {
width: calc(50% - 0.5em);
}
}
section.image-grid-4x4 {
figure.multiple {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1em;
margin-bottom: 1em;
* {
width: calc(50% - 0.5em);
margin: 0;
padding: 0;
}
}
.image-grid-4x4 {
aspect-ratio: 1 / 1;
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
gap: 2px;
margin-bottom: 1em;
place-items: stretch stretch;
place-items: center center;
* {
margin: 0;
padding: 0;
width: 100%;
aspect-ratio: auto;
}
}

View File

@ -7,8 +7,9 @@
}
.toggle-button {
aspect-ratio: 1 / 1;
width: 1.5rem;
height: 1.5rem;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@ -143,7 +143,7 @@ function getVertexShader() {
void main() {
v_uv = uv;
vColor = color;
vColor = vec4(color.rgb, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

View File

@ -9,6 +9,7 @@ import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
import { Timer } from "three/addons/Addons.js";
import GUI from "lil-gui";
import { CustomOutlinePass } from "./CustomOutlinePass.js";
@ -97,9 +98,7 @@ export class OutlineModelViewer extends HTMLElement {
this.render(component_rect.height);
const model_path =
this.getAttribute("model") ||
"/assets/projects/bike_lights/models/bigger.glb";
const model_path = this.getAttribute("model");
const spin = (this.getAttribute("spin") || "true") === "true";
const container = this.shadow.querySelector("div#container");
@ -224,21 +223,39 @@ export class OutlineModelViewer extends HTMLElement {
new THREE.BufferAttribute(colorsTypedArray, 4)
);
// Hack specific to kicad models to make the tracks and zones look good
if (node.name.includes("track") || node.name.includes("zone")) {
//set to a copper colour
// #c87533
material_params = {
node.material = new THREE.MeshStandardMaterial({
color: new THREE.Color(0x558855),
};
});
node.position.y += 0.00001;
}
// Hack specific to kicad models to make the tracks and zones look good
if (node.name.includes("pad")) {
material_params = {
node.material = new THREE.MeshStandardMaterial({
color: new THREE.Color(0xaaaaaa),
};
});
node.position.y += 0.00002;
}
// override materials
if (node.name.includes("PCB")) {
node.material = new THREE.MeshStandardMaterial({
color: new THREE.Color(0x446644),
});
}
// override materials for different purposes
// materials = outlines
// sets the material to be emissive to the background colour of the page
// This makes for nice two colour rendering with no shading
// material = flat overides all the materials to just be flat with the base colour
// material = keep uses whatever material is defined in the gltf
const material_mode = this.getAttribute("materials") || "outlines";
if (material_mode === "outlines") {
node.material = new THREE.MeshStandardMaterial({
@ -251,7 +268,9 @@ export class OutlineModelViewer extends HTMLElement {
} else if (material_mode === "keep") {
// Do nothing, leave the material as set in the GLTF file
} else {
throw new Error("Invalid material mode");
throw new Error(
"Invalid material mode, should be outlines, flat or keep."
);
}
}
});
@ -282,7 +301,7 @@ export class OutlineModelViewer extends HTMLElement {
let intersects = [];
const doRayCast = () => {
// Perform raycasting for hovering
// Perform raycasting for a click
this.raycaster.setFromCamera(this.mouse, camera);
intersects.length = 0;
@ -300,20 +319,27 @@ export class OutlineModelViewer extends HTMLElement {
);
}
shadow.querySelector(
this.shadow.querySelector(
"#clicked-item"
).innerText = `${object.name} - ${object.type}`;
).innerText = `${object.name}`;
}
} else if (this.intersectedObject) {
this.intersectedObject = null;
params.selectedObject = "";
}
if (intersects.length === 0) {
this.shadow.querySelector("#clicked-item").innerText = "";
}
};
window.addEventListener("click", doRayCast);
// Render loop
const timer = new Timer();
const update = () => {
if (this.isVisible) {
timer.update();
const delta = timer.getDelta();
// this.shadow.querySelector("#clicked-item").innerText = `${1 / delta}`;
requestAnimationFrame(update);
controls.update();
composer.render();
@ -393,13 +419,13 @@ export class OutlineModelViewer extends HTMLElement {
gui
.add(params.mode, "Mode", {
"Outlines + Shaded (default)": 0,
"Just Outlines": 5,
"Only outer outlines + shading": 1,
Shaded: 2,
"Depth buffer": 3,
"SurfaceID buffer": 4,
Outlines: 5,
"Depth Difference": 6,
"SurfaceID Difference": 7,
"Only shading": 2,
"(Debug) SurfaceID buffer": 4,
"(Debug) Depth buffer": 3,
"(Debug) Depth Difference (external edges / outline)": 6,
"(Debug) SurfaceID Difference (internal edges)": 7,
})
.onChange(function (value) {
uniforms.debugVisualize.value = value;