Skip to content

Commit

Permalink
Merge branch 'develop' into gui-2.0
Browse files Browse the repository at this point in the history
# Conflicts:
#	phi/app/app.py
#	phi/viz/dash/dash_gui.py
  • Loading branch information
holl- committed Dec 12, 2019
2 parents 1fbf52e + 887f8f2 commit 2496f5e
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 60 deletions.
10 changes: 9 additions & 1 deletion README.md
@@ -1,6 +1,7 @@
# Φ<sub>*Flow*</sub>

[![Build Status](https://travis-ci.com/tum-pbs/PhiFlow.svg?token=8vG2QPsZzeswTApmkekH&branch=master)](https://travis-ci.com/tum-pbs/PhiFlow)
[![image](https://www.tensorflow.org/images/colab_logo_32px.png) Run in Google Colab](https://colab.research.google.com/drive/1S21OY8hzh1oZK2wQyL3BNXvSlrMTtRbV#offline=true&sandboxMode=true)

![Gui](documentation/figures/Gui.png)

Expand Down Expand Up @@ -34,7 +35,12 @@ with TensorFlow support.
| [Index](documentation) | [Demos](demos) / [Tests](tests) | [Source](phi) |
|------------------------|---------------------------------|---------------|

If you would like to get right into it and have a look at some example code, check out the following demos:

If you would like to get right into it and have a look at some code, check out the example
[notebook on Google Colab](https://colab.research.google.com/drive/1S21OY8hzh1oZK2wQyL3BNXvSlrMTtRbV#offline=true&sandboxMode=true).
It lets you experiment with Φ<sub>*Flow*</sub> in the browser.

The following introductory demos are also helpful to get started with writing your own app using Φ<sub>*Flow*</sub>:

- [simpleplume.py](./demos/simpleplume.py): Runs a fluid simulation and displays it in the browser
- [optimize_pressure.py](./demos/optimize_pressure.py): Uses TensorFlow to optimize a velocity channel. TensorBoard can be started from the GUI and displays the loss.
Expand Down Expand Up @@ -64,6 +70,8 @@ All simulations of continuous systems are based on the [Field API](documentation

The [software architecture documentation](documentation/Software_Architecture.md) shows the building blocks of Φ<sub>*Flow*</sub> and the module dependencies.

## [Version history](documentation/Version_History.md)


## Known Issues

Expand Down
4 changes: 2 additions & 2 deletions documentation/Simulation_Architecture.md
Expand Up @@ -88,8 +88,8 @@ This aligns with the typical TensorFlow workflow where `tf.Session.run` is used
`phi.tf.session.Session.run` simply extends the functionality by allowing `State` objects to be passed directly.

While this extra code is unavoidable for machine learning applications, if you are simply running a simulation, you
can add the states to a world (e.g. using `world.Fluid` instead of `Fluid`) and call the function
`tf_bake_graph(session, world)` to automatically convert all physics objects to TensorFlow graph executions.
can add the states to a world using `world.add(state)` and call the function
`tf_bake_graph(world, session)` to automatically convert all physics objects to TensorFlow graph executions.

The similarities and differences of NumPy vs TensorFlow are illustrated in the example
[manual_fluid_numpy_or_tf.py](../demos/manual_fluid_numpy_or_tf.py) for a simple custom fluid simulation.
Expand Down
19 changes: 19 additions & 0 deletions documentation/Version_History.md
@@ -0,0 +1,19 @@
# Φ<sub>*Flow*</sub> Version History

## Version 1.0.x


Version 1.0.0:

- Public release on GitHub
- Open-source with MIT license
- Use Travis-CI for testing


## Pre-release version

Major refactoring was performed before release, renaming (among others): Smoke &rarr; Fluid, anytype &rarr; unsafe.

Version 0.4 introduced the Field API, bringing with it a new data layout for staggered grids.

Version 0.3 introduced structs and used them to split state and physics into different objects.
3 changes: 2 additions & 1 deletion phi/math/__init__.py
Expand Up @@ -59,7 +59,8 @@
ones_like = DYNAMIC_BACKEND.ones_like
pad = DYNAMIC_BACKEND.pad
py_func = DYNAMIC_BACKEND.py_func
random_like = DYNAMIC_BACKEND.random_like
random_uniform = DYNAMIC_BACKEND.random_uniform
range = DYNAMIC_BACKEND.range
real = DYNAMIC_BACKEND.real
resample = DYNAMIC_BACKEND.resample
reshape = DYNAMIC_BACKEND.reshape
Expand Down
24 changes: 18 additions & 6 deletions phi/math/base_backend.py
Expand Up @@ -23,7 +23,7 @@ def is_tensor(self, x):
def as_tensor(self, x):
raise NotImplementedError()

def random_like(self, array):
def random_uniform(self, shape):
raise NotImplementedError(self)

def stack(self, values, axis=0):
Expand Down Expand Up @@ -67,7 +67,10 @@ def mean(self, value, axis=None):
def py_func(self, func, inputs, Tout, shape_out, stateful=True, name=None, grad=None):
raise NotImplementedError(self)

def resample(self, inputs, sample_coords, interpolation='LINEAR', boundary='ZERO'):
def resample(self, inputs, sample_coords, interpolation='LINEAR', boundary='zero'):
raise NotImplementedError(self)

def range(self, start, limit=None, delta=1, dtype=None):
raise NotImplementedError(self)

def zeros_like(self, tensor):
Expand Down Expand Up @@ -149,6 +152,9 @@ def dimrange(self, tensor):
def gather(self, values, indices):
raise NotImplementedError(self)

def gather_nd(self, values, indices):
raise NotImplementedError(self)

def flatten(self, x):
return self.reshape(x, (-1,) )

Expand Down Expand Up @@ -273,8 +279,8 @@ def is_tensor(self, x):
def as_tensor(self, x):
return self.choose_backend(x).as_tensor(x)

def random_like(self, tensor):
return self.choose_backend(tensor).random_like(tensor)
def random_uniform(self, tensor):
return self.choose_backend(tensor).random_uniform(tensor)

def stack(self, values, axis=0):
return self.choose_backend(values).stack(values, axis)
Expand Down Expand Up @@ -313,9 +319,12 @@ def mean(self, value, axis=None):
def py_func(self, func, inputs, Tout, shape_out, stateful=True, name=None, grad=None):
return self.choose_backend(inputs).py_func(func, inputs, Tout, shape_out, stateful, name, grad)

def resample(self, inputs, sample_coords, interpolation='LINEAR', boundary='ZERO'):
def resample(self, inputs, sample_coords, interpolation='LINEAR', boundary='zero'):
return self.choose_backend((inputs, sample_coords)).resample(inputs, sample_coords, interpolation, boundary)

def range(self, start, limit=None, delta=1, dtype=None):
return self.choose_backend((start, limit, delta)).range(start, limit, delta, dtype)

def zeros_like(self, tensor):
return self.choose_backend(tensor).zeros_like(tensor)

Expand Down Expand Up @@ -389,7 +398,10 @@ def to_complex(self, x):
return self.choose_backend(x).to_complex(x)

def gather(self, values, indices):
return self.choose_backend([values, indices]).gather(values, indices)
return self.choose_backend([values]).gather(values, indices)

def gather_nd(self, values, indices):
return self.choose_backend([values]).gather_nd(values, indices)

def unstack(self, tensor, axis=0):
return self.choose_backend(tensor).unstack(tensor, axis)
Expand Down
23 changes: 13 additions & 10 deletions phi/math/nd.py
@@ -1,3 +1,6 @@
# Because division is different in Python 2 and 3
from __future__ import division

import numpy as np

from phi import struct
Expand Down Expand Up @@ -212,8 +215,8 @@ def gradient(tensor, dx=1, difference='forward'):
def _backward_diff_nd(field, dims):
df_dq = []
for dimension in dims:
upper_slices = [(slice(1, None) if i == dimension else slice(None)) for i in dims]
lower_slices = [(slice(-1) if i == dimension else slice(None)) for i in dims]
upper_slices = tuple([(slice(1, None) if i==dimension else slice(None)) for i in dims])
lower_slices = tuple([(slice(-1) if i==dimension else slice(None)) for i in dims])
diff = field[(slice(None),)+upper_slices] - field[(slice(None),)+lower_slices]
padded = math.pad(diff, [[0,0]]+[([1,0] if i == dimension else [0,0]) for i in dims])
df_dq.append(padded)
Expand All @@ -223,9 +226,9 @@ def _backward_diff_nd(field, dims):
def _forward_diff_nd(field, dims):
df_dq = []
for dimension in dims:
upper_slices = [(slice(1, None) if i == dimension else slice(None)) for i in dims]
lower_slices = [(slice(-1) if i == dimension else slice(None)) for i in dims]
diff = field[(slice(None),)+upper_slices] - field[(slice(None),)+lower_slices]
upper_slices = tuple([(slice(1, None) if i==dimension else slice(None)) for i in dims])
lower_slices = tuple([(slice(-1) if i==dimension else slice(None)) for i in dims])
diff = field[(slice(None),) + upper_slices] - field[(slice(None),) + lower_slices]
padded = math.pad(diff, [[0,0]]+[([0,1] if i == dimension else [0,0]) for i in dims])
df_dq.append(padded)
return math.stack(df_dq, axis=-1)
Expand All @@ -235,9 +238,9 @@ def _central_diff_nd(field, dims):
field = math.pad(field, [[0,0]] + [[1,1]]*spatial_rank(field) + [[0, 0]], 'symmetric')
df_dq = []
for dimension in dims:
upper_slices = [(slice(2, None) if i==dimension else slice(1,-1)) for i in dims]
lower_slices = [(slice(-2) if i==dimension else slice(1,-1)) for i in dims]
diff = field[(slice(None),) + upper_slices + [0]] - field[(slice(None),) + lower_slices + [0]]
upper_slices = tuple([(slice(2, None) if i==dimension else slice(1,-1)) for i in dims])
lower_slices = tuple([(slice(-2) if i==dimension else slice(1,-1)) for i in dims])
diff = field[(slice(None),) + upper_slices + (0,)] - field[(slice(None),) + lower_slices + (0,)]
df_dq.append(diff)
return math.stack(df_dq, axis=-1)

Expand Down Expand Up @@ -392,7 +395,7 @@ def interpolate_linear(tensor, upper_weight, dimensions):
lower_weight = 1 - upper_weight
for dimension in spatial_dimensions(tensor):
if dimension in dimensions:
upper_slices = [(slice(1, None) if i == dimension else slice(None)) for i in all_dimensions(tensor)]
lower_slices = [(slice(-1) if i == dimension else slice(None)) for i in all_dimensions(tensor)]
upper_slices = tuple([(slice(1, None) if i == dimension else slice(None)) for i in all_dimensions(tensor)])
lower_slices = tuple([(slice(-1) if i == dimension else slice(None)) for i in all_dimensions(tensor)])
tensor = tensor[upper_slices] * upper_weight[...,dimension-1] + tensor[lower_slices] * lower_weight[...,dimension-1]
return tensor
12 changes: 11 additions & 1 deletion phi/math/scipy_backend.py
Expand Up @@ -44,12 +44,17 @@ def divide_no_nan(self, x, y):
else:
return (x/y)

def random_like(self, shape):
def random_uniform(self, shape):
return np.random.random(shape).astype('f')

def rank(self, value):
return len(value.shape)

def range(self, start, limit=None, delta=1, dtype=None):
if limit is None:
start, limit = 0, start
return np.arange(start, limit, delta, dtype)

def tile(self, value, multiples):
return np.tile(value, multiples)

Expand Down Expand Up @@ -229,6 +234,11 @@ def cast(self, x, dtype):
def gather(self, values, indices):
return values[indices]

def gather_nd(self, values, indices):
# Reduce rank of input indices, by convention it should be [index] so gather works for Tensorflow
index, = indices
return values[index]

def unstack(self, tensor, axis=0):
if axis < 0:
axis += len(tensor.shape)
Expand Down
6 changes: 3 additions & 3 deletions phi/physics/domain.py
Expand Up @@ -89,17 +89,17 @@ def equal(grid1, grid2):

def centered_shape(self, components=1, batch_size=1, name=None, extrapolation=None, age=0.0):
with struct.unsafe():
return CenteredGrid(tensor_shape(batch_size, self.resolution, components), age=age, box=self.box, extrapolation=extrapolation, name=name, batch_size=batch_size)
return CenteredGrid(tensor_shape(batch_size, self.resolution, components), age=age, box=self.box, extrapolation=extrapolation, name=name, batch_size=batch_size, flags=())

def staggered_shape(self, batch_size=1, name=None, extrapolation=None, age=0.0):
with struct.unsafe():
grids = []
for axis in range(self.rank):
shape = _extend1(tensor_shape(batch_size, self.resolution, 1), axis)
box = staggered_component_box(self.resolution, axis, self.box)
grid = CenteredGrid(shape, box, age=age, extrapolation=extrapolation, name=None, batch_size=batch_size)
grid = CenteredGrid(shape, box, age=age, extrapolation=extrapolation, name=None, batch_size=batch_size, flags=())
grids.append(grid)
return StaggeredGrid(grids, age=age, box=self.box, name=name, batch_size=batch_size, extrapolation=extrapolation)
return StaggeredGrid(grids, age=age, box=self.box, name=name, batch_size=batch_size, extrapolation=extrapolation, flags=())

def centered_grid(self, data, components=1, dtype=np.float32, name=None, batch_size=None, extrapolation=None):
if extrapolation is None:
Expand Down
1 change: 1 addition & 0 deletions phi/physics/field/__init__.py
Expand Up @@ -8,3 +8,4 @@
from . import advect
from . import manta
from .util import diffuse, data_bounds
from .sampled import SampledField
2 changes: 1 addition & 1 deletion phi/physics/field/grid.py
Expand Up @@ -14,7 +14,7 @@
def _crop_for_interpolation(data, offset_float, window_resolution):
offset = math.to_int(offset_float)
slices = [slice(o, o+res+1) for o, res in zip(offset, window_resolution)]
data = data[[slice(None)] + slices + [slice(None)]]
data = data[tuple([slice(None)] + slices + [slice(None)])]
return data


Expand Down

0 comments on commit 2496f5e

Please sign in to comment.