Add step_1 of navier stokes

This commit is contained in:
Alex Selimov 2026-03-16 14:30:02 -04:00
parent 0848e7afe6
commit 17ee54abd4
8 changed files with 466 additions and 2 deletions

View file

@ -2,6 +2,201 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "clap"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "poloto"
version = "19.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164dbd541c9832e92fa34452e9c2e98b515a548a3f8549fb2402fe1cd5e46b96"
dependencies = [
"tagu",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "steps" name = "steps"
version = "0.1.0" version = "0.1.0"
dependencies = [
"clap",
"poloto",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tagu"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddb6b06d20fba9ed21fca3d696ee1b6e870bca0bcf9fa2971f6ae2436de576a"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]

View file

@ -4,3 +4,5 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
clap = { version = "4.6.0", features = ["derive"] }
poloto = "19.1.2"

View file

@ -0,0 +1,97 @@
<svg class="poloto" width="800" height="500" viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
<style>
.poloto{
stroke-linecap:round;
stroke-linejoin:round;
font-family:Roboto,sans-serif;
font-size:16px;
}
.poloto_background{fill:AliceBlue;}
.poloto_scatter{stroke-width:7}
.poloto_line{stroke-width:2}
.poloto_text{fill: black;}
.poloto_name{font-size:24px;dominant-baseline:auto;text-anchor:middle;}
.poloto_where{dominant-baseline:middle;text-anchor:start}
.poloto_text.poloto_legend{font-size:20px;dominant-baseline:middle;text-anchor:start;}
.poloto_text.poloto_ticks.poloto_y{dominant-baseline:middle;text-anchor:end}
.poloto_text.poloto_ticks.poloto_x{dominant-baseline:auto;text-anchor:middle}
.poloto_imgs.poloto_ticks{stroke: black;stroke-width:3;fill:none;stroke-dasharray:none}
.poloto_grid{stroke:gray;stroke-width:0.5}
.poloto0.poloto_stroke{stroke:blue;}
.poloto1.poloto_stroke{stroke:red;}
.poloto2.poloto_stroke{stroke:green;}
.poloto3.poloto_stroke{stroke:gold;}
.poloto4.poloto_stroke{stroke:aqua;}
.poloto5.poloto_stroke{stroke:lime;}
.poloto6.poloto_stroke{stroke:orange;}
.poloto7.poloto_stroke{stroke:chocolate;}
.poloto0.poloto_fill{fill:blue;}
.poloto1.poloto_fill{fill:red;}
.poloto2.poloto_fill{fill:green;}
.poloto3.poloto_fill{fill:gold;}
.poloto4.poloto_fill{fill:aqua;}
.poloto5.poloto_fill{fill:lime;}
.poloto6.poloto_fill{fill:orange;}
.poloto7.poloto_fill{fill:chocolate;}
</style>
<circle r="1e5" class="poloto_background" fill="white"/>
<g id="poloto_plot0" class="poloto_plot poloto_imgs poloto_line poloto0 poloto_stroke" fill="none" stroke="black">
<path d=" M 150.00 400.00 L 162.50 400.00 L 175.00 400.00 L 187.50 400.00 L 200.00 400.00 L 212.50 400.00 L 225.00 400.00 L 237.50 400.00 L 250.00 400.00 L 262.50 400.00 L 275.00 100.00 L 287.50 100.00 L 300.00 100.00 L 312.50 100.00 L 325.00 100.00 L 337.50 100.00 L 350.00 100.00 L 362.50 100.00 L 375.00 100.00 L 387.50 100.00 L 400.00 100.00 L 412.50 400.00 L 425.00 400.00 L 437.50 400.00 L 450.00 400.00 L 462.50 400.00 L 475.00 400.00 L 487.50 400.00 L 500.00 400.00 L 512.50 400.00 L 525.00 400.00 L 537.50 400.00 L 550.00 400.00 L 562.50 400.00 L 575.00 400.00 L 587.50 400.00 L 600.00 400.00 L 612.50 400.00 L 625.00 400.00 L 637.50 400.00 L 650.00 400.00"/>
</g>
<g id="poloto_plot1" class="poloto_plot poloto_imgs poloto_line poloto1 poloto_stroke" fill="none" stroke="black">
<path d=" M 150.00 400.00 L 162.50 400.00 L 175.00 400.00 L 187.50 400.00 L 200.00 400.00 L 212.50 400.00 L 225.00 400.00 L 237.50 400.00 L 250.00 400.00 L 262.50 400.00 L 275.00 399.93 L 287.50 399.05 L 300.00 394.21 L 312.50 378.10 L 325.00 341.85 L 337.50 283.84 L 350.00 216.16 L 362.50 158.15 L 375.00 121.90 L 387.50 105.79 L 400.00 100.95 L 412.50 100.15 L 425.00 100.95 L 437.50 105.79 L 450.00 121.90 L 462.50 158.15 L 475.00 216.16 L 487.50 283.84 L 500.00 341.85 L 512.50 378.10 L 525.00 394.21 L 537.50 399.05 L 550.00 399.93 L 562.50 400.00 L 575.00 400.00 L 587.50 400.00 L 600.00 400.00 L 612.50 400.00 L 625.00 400.00 L 637.50 400.00 L 650.00 400.00"/>
</g>
<g id="poloto_plot2" class="poloto_plot poloto_imgs poloto_line poloto2 poloto_stroke" fill="none" stroke="black">
<path d=" M 150.00 400.00 L 162.50 400.00 L 175.00 400.00 L 187.50 400.00 L 200.00 400.00 L 212.50 400.00 L 225.00 400.00 L 237.50 400.00 L 250.00 400.00 L 262.50 400.00 L 275.00 400.00 L 287.50 400.00 L 300.00 400.00 L 312.50 399.98 L 325.00 399.86 L 337.50 399.39 L 350.00 397.81 L 362.50 393.51 L 375.00 383.84 L 387.50 365.57 L 400.00 336.35 L 412.50 296.49 L 425.00 250.00 L 437.50 203.51 L 450.00 163.68 L 462.50 134.57 L 475.00 116.77 L 487.50 108.69 L 500.00 108.69 L 512.50 116.77 L 525.00 134.57 L 537.50 163.68 L 550.00 203.51 L 562.50 250.00 L 575.00 296.49 L 587.50 336.35 L 600.00 365.57 L 612.50 383.84 L 625.00 393.51 L 637.50 397.81 L 650.00 399.39"/>
</g>
<g class="poloto_legend poloto_imgs poloto_line poloto0 poloto_stroke"><line x1="680" x2="730" y1="81.25" y2="81.25"/></g>
<g class="poloto_legend poloto_imgs poloto_line poloto1 poloto_stroke"><line x1="680" x2="730" y1="131.25" y2="131.25"/></g>
<g class="poloto_legend poloto_imgs poloto_line poloto2 poloto_stroke"><line x1="680" x2="730" y1="181.25" y2="181.25"/></g>
<text class="poloto_legend poloto_text poloto_line poloto0" x="675" y="100">i=0</text>
<text class="poloto_legend poloto_text poloto_line poloto1" x="675" y="150">i=12</text>
<text class="poloto_legend poloto_text poloto_line poloto2" x="675" y="200">i=25</text>
<text class="poloto_text poloto_name poloto_title" x="400.00" y="37.50">Data Comparison</text>
<text class="poloto_text poloto_name poloto_x" x="400.00" y="481.25">Index</text>
<text class="poloto_text poloto_name poloto_y" x="37.50" y="250.00" transform="rotate(-90,37.50,250.00)">Value</text>
<text class="poloto_text poloto_ticks poloto_y">
<tspan x="135.00" y="400.00">1.0</tspan>
<tspan x="135.00" y="340.00">1.2</tspan>
<tspan x="135.00" y="280.00">1.4</tspan>
<tspan x="135.00" y="220.00">1.6</tspan>
<tspan x="135.00" y="160.00">1.8</tspan>
<tspan x="135.00" y="100.00">2.0</tspan>
</text>
<g class="poloto_imgs poloto_ticks poloto_y" stroke="black">
<line x1="150.00" x2="144.00" y1="400.00" y2="400.00"/>
<line x1="150.00" x2="144.00" y1="340.00" y2="340.00"/>
<line x1="150.00" x2="144.00" y1="280.00" y2="280.00"/>
<line x1="150.00" x2="144.00" y1="220.00" y2="220.00"/>
<line x1="150.00" x2="144.00" y1="160.00" y2="160.00"/>
<line x1="150.00" x2="144.00" y1="100.00" y2="100.00"/>
</g>
<text class="poloto_text poloto_ticks poloto_x">
<tspan x="150.00" y="430.00">0</tspan>
<tspan x="212.50" y="430.00">5</tspan>
<tspan x="275.00" y="430.00">10</tspan>
<tspan x="337.50" y="430.00">15</tspan>
<tspan x="400.00" y="430.00">20</tspan>
<tspan x="462.50" y="430.00">25</tspan>
<tspan x="525.00" y="430.00">30</tspan>
<tspan x="587.50" y="430.00">35</tspan>
<tspan x="650.00" y="430.00">40</tspan>
</text>
<g class="poloto_imgs poloto_ticks poloto_x" stroke="black">
<line x1="150.00" x2="150.00" y1="400.00" y2="405.00"/>
<line x1="212.50" x2="212.50" y1="400.00" y2="405.00"/>
<line x1="275.00" x2="275.00" y1="400.00" y2="405.00"/>
<line x1="337.50" x2="337.50" y1="400.00" y2="405.00"/>
<line x1="400.00" x2="400.00" y1="400.00" y2="405.00"/>
<line x1="462.50" x2="462.50" y1="400.00" y2="405.00"/>
<line x1="525.00" x2="525.00" y1="400.00" y2="405.00"/>
<line x1="587.50" x2="587.50" y1="400.00" y2="405.00"/>
<line x1="650.00" x2="650.00" y1="400.00" y2="405.00"/>
</g>
<path class="poloto_imgs poloto_ticks poloto_x" stroke="black" style="stroke-dasharray:6.25;stroke-dashoffset:-0;" d=" M 150.00 400.00 L 650.00 400.00"/>
<path class="poloto_imgs poloto_ticks poloto_y" stroke="black" style="stroke-dasharray:7.5;stroke-dashoffset:-0;" d=" M 150.00 400.00 L 150.00 100.00"/>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,35 @@
use crate::{
step_1::{Step1Config, one_dim_solver},
utils::plot_vectors,
};
mod step_1;
mod utils;
pub fn run_example_step1() {
let u0 = vec![
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0,
2.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0, 1.0,
];
let nx = 41.0;
let dx = 2.0 / (nx - 1.0);
let dt = 0.025;
let c = 1.0;
let config = Step1Config {
c,
dt,
dx,
u0,
timesteps: 25,
};
let uss = one_dim_solver(config);
plot_vectors(
&[uss[0].clone(), uss[12].clone(), uss[25].clone()],
Some(&["i=0", "i=12", "i=25"]),
"/Users/aselimov/repos/cfd_playground/12_steps_to_navier_stokes/plots/step1.svg",
)
.unwrap();
}

View file

@ -1,3 +1,18 @@
fn main() { use clap::Parser;
println!("Hello, world!"); use steps::run_example_step1;
#[derive(Parser, Debug)]
#[command(version, about = "Simple runner for navier-stokes steps")]
struct Args {
// Which step to run
#[arg(value_parser = clap::value_parser!(u8).range(1..=12))]
pub step_count: u8,
}
fn main() {
let args = Args::parse();
match args.step_count {
1 => run_example_step1(),
_ => todo!(),
}
} }

View file

@ -0,0 +1,75 @@
pub struct Step1Config {
pub c: f64,
pub dt: f64,
pub dx: f64,
pub u0: Vec<f64>,
pub timesteps: usize,
}
/// Take a single timestep and calculate the updated u.
/// NOTE: Our BC fixes u(x=0, t) = u(x=0, t=0) for simplicity.
fn step(us: &[f64], dt: f64, dx: f64, c: f64) -> Vec<f64> {
us.iter()
.enumerate()
.map(|(i, u)| -> f64 {
if i > 0 {
u - (dt / dx) * c * (u - us[i - 1])
} else {
*u
}
})
.collect()
}
pub fn one_dim_solver(config: Step1Config) -> Vec<Vec<f64>> {
let mut uss: Vec<Vec<f64>> = vec![config.u0.clone()];
for iter in 1..config.timesteps + 1 {
uss.push(step(&uss[iter - 1], config.dt, config.dx, config.c));
println!("i = {iter}: {:?}", uss[iter])
}
uss
}
#[cfg(test)]
mod test {
use crate::step_1::{Step1Config, one_dim_solver, step};
#[test]
fn test_step() {
let new_us = step(&[1.0, 2.0, 1.0], 0.5, 2.0, 1.0);
let correct_us = [1.0, 1.75, 1.25];
for (i, (sol, correct)) in new_us.iter().zip(correct_us).enumerate() {
println!("i={i} sol={sol} corect={correct}");
assert!(f64::abs(sol - correct) < 1e-12);
}
}
#[test]
pub fn test_tophat_sim() {
let baseline = [0.0; 5];
let top = [1.0; 5];
let config = Step1Config {
c: 3.0,
dt: 0.25,
dx: 10.0,
u0: [&baseline[..], &top[..], &baseline[..]].concat(),
timesteps: 1,
};
let uss = one_dim_solver(config);
let correct_solution = [
&baseline[..],
&[0.925, 1.0, 1.0, 1.0, 1.0, 0.075, 0.0, 0.0, 0.0, 0.0],
]
.concat();
for (i, (sol, correct)) in uss[1].iter().zip(correct_solution).enumerate() {
println!("{i}, {sol}, {correct}");
assert!((sol - correct).abs() < 1e-12);
}
}
}

View file

@ -0,0 +1,38 @@
use poloto::prelude::*;
use std::fs::File;
use std::io::Write;
pub fn plot_vectors(
data: &[Vec<f64>],
labels: Option<&[&str]>,
file_name: &str,
) -> std::io::Result<()> {
let plots: Vec<_> = data
.iter()
.enumerate()
.map(|(i, v)| {
let label = labels
.and_then(|l| l.get(i).copied())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("Vector {}", i + 1));
poloto::build::plot(label).line(v.iter().enumerate().map(|(x, &y)| [x as f64, y]))
})
.collect();
// 2. Pass the Vec directly — Vec<impl PlotIterator> implements PlotIterator.
let plotter =
poloto::frame_build()
.data(plots)
.build_and_label(("Data Comparison", "Index", "Value"));
// 3. Render and save
let content = plotter
.append_to(poloto::header().light_theme())
.render_string()
.unwrap();
let mut file = File::create(file_name)?;
write!(file, "{}", content)?;
Ok(())
}

View file

@ -3,3 +3,10 @@
Playground for me to mess around with CFD simulations. Playground for me to mess around with CFD simulations.
First part of this will be implementing Lorena Barba's 12 steps to Navier-Stokes codes. First part of this will be implementing Lorena Barba's 12 steps to Navier-Stokes codes.
Also will be doing this in Rust because performance isn't critical here and I like Rust the best 🦀🦀🦀. Also will be doing this in Rust because performance isn't critical here and I like Rust the best 🦀🦀🦀.
## Notes on Rust Code
Just fyi to anyone coming across this.
This is a playground and not intended to be production grade rust.
As a result, you may see some unwraps here and there (or other not safe/not production Rust).
I don't suggest copying this code exactly if you are writing your own production grade solvers.