README: clean up the pieces which refer to examples

This commit is contained in:
2022-05-04 00:05:30 -07:00
parent c852cb52e0
commit a2dc3c7c8f

158
README.md
View File

@@ -16,52 +16,122 @@ as the simulation progresses or completes.
simulations are defined by creating either a binary crate which links against the `coremem` library, simulations are defined by creating either a binary crate which links against the `coremem` library,
or simply inserting a top-level file to the `examples/` directory in this repo and running it. or simply inserting a top-level file to the `examples/` directory in this repo and running it.
here's an excerpt from the [sr_latch](examples/sr_latch.rs) example shipped in this library: here's an excerpt from the [wavefront](examples/wavefront.rs) example shipped in this library:
```rust ```rust
// create some `Region` instances which will define our geometry // Create the simulation "driver" which uses the CPU as backend.
let ferro1_region = Torus::new_xy(Meters::new(ferro1_center, ferro_center_y, half_depth), ferro_major, ferro_minor); let mut driver: driver::CpuDriver = driver::Driver::new(size, feature_size);
let ferro2_region = Torus::new_xy(Meters::new(ferro2_center, ferro_center_y, half_depth), ferro_major, ferro_minor);
let set_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y - ferro_major, half_depth), wire_major, wire_minor);
let reset_region = Torus::new_yz(Meters::new(ferro1_center, ferro_center_y + ferro_major, half_depth), wire_major, wire_minor);
let coupling_region = Torus::new_xz(Meters::new(0.5*(ferro1_center + ferro2_center), ferro_center_y, half_depth), wire_coupling_major, wire_minor);
// create the interface through which we'll "drive" a simulation to completion: // create a conductor on the left side.
let mut driver: SpirvDriver<spirv::FullyGenericMaterial> = Driver::new_spirv(Meters::new(width, height, depth), feat_size); let conductor = Cube::new(
Index::new(0, 0, 0).to_meters(feature_size),
Index::new(width/10, height, 1).to_meters(feature_size),
);
driver.fill_region(&conductor, mat::IsomorphicConductor::new(200f32));
// fill each region within the simulation with the appropriate material // create a vertical strip in the center of the simulation which emits a wave.
driver.fill_region(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0)); let center_region = Cube::new(
driver.fill_region(&ferro2_region, mat::MHPgram::new(25.0, 881.33, 44000.0)); Index::new(200, height/4, 0).to_meters(feature_size),
driver.fill_region(&set_region, mat::IsomorphicConductor::new(drive_conductivity)); Index::new(201, height*3/4, 1).to_meters(feature_size),
driver.fill_region(&reset_region, mat::IsomorphicConductor::new(drive_conductivity)); );
driver.fill_region(&coupling_region, mat::IsomorphicConductor::new(drive_conductivity)); // emit a constant E/H delta over this region for 100 femtoseconds
let stim = Stimulus::new(
center_region,
UniformStimulus::new(
Vec3::new(2e19, 0.0, 0.0), // E field (per second)
Vec3::new(0.0, 0.0, 2e19/376.730) // H field (per second)
).gated(0.0, 100e-15),
);
driver.add_stimulus(stim);
// populate our stimuli. // finally, run the simulation:
// here we pulse the E field with amplitude defined by a half-sine wave w.r.t. time. driver.step_until(Seconds(100e-12));
// we apply this to the "set" wire loop, everywhere directed tangential to the loop.
let wave = Sinusoid1::from_wavelength(peak_set_field, set_pulse_duration * 2.0)
.half_cycle()
.shifted(start);
driver.add_stimulus(CurlStimulus::new(
set_region.clone(),
wave,
set_region.center(),
set_region.axis()
));
// every 36000 "steps", render the simulation state to disk.
driver.add_serializer_renderer("out/examples/sr_latch/frame-", 36000, None);
driver.step_until(Seconds(3.0*set_pulse_duration));
``` ```
you can run the full example with: you can run the full example with:
```
$ cargo run --release --example wavefront
```
graphical simulation output is displayed directly to the terminal, and also rendered (in higher resolution) to
a video file in `out/examples/wavefront/`.
this example runs on the CPU. the moment you do anything more complex than this, you'll want to leverage the GPU,
which can easily be 40-100x faster:
## GPU Acceleration
we use rust-gpu for gpu acceleration. presently, this requires *specific* versions of rust-nightly to work:
```
$ rustup default nightly-2022-01-13
$ rustup component add rust-src rustc-dev llvm-tools-preview
```
(it's possible to work with older nightlies like `nightly-2021-06-08` if you enable the 2020 feature.)
now you can swap out the `CpuDriver` with a `SpirvDriver` and you're set:
```diff
- let mut driver: driver::CpuDriver = driver::Driver::new(size, feature_size);
+ let mut driver: driver::SpirvDriver = driver::Driver::new_spirv(size, feature_size);
```
re-run it as before and you should see the same results:
```
$ cargo run --release --example wavefront
```
see the "Processing Loop" section below to understand what GPU acceleration entails.
## Advanced Usage: Viewers, Measurements
the [sr\_latch](examples/sr_latch.rs) example explores a more interesting feature set.
first, it "measures" a bunch of parameters over different regions of the simulation
(peak inside `src/meas.rs` to see how these each work):
```rust
// measure a bunch of items of interest throughout the whole simulation duration:
driver.add_measurement(meas::CurrentLoop::new("coupling", coupling_region.clone()));
driver.add_measurement(meas::Current::new("coupling", coupling_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("sense", sense_region.clone()));
driver.add_measurement(meas::Current::new("sense", sense_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::Magnetization::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticFlux::new("mem1", ferro1_region.clone()));
driver.add_measurement(meas::MagneticLoop::new("mem2", ferro2_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("set", set_region.clone()));
driver.add_measurement(meas::Current::new("set", set_region.clone()));
driver.add_measurement(meas::Power::new("set", set_region.clone()));
driver.add_measurement(meas::CurrentLoop::new("reset", reset_region.clone()));
```
during the simulation, it dumps these measurements into a CSV:
```rust
// render a couple CSV files: one very detailed and the other more sparsely detailed
driver.add_csv_renderer(&*format!("{}meas.csv", prefix), 200, None);
driver.add_csv_renderer(&*format!("{}meas-sparse.csv", prefix), 1600, None);
```
furthermore, it serializes point-in-time snapshots of the simulation while it's running,
allowing you to dig further into the simulation in an _interactive_ way (versus the raw video
renderer used in the `wavefront` example):
```rust
// serialize frames for later viewing with `cargo run --release --bin viewer`
driver.add_serializer_renderer(&*format!("{}frame-", prefix), 36000, None);
```
run this, after having setup the GPU pre-requisites:
``` ```
$ cargo run --release --example sr_latch $ cargo run --release --example sr_latch
``` ```
TODO: switch between CPU and GPU accel in this demo.
and then investigate the results with and then investigate the results with
``` ```
$ cargo run --bin viewer out/examples/sr_latch $ cargo run --bin viewer out/examples/sr_latch
``` ```
@@ -85,7 +155,7 @@ here's a plot of `M(mem2)` over time from the SR latch simulation. we're measuri
![plot of M(mem2) over time](readme_images/sr_latch_vd_M2.png "plot of M(mem2) over time") ![plot of M(mem2) over time](readme_images/sr_latch_vd_M2.png "plot of M(mem2) over time")
## Processing Loop ## Processing Loop (and how GPU acceleration works)
the processing loop for a simulation is roughly as follows (src/driver.rs:step_until drives this loop): the processing loop for a simulation is roughly as follows (src/driver.rs:step_until drives this loop):
1. evaluate all stimuli at the present moment in time; these produce an "externally applied" E and B field 1. evaluate all stimuli at the present moment in time; these produce an "externally applied" E and B field
@@ -104,20 +174,6 @@ it turns out that the Courant rules force us to evaluate FDTD updates (step 2) o
as a result, step 2 is actually able to apply the FDTD update functions not just once but up to `min(N, M, Z)` times. as a result, step 2 is actually able to apply the FDTD update functions not just once but up to `min(N, M, Z)` times.
although steps 1 and 3 vary heavily based on the user configuration of the simulation, step 2 can be defined pretty narrowly in code (no user-callbacks/dynamic function calls/etc). this lets us offload the processing of step 2 to a dedicated GPU. by tuning N/M/Z, step 2 becomes the dominant cost in our simulations an GPU offloading can trivially boost performance by more than an order of magnitude on even a mid-range consumer GPU. although steps 1 and 3 vary heavily based on the user configuration of the simulation, step 2 can be defined pretty narrowly in code (no user-callbacks/dynamic function calls/etc). this lets us offload the processing of step 2 to a dedicated GPU. by tuning N/M/Z, step 2 becomes the dominant cost in our simulations an GPU offloading can trivially boost performance by more than an order of magnitude on even a mid-range consumer GPU.
## GPU Acceleration
we use rust-gpu for gpu acceleration. presently, this requires *specific* versions of rust-nightly to work:
```
$ rustup default nightly-2022-01-13
$ rustup component add rust-src rustc-dev llvm-tools-preview
```
(it's possible to work with older nightlies like `nightly-2021-06-08` if you enable the 2020 feature.)
TODO: show how to enable gpu accel.
# Features # Features
TODO: document Material options, Stimulus options, Measurement options, Rendering options. TODO: document Material options, Stimulus options, Measurement options, Rendering options.
@@ -133,18 +189,18 @@ this is the "default" optimized version. you could introduce a new material to t
contrast that to the CPU-only implementation which achieves 24.6M grid cell steps per second: that's about a 34x gain. contrast that to the CPU-only implementation which achieves 24.6M grid cell steps per second: that's about a 34x gain.
# Support # Support
the author can be reached on Matrix <@colin:uninsane.org> or Activity Pub <@colin@fed.uninsane.org>. i poured a lot of time into making the author can be reached on Matrix <@colin:uninsane.org> or Activity Pub <@colin@fed.uninsane.org>. i poured a lot of time into making
this: i'm happy to spend the marginal extra time to help curious people make use of what i've made, so don't hesitate to reach out. this: i'm happy to spend the marginal extra time to help curious people make use of what i've made, so don't hesitate to reach out.
## Additional Resources ## Additional Resources
TODO: cite the works which were useful in getting this off the ground. TODO: cite the works which were useful in getting this off the ground.
TODO: consult the licenses of my dependencies. ## License
# License
i'm not a lawyer, and i don't want to be. i'm not a lawyer, and i don't want to be.
by nature of your reading this, my computer has freely shared these bits with yours. by nature of your reading this, my computer has freely shared these bits with yours.