Skip to content
This repository was archived by the owner on Apr 23, 2025. It is now read-only.

Commit e920838

Browse files
authored
Update Shallow Water PDE example to use GIF writer (#684)
* Render shallow water PDE example visualizations with GIF writer - Use Tensor as protocol requirement for water level output - Remove single-purpose visualization code - Updated readme file * GIF quantizing for grayscale image tensors - Greyscale image is represented as [width, height, 1] shaped tensor - New palette with 64 shades of grey * Improve CLI of shallow water PDE example - Options for setting resolution, duration, iterations, learning rate and target image - Write animations to 'output' directory - Can be run from repository root - Updated readme
1 parent 682bb0b commit e920838

12 files changed

+120
-109
lines changed

Examples/Shallow-Water-PDE/ArrayLoopSolution.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ import TensorFlow
4343
///
4444
struct ArrayLoopSolution: ShallowWaterEquationSolution {
4545
/// Water level height
46-
var waterLevel: [[Float]] { u1 }
46+
var waterLevel: Tensor<Float> {
47+
Tensor<Float>(shape: TensorShape([resolution, resolution]), scalars: u1.flatMap{ $0 })
48+
}
4749
/// Solution time
4850
var time: Float { t }
4951

@@ -58,7 +60,7 @@ struct ArrayLoopSolution: ShallowWaterEquationSolution {
5860
/// Dispersion coefficient
5961
@noDerivative private let α: Float = 0.00001
6062
/// Number of spatial grid points
61-
@noDerivative private let resolution: Int = 256
63+
@noDerivative private let resolution: Int
6264
/// Spatial discretization step
6365
@noDerivative private var Δx: Float { 1 / Float(resolution) }
6466
/// Time-step calculated to stay below the CFL stability limit
@@ -67,11 +69,11 @@ struct ArrayLoopSolution: ShallowWaterEquationSolution {
6769
/// Creates initial solution with water level `u0` at time `t`.
6870
@differentiable
6971
init(waterLevel u0: [[Float]], time t: Float = 0.0) {
72+
self.resolution = u0.count
7073
self.u0 = u0
7174
self.u1 = u0
7275
self.t = t
7376

74-
precondition(u0.count == resolution)
7577
precondition(u0.allSatisfy { $0.count == resolution })
7678
}
7779

@@ -102,11 +104,11 @@ struct ArrayLoopSolution: ShallowWaterEquationSolution {
102104
/// Constructs intermediate solution with previous water level `u0`, current water level `u1` and time `t`.
103105
@differentiable
104106
private init(u0: [[Float]], u1: [[Float]], t: Float) {
107+
self.resolution = u0.count
105108
self.u0 = u0
106109
self.u1 = u1
107110
self.t = t
108111

109-
precondition(u0.count == self.resolution)
110112
precondition(u0.allSatisfy { $0.count == self.resolution })
111113
precondition(u1.count == self.resolution)
112114
precondition(u1.allSatisfy { $0.count == self.resolution })
Loading
-9.06 MB
Loading

Examples/Shallow-Water-PDE/README.md

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,33 @@ The code is re-implemented from the [DiffTaichi paper](https://arxiv.org/abs/191
99

1010
## Splash in a Bathtub
1111

12-
The following code builds and runs a demo simulation of a water surface behavior in a rectangular bathtub. There's an initial "splash" at the beginning that drives the simulation. The splash generates surface gravity waves that propagate away from the center and reflect off the domain walls.
13-
1412
<img alt="Simulation of a splash in bathtub" src="Images/Splash.gif" align="right">
1513

16-
```sh
17-
# Make sure you run the example from this directory
18-
cd swift-models/Examples/Shallow-Water-PDE
14+
The following code builds and runs a demo simulation of a water surface behavior in a rectangular bathtub. There's an initial "splash" at the beginning that drives the simulation. The splash generates surface gravity waves that propagate away from the center and reflect off the domain walls.
1915

16+
```sh
2017
# Build and run the example app with the splash flag
2118
swift run -c release Shallow-Water-PDE --splash
22-
23-
# Create animation and remove the intermediate files
24-
convert Images/Splash-*.jpg Images/Splash.gif
25-
rm Images/Splash-*.jpg
2619
```
2720

21+
Animation of the solution is saved to ` output/splash.gif`.
22+
2823

2924
## Optimization of Initial Water Level
3025

26+
<img alt="Result of initial condition optimization" src="Images/Optimization.gif" align="right">
27+
3128
The following example takes advantage of the end-to-end differentiability of the PDE solution and optimizes the initial condition. The initial condition is the water surface height used to start the simulation. The goal, a very tricky one from the perspective of optimal control theory, is to achieve specific wave pattern when the simulation terminates.
3229

3330
The goal is represented by a MSE cost function that measures difference between the terminal water level and a target picture. Optimization procedure itself is a simple gradient descent that adjust the initial water surface height to achieve smaller cost after every iteration.
3431

35-
<img alt="Result of initial condition optimization" src="Images/Optimization.gif" align="right">
36-
3732
```sh
38-
# Make sure you run the example from this directory
39-
cd swift-models/Examples/Shallow-Water-PDE
40-
4133
# Build and run the example app with the optimization flag
4234
swift run -c release Shallow-Water-PDE --optimization
43-
44-
# Create animation and remove the intermediate files
45-
convert Images/Optimization-*.jpg Images/Optimization.gif
46-
rm Images/Optimization-*.jpg
4735
```
4836

37+
Animation of the optimized solution is saved to `output/optimization.gif`.
38+
4939

5040
## Benchmark of 4 Solver Implementations
5141

Examples/Shallow-Water-PDE/Solution.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import TensorFlow
1919
/// Differentiable solution of shallow water equation on a unit square.
2020
protocol ShallowWaterEquationSolution: Differentiable {
2121
/// Snapshot of water level height at time `time`.
22-
@noDerivative var waterLevel: [[Float]] { get }
22+
@noDerivative var waterLevel: Tensor<Float> { get }
2323
/// Solution time
2424
@noDerivative var time: Float { get }
2525

@@ -44,4 +44,26 @@ extension Array where Array.Element: ShallowWaterEquationSolution {
4444
}
4545
self.append(currentSolution)
4646
}
47+
48+
/// Saves the shallow water equation solutions as a GIF image with the specified `delay` and keeping every `keep` frames.
49+
func saveAnimatedImage(directory: String, name: String, delay: Int = 4, keep: Int = 8) throws {
50+
let filtered = self.enumerated().filter { $0.offset % keep == 0 }.map { $0.element }
51+
let frames = filtered.map { $0.waterLevel.normalizedGrayscaleImage(min: -1, max: +1) }
52+
try frames.saveAnimatedImage(directory: directory, name: name, delay: delay)
53+
}
54+
}
55+
56+
// MARK: - Utilities
57+
58+
extension Tensor where Scalar == Float {
59+
/// Returns a 3D grayscale image tensor clipped and normalized from `min`-`max` to 0-255 range.
60+
func normalizedGrayscaleImage(min: Scalar = -1, max: Scalar = +1) -> Tensor<Float> {
61+
precondition(max > min)
62+
precondition(rank == 2)
63+
64+
let clipped = self.clipped(min: min, max: max)
65+
let normalized = (clipped - min) / (max - min) * Float(255.0)
66+
67+
return normalized.expandingShape(at: 2)
68+
}
4769
}

Examples/Shallow-Water-PDE/TensorConvSolution.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import TensorFlow
4949
///
5050
struct TensorConvSolution: ShallowWaterEquationSolution {
5151
/// Water level height
52-
var waterLevel: [[Float]] { u1.array.map { $0.scalars } }
52+
var waterLevel: Tensor<Float> { u1 }
5353
/// Solution time
5454
var time: Float { t }
5555

@@ -64,7 +64,7 @@ struct TensorConvSolution: ShallowWaterEquationSolution {
6464
/// Dispersion coefficient
6565
@noDerivative private let α: Float = 0.00001
6666
/// Number of spatial grid points
67-
@noDerivative private let resolution: Int = 256
67+
@noDerivative private let resolution: Int
6868
/// Spatial discretization step
6969
@noDerivative private var Δx: Float { 1 / Float(resolution) }
7070
/// Time-step calculated to stay below the CFL stability limit
@@ -76,6 +76,7 @@ struct TensorConvSolution: ShallowWaterEquationSolution {
7676
/// Creates initial solution with water level `u0` at time `t`.
7777
@differentiable
7878
init(waterLevel u0: Tensor<Float>, time t: Float = 0.0) {
79+
self.resolution = u0.shape[0]
7980
self.u0 = u0
8081
self.u1 = u0
8182
self.t = t
@@ -125,6 +126,7 @@ struct TensorConvSolution: ShallowWaterEquationSolution {
125126
/// Constructs intermediate solution with previous water level `u0`, current water level `u1` and time `t`.
126127
@differentiable
127128
private init(u0: Tensor<Float>, u1: Tensor<Float>, t: Float) {
129+
self.resolution = u0.shape[0]
128130
self.u0 = u0
129131
self.u1 = u1
130132
self.t = t

Examples/Shallow-Water-PDE/TensorLoopSolution.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import TensorFlow
4444
///
4545
struct TensorLoopSolution: ShallowWaterEquationSolution {
4646
/// Water level height
47-
var waterLevel: [[Float]] { u1.array.map { $0.scalars } }
47+
var waterLevel: Tensor<Float> { u1 }
4848
/// Solution time
4949
var time: Float { t }
5050

@@ -59,7 +59,7 @@ struct TensorLoopSolution: ShallowWaterEquationSolution {
5959
/// Dispersion coefficient
6060
@noDerivative private let α: Float = 0.00001
6161
/// Number of spatial grid points
62-
@noDerivative private let resolution: Int = 256
62+
@noDerivative private let resolution: Int
6363
/// Spatial discretization step
6464
@noDerivative private var Δx: Float { 1 / Float(resolution) }
6565
/// Time-step calculated to stay below the CFL stability limit
@@ -68,6 +68,7 @@ struct TensorLoopSolution: ShallowWaterEquationSolution {
6868
/// Creates initial solution with water level `u0` at time `t` using the specified TensorFlow `device`.
6969
@differentiable
7070
init(waterLevel u0: Tensor<Float>, time t: Float = 0.0) {
71+
self.resolution = u0.shape[0]
7172
self.u0 = u0
7273
self.u1 = u0
7374
self.t = t
@@ -112,6 +113,7 @@ struct TensorLoopSolution: ShallowWaterEquationSolution {
112113
/// Constructs intermediate solution with previous water level `u0`, current water level `u1` and time `t`.
113114
@differentiable
114115
private init(u0: Tensor<Float>, u1: Tensor<Float>, t: Float) {
116+
self.resolution = u0.shape[0]
115117
self.u0 = u0
116118
self.u1 = u1
117119
self.t = t

Examples/Shallow-Water-PDE/TensorSliceSolution.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import TensorFlow
4949
///
5050
struct TensorSliceSolution: ShallowWaterEquationSolution {
5151
/// Water level height
52-
var waterLevel: [[Float]] { u1.array.map { $0.scalars } }
52+
var waterLevel: Tensor<Float> { u1 }
5353
/// Solution time
5454
var time: Float { t }
5555

@@ -64,7 +64,7 @@ struct TensorSliceSolution: ShallowWaterEquationSolution {
6464
/// Dispersion coefficient
6565
@noDerivative private let α: Float = 0.00001
6666
/// Number of spatial grid points
67-
@noDerivative private let resolution: Int = 256
67+
@noDerivative private let resolution: Int
6868
/// Spatial discretization step
6969
@noDerivative private var Δx: Float { 1 / Float(resolution) }
7070
/// Time-step calculated to stay below the CFL stability limit
@@ -73,6 +73,7 @@ struct TensorSliceSolution: ShallowWaterEquationSolution {
7373
/// Creates initial solution with water level `u0` at time `t`.
7474
@differentiable
7575
init(waterLevel u0: Tensor<Float>, time t: Float = 0.0) {
76+
self.resolution = u0.shape[0]
7677
self.u0 = u0
7778
self.u1 = u0
7879
self.t = t
@@ -116,6 +117,7 @@ struct TensorSliceSolution: ShallowWaterEquationSolution {
116117
/// Constructs intermediate solution with previous water level `u0`, current water level `u1` and time `t`.
117118
@differentiable
118119
private init(u0: Tensor<Float>, u1: Tensor<Float>, t: Float) {
120+
self.resolution = u0.shape[0]
119121
self.u0 = u0
120122
self.u1 = u1
121123
self.t = t

Examples/Shallow-Water-PDE/Visualization.swift

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)