///|
pub(all) struct PipelineConfig {
name : String
grid : Grid1D
particles : Int
steps : Int
dt : Double
shape : ShapeKind
boundary : Boundary1D
} derive(Debug, ToJson)
///|
pub(all) struct PipelineFrame {
step : Int
state : PicState
diagnostics : PicDiagnostics
} derive(Debug, ToJson)
///|
pub(all) struct PipelineResult {
config : PipelineConfig
frames : Array[PipelineFrame]
} derive(Debug, ToJson)
///|
pub fn PipelineConfig::new(
name : String,
grid : Grid1D,
particles : Int,
steps : Int,
dt : Double,
) -> PipelineConfig {
{
name,
grid,
particles: particles.max(0),
steps: steps.max(0),
dt,
shape: CIC,
boundary: Periodic,
}
}
///|
pub fn PipelineConfig::with_shape(
config : PipelineConfig,
shape : ShapeKind,
) -> PipelineConfig {
{ ..config, shape, }
}
///|
pub fn PipelineConfig::with_boundary(
config : PipelineConfig,
boundary : Boundary1D,
) -> PipelineConfig {
{ ..config, boundary, }
}
///|
pub fn pipeline_initial_state(config : PipelineConfig) -> PicState {
let particles = sample_particles(
config.grid,
config.particles,
deterministic_seed(config.name, 11),
0.0,
1.0e4,
)
PicState::new(config.grid, particles)
}
///|
pub fn pipeline_frame(
_config : PipelineConfig,
step : Int,
state : PicState,
) -> PipelineFrame {
{ step, state, diagnostics: PicDiagnostics::from_state(state) }
}
///|
pub fn pipeline_step(config : PipelineConfig, state : PicState) -> PicState {
let stepped = state.step(config.dt)
let bounded = apply_particle_boundary_batch(
config.grid,
config.boundary,
stepped.particles,
).alive
let charge = deposit_charge_with_shape(config.grid, bounded, config.shape)
{
grid: config.grid,
particles: bounded,
field: solve_periodic_field(config.grid, charge),
time: stepped.time,
}
}
///|
pub fn run_pipeline(config : PipelineConfig) -> PipelineResult {
let frames : Array[PipelineFrame] = []
let mut state = pipeline_initial_state(config)
frames.push(pipeline_frame(config, 0, state))
for step in 0.. PipelineFrame? {
if result.frames.length() == 0 {
None
} else {
Some(result.frames[result.frames.length() - 1])
}
}
///|
pub fn PipelineResult::times(result : PipelineResult) -> Array[Double] {
result.frames.map(fn(frame) { frame.diagnostics.time })
}
///|
pub fn PipelineResult::energies(result : PipelineResult) -> Array[Double] {
result.frames.map(fn(frame) { frame.diagnostics.total_energy() })
}
///|
pub fn PipelineResult::charge_errors(result : PipelineResult) -> Array[Double] {
if result.frames.length() == 0 {
[]
} else {
let initial = result.frames[0].diagnostics.total_charge
result.frames.map(fn(frame) {
(frame.diagnostics.total_charge - initial).abs()
})
}
}
///|
pub fn pipeline_to_csv(result : PipelineResult) -> String {
let output = StringBuilder()
output.write_string(
"step,time,particles,total_charge,kinetic_energy,field_energy,total_energy\n",
)
for frame in result.frames {
let d = frame.diagnostics
output.write_string(
"\{frame.step},\{d.time},\{d.particles},\{d.total_charge},\{d.kinetic_energy},\{d.field_energy},\{d.total_energy()}\n",
)
}
output.to_string()
}
///|
pub fn pipeline_summary(result : PipelineResult) -> String {
"name=\{result.config.name}\nframes=\{result.frames.length()}\nwork_units=\{result.config.grid.cells * result.config.particles * result.config.steps}\n"
}