Skip to content

Commit

Permalink
Merge pull request #43 from tum-pbs/develop
Browse files Browse the repository at this point in the history
2.0.0
  • Loading branch information
holl- authored Sep 22, 2021
2 parents b6186fd + 58f43eb commit 4a85f8a
Show file tree
Hide file tree
Showing 133 changed files with 3,708 additions and 34,641 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --quiet tensorflow torch jax jaxlib plotly nbformat ipython pylint coverage pytest
pip install --quiet tensorflow tensorflow_probability torch jax jaxlib plotly nbformat ipython pylint coverage pytest
pip install .
- name: Test with pytest
run: |
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/update-gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Update Documentation

on:
push:
branches:
- develop

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/[email protected]

- name: Set up Python
uses: actions/setup-python@v2

- name: Install dependencies 🔧
run: |
python -m pip install --upgrade pip
pip install --quiet pdoc3 tensorflow torch jax jaxlib plotly nbformat ipython notebook ipywidgets
pip install .
pip list
- name: Build API with pdoc3
run: pdoc --html --output-dir docs --force phi

- name: Build static HTML for Jupyter Notebooks
run: jupyter nbconvert --to html --execute --allow-errors docs/*.ipynb

- name: Deploy 🚀
uses: JamesIves/[email protected] # See https://github.com/marketplace/actions/deploy-to-github-pages
with:
branch: gh-pages # The branch the action should deploy to.
folder: docs # The folder the action should deploy.
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# Φ<sub>Flow</sub>
# ![PhiFlow](docs/figures/Logo1_layout.png)

![Build Status](https://github.com/tum-pbs/PhiFlow/actions/workflows/unit-tests.yml/badge.svg)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/phiflow.svg)](https://pypi.org/project/phiflow/)
[![PyPI license](https://img.shields.io/pypi/l/phiflow.svg)](https://pypi.org/project/phiflow/)
[![Code Coverage](https://codecov.io/gh/tum-pbs/PhiFlow/branch/develop/graph/badge.svg)](https://codecov.io/gh/tum-pbs/PhiFlow/branch/develop/)
[![Google Collab Book](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1LNPpHoZSTNN1L1Jt9MjLZ0r3Ejg0u7hY#offline=true&sandboxMode=true)

![Gui](https://tum-pbs.github.io/PhiFlow/figures/WebInterface.png)
[![Google Collab Book](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tum-pbs/PhiFlow/blob/develop/docs/Fluids_Tutorial.ipynb)

Φ<sub>Flow</sub> is an open-source simulation toolkit built for optimization and machine learning applications.
It is written mostly in Python and can be used with
Expand All @@ -21,6 +19,8 @@ This is major version 2 of Φ<sub>Flow</sub>.
Version 1 is available in the [branch `1.5`](https://github.com/tum-pbs/PhiFlow/tree/1.5) but will not receive new features anymore.
Older versions are available through the [release history](https://github.com/tum-pbs/PhiFlow/releases).

![Gui](https://tum-pbs.github.io/PhiFlow/figures/WebInterface.png)

## Features

* Variety of built-in PDE operations with focus on fluid phenomena, allowing for concise formulation of simulations.
Expand All @@ -38,9 +38,9 @@ Older versions are available through the [release history](https://github.com/tu

## Installation

Installation with pip on Python 3.6 or newer:
Installation with pip on Python 3.6 and above:
``` bash
$ pip install phiflow==2.0.0rc2 dash plotly imageio
$ pip install phiflow dash plotly imageio
```
Install TensorFlow or PyTorch in addition to Φ<sub>Flow</sub> to enable machine learning capabilities and GPU execution.
See the [detailed installation instructions](https://tum-pbs.github.io/PhiFlow/Installation_Instructions.html) on how to compile the custom CUDA operators and verify your installation.
Expand All @@ -49,15 +49,13 @@ See the [detailed installation instructions](https://tum-pbs.github.io/PhiFlow/I
[**Documentation**](https://tum-pbs.github.io/PhiFlow/)
&nbsp;&nbsp;&nbsp; [**API**](https://tum-pbs.github.io/PhiFlow/phi/)
&nbsp;&nbsp;&nbsp; [**Demos**](https://github.com/tum-pbs/PhiFlow/tree/develop/demos)
&nbsp;&nbsp;&nbsp; [<img src="https://www.tensorflow.org/images/colab_logo_32px.png" height=16> **Fluids Tutorial**](https://colab.research.google.com/drive/1LNPpHoZSTNN1L1Jt9MjLZ0r3Ejg0u7hY#offline=true&sandboxMode=true)
&nbsp;&nbsp;&nbsp; [<img src="https://www.tensorflow.org/images/colab_logo_32px.png" height=16> **Playground**](https://colab.research.google.com/drive/1zBlQbmNguRt-Vt332YvdTqlV4DBcus2S#offline=true&sandboxMode=true)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<img src="https://www.tensorflow.org/images/colab_logo_32px.png" height=16> **Playground**](https://colab.research.google.com/drive/1zBlQbmNguRt-Vt332YvdTqlV4DBcus2S#offline=true&sandboxMode=true)

An overview of all available documentation can be found [here](https://tum-pbs.github.io/PhiFlow/).

If you would like to get right into it and have a look at some code, check out the
[tutorial notebook on Google Colab](https://colab.research.google.com/drive/1LNPpHoZSTNN1L1Jt9MjLZ0r3Ejg0u7hY#offline=true&sandboxMode=true).
[tutorial notebook on Google Colab](https://colab.research.google.com/github/tum-pbs/PhiFlow/blob/develop/docs/Fluids_Tutorial.ipynb).
It lets you run fluid simulations with Φ<sub>Flow</sub> in the browser.
Also check out the [explanation of common fluid simulation operations](https://tum-pbs.github.io/PhiFlow/Fluid_Simulation.html).

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

Expand All @@ -67,7 +65,6 @@ The following introductory demos are also helpful to get started with writing yo
## Version History

The [Version history](https://github.com/tum-pbs/PhiFlow/releases) lists all major changes since release.

The releases are also listed on [PyPI](https://pypi.org/project/phiflow/).

## Contributions
Expand Down
2 changes: 1 addition & 1 deletion demos/burgers_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

velocity = CenteredGrid(Noise(vector=2), extrapolation.PERIODIC, x=64, y=64, bounds=Box[0:200, 0:100]) * 2

for _ in view(play=False, framerate=10).range():
for _ in view(play=False, framerate=10, namespace=globals()).range():
velocity = diffuse.explicit(velocity, 0.1, dt=1)
velocity = advect.semi_lagrangian(velocity, velocity, dt=1)
8 changes: 4 additions & 4 deletions demos/differentiate_advection.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ def loss(velocity):
velocity_fit = StaggeredGrid(0, extrapolation.ZERO, **DOMAIN)
marker_fit = CenteredGrid(0, extrapolation.BOUNDARY, **DOMAIN)
smooth_difference = CenteredGrid(0, extrapolation.BOUNDARY, **DOMAIN)
app = view(display=['marker_fit', 'gradient'], play=False)
viewer = view(display=['marker_fit', 'gradient'], play=False, namespace=globals())

for iteration in app.range(warmup=1):
for iteration in viewer.range(warmup=1):
(loss, marker_fit, smooth_difference), (gradient,) = gradient_function(velocity_fit)
app.info(f"Loss = {loss:.2f}")
app.log_scalars(loss=loss)
viewer.info(f"Loss = {loss:.2f}")
viewer.log_scalars(loss=loss)
velocity_fit -= gradient
4 changes: 2 additions & 2 deletions demos/differentiate_pressure.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
DOMAIN = dict(x=80, y=64)
LEFT = StaggeredGrid(HardGeometryMask(Box[:40, :]), extrapolation.ZERO, **DOMAIN)
RIGHT = StaggeredGrid(HardGeometryMask(Box[40:, :]), extrapolation.ZERO, **DOMAIN)
TARGET = RIGHT * StaggeredGrid(lambda x: math.exp(-0.5 * math.vec_squared(x - (50, 10)) / 32**2), extrapolation.ZERO, **DOMAIN) * (0, 2)
TARGET = RIGHT * StaggeredGrid(lambda x: math.exp(-0.5 * math.vec_squared(x - (50, 10), 'vector') / 32**2), extrapolation.ZERO, **DOMAIN) * (0, 2)


def loss(v0, p0):
Expand All @@ -23,7 +23,7 @@ def loss(v0, p0):
eval_grad = field.functional_gradient(loss, [0], get_output=True)
p0 = None
velocity_fit = StaggeredGrid(Noise(), extrapolation.ZERO, **DOMAIN) * 0.1 * LEFT
viewer = view('incompressible_velocity', TARGET, 'gradient', velocity_fit, 'remaining_divergence', play=False)
viewer = view('incompressible_velocity', TARGET, 'gradient', velocity_fit, 'remaining_divergence', play=False, namespace=globals())

for iteration in viewer.range(warmup=1):
(loss, incompressible_velocity, pressure_guess), (gradient,) = eval_grad(velocity_fit, p0)
Expand Down
6 changes: 3 additions & 3 deletions demos/flip_liquid.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@

particles = DOMAIN.distribute_points(union(Box[15:30, 50:60], Box[:, :5])) * (0, 0)
# particles = nonzero(CenteredGrid(union(Box[15:30, 50:60], Box[:, :5]), 0, **DOMAIN)) * (0, 0)
velocity = particles >> DOMAIN.staggered_grid()
velocity = particles @ DOMAIN.staggered_grid()
pressure = DOMAIN.scalar_grid()
scene = particles & _OBSTACLE_POINTS * (0, 0) # only for plotting

for _ in view(display='scene', play=False).range():
for _ in view('scene,velocity,pressure', display='scene', play=False, namespace=globals()).range():
div_free_velocity, _, occupied = flip.make_incompressible(velocity + DT * GRAVITY, DOMAIN, particles, ACCESSIBLE_MASK)
particles = flip.map_velocity_to_particles(particles, div_free_velocity, occupied, previous_velocity_grid=velocity)
particles = advect.runge_kutta_4(particles, div_free_velocity, DT, accessible=ACCESSIBLE_MASK, occupied=occupied)
particles = flip.respect_boundaries(particles, DOMAIN, [OBSTACLE])
velocity = particles >> DOMAIN.staggered_grid()
velocity = particles @ DOMAIN.staggered_grid()
scene = particles & _OBSTACLE_POINTS * (0, 0)
10 changes: 5 additions & 5 deletions demos/fluid_logo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@

OBSTACLE_GEOMETRIES = [Box[15 + x * 7:15 + (x + 1) * 7, 41:83] for x in range(1, 10, 2)] + [Box[43:50, 41:48], Box[15:43, 83:90], Box[50:85, 83:90]]
OBSTACLE = Obstacle(union(OBSTACLE_GEOMETRIES))
OBSTACLE_MASK = HardGeometryMask(OBSTACLE.geometry) >> CenteredGrid(0, extrapolation.BOUNDARY, **DOMAIN)
OBSTACLE_MASK = HardGeometryMask(OBSTACLE.geometry) @ CenteredGrid(0, extrapolation.BOUNDARY, **DOMAIN)

INFLOW = CenteredGrid(Box[14:21, 6:10], extrapolation.BOUNDARY, **DOMAIN) + \
CenteredGrid(Box[79:86, 6:10], extrapolation.BOUNDARY, **DOMAIN) * 0.8 + \
CenteredGrid(Box[44:47, 49:50], extrapolation.BOUNDARY, **DOMAIN) * 0.1
CenteredGrid(Box[81:88, 6:10], extrapolation.BOUNDARY, **DOMAIN) * 0.9 + \
CenteredGrid(Box[44:47, 49:51], extrapolation.BOUNDARY, **DOMAIN) * 0.4
velocity = StaggeredGrid(0, extrapolation.ZERO, **DOMAIN)
smoke = pressure = divergence = remaining_divergence = CenteredGrid(0, extrapolation.BOUNDARY, **DOMAIN)

for _ in view(display=['smoke', 'velocity', 'pressure', 'OBSTACLE_MASK'], play=False).range(warmup=1):
for _ in view('smoke, velocity, pressure, OBSTACLE_MASK', play=False, namespace=globals()).range(warmup=1):
smoke = advect.semi_lagrangian(smoke, velocity, 1) + INFLOW
buoyancy_force = smoke * (0, 0.1) >> velocity # resamples density to velocity sample points
buoyancy_force = smoke * (0, 0.1) @ velocity # resamples density to velocity sample points
velocity = advect.semi_lagrangian(velocity, velocity, 1) + buoyancy_force
velocity, pressure = fluid.make_incompressible(velocity, (OBSTACLE,), Solve('CG-adaptive', 1e-5, 0, x0=pressure))
remaining_divergence = field.divergence(velocity)
2 changes: 1 addition & 1 deletion demos/heat_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
radius = control(4, (2, 10))
temperature = CenteredGrid(0, **DOMAIN)

for _ in view(temperature, framerate=30).range():
for _ in view(temperature, framerate=30, namespace=globals()).range():
temperature -= DT * CenteredGrid(Box[0:64, 44:46], **DOMAIN)
temperature += DT * CenteredGrid(Sphere([x, y], radius=radius), **DOMAIN)
temperature = diffuse.explicit(temperature, 0.5, DT, substeps=4)
82 changes: 4 additions & 78 deletions demos/hw2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,67 +22,6 @@
PARAMS = dict(c1=c1, nu=nu, N=N, arak=arakawa_coeff, kappa=kappa_coeff)


class Namespace(dict):
def __mul__(self, other):
if isinstance(other, Namespace):
return Namespace({key: other[key] * val for key, val in self.items()})
else:
return Namespace({key: other * val for key, val in self.items()})

__rmul__ = __mul__

def __div__(self, other):
if isinstance(other, Namespace):
return Namespace({key: val / other[key] for key, val in self.items()})
else:
return Namespace({key: val / other for key, val in self.items()})

def __truediv__(self, other):
if isinstance(other, Namespace):
return Namespace({key: val / other[key] for key, val in self.items()})
else:
return Namespace({key: val / other for key, val in self.items()})

def __rdiv__(self, other):
if isinstance(other, Namespace):
return Namespace({key: other[key] / val for key, val in self.items()})
else:
return Namespace({key: other / val for key, val in self.items()})

def __add__(self, other):
if isinstance(other, Namespace):
return Namespace({key: other[key] + val for key, val in self.items()})
else:
return Namespace({key: other + val for key, val in self.items()})

__radd__ = __add__

def __sub__(self, other):
return Namespace({key: other - val for key, val in self.items()})

def __getattr__(self, key):
try:
return self[key]
except KeyError as k:
raise AttributeError(k)

def __setattr__(self, key, value):
self[key] = value

def __delattr__(self, key):
try:
del self[key]
except KeyError as k:
raise AttributeError(k)

@property
def dtype(self):
return self["density"].dtype

def copy(self):
return Namespace({key: val for key, val in self.items()})


def get_phi(plasma, guess=None):
"""Fourier Poisson Solve for Phi"""
centered_omega = plasma.omega # - math.mean(plasma.omega)
Expand All @@ -95,7 +34,6 @@ def get_phi(plasma, guess=None):
)


# Diffusion function
def diffuse(arr, N, dx):
if not isinstance(N, int):
print(f"{N} {type(N)}")
Expand Down Expand Up @@ -129,18 +67,12 @@ def step_gradient_2d(plasma, phi, N=0, nu=0, c1=0, arak=0, kappa=0, dt=0):
n += -kappa * dy_p
if nu:
n += nu * diffuse(plasma.density, N=N, dx=plasma.dx)
return Namespace(
density=n,
omega=o,
phi=phi, # NOTE: NOT A GRADIENT
age=plasma.age + dt,
dx=plasma.dx,
)
return math.Dict(density=n, omega=o, phi=phi, age=plasma.age + dt, dx=plasma.dx)


def rk4_step(dt, physics_params, gradient_func=step_gradient_2d, **kwargs):
gradient_func = partial(gradient_func, **physics_params)
yn = Namespace(**kwargs) # given dict to Namespace
yn = math.Dict(**kwargs) # given dict to Namespace
in_age = yn.age
# Only in the first iteration recalculate phi
if yn.age == 0:
Expand All @@ -156,13 +88,7 @@ def rk4_step(dt, physics_params, gradient_func=step_gradient_2d, **kwargs):
k4 = dt * gradient_func(yn + k3, p3, dt=dt)
y1 = yn + (k1 + 2 * k2 + 2 * k3 + k4) / 6
phi = get_phi(y1) # , guess=pn+p3*0.5)
return Namespace(
density=y1.density,
omega=y1.omega,
phi=phi, # TODO: Somehow this does not work properly
age=in_age + dt, # y1 contains 2 time steps from compute
dx=yn.dx,
)
return math.Dict(density=y1.density, omega=y1.omega, phi=phi, age=in_age + dt, dx=yn.dx)


domain = dict(extrapolation=extrapolation.PERIODIC, bounds=Box[0:L, 0:L])
Expand All @@ -185,7 +111,7 @@ def rk4_step(dt, physics_params, gradient_func=step_gradient_2d, **kwargs):
)
)

for _ in view(density, omega, phi, play=False, framerate=10).range():
for _ in view(density, omega, phi, play=False, framerate=10, namespace=globals()).range():
new_state = rk4(dt, density=density, omega=omega, phi=phi, age=age, dx=dx)
density, omega, phi = new_state["density"], new_state["omega"], new_state["phi"]
age += dt
4 changes: 2 additions & 2 deletions demos/marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def checkerboard(size=8, offset=2):

velocity = DOMAIN.staggered_grid(Noise(vector=2, scale=100)) * 4
dense_marker = CenteredGrid(checkerboard(), DOMAIN.boundaries['scalar'], DOMAIN.bounds)
points = math.join_dimensions(DOMAIN.cells.center.x[::4].y[::4], ('x', 'y'), collection('points')).points.as_batch()
points = math.pack_dims(DOMAIN.cells.center.x[::4].y[::4], ('x', 'y'), instance('points')).points.as_batch()
sparse_marker = DOMAIN.points(points)

for _ in view(framerate=10, play=False).range():
for _ in view(framerate=10, play=False, namespace=globals()).range():
velocity, _ = fluid.make_incompressible(velocity)
dense_marker = advect.advect(dense_marker, velocity, DT)
sparse_marker = advect.advect(sparse_marker, velocity, DT)
Expand Down
17 changes: 8 additions & 9 deletions demos/moving_obstacle.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from phi.torch.flow import *
from phi.flow import *


TORCH.set_default_device('GPU')
DOMAIN = Domain(x=30, y=30, boundaries=((OPEN, OPEN), (CLOSED, CLOSED)), bounds=Box[0:30, 0:30])
DOMAIN = dict(x=30, y=30)
DT = 0.1


Expand All @@ -15,12 +14,12 @@ def move_obstacle(obstacle):


obstacle = Obstacle((Box[5:11, 10:16]), velocity=[1., 0], angular_velocity=tensor(0,))
velocity = DOMAIN.staggered_grid((0, 0))
pressure = DOMAIN.scalar_grid()
obstacle_mask = HardGeometryMask(obstacle.geometry) >> pressure
velocity = StaggeredGrid(0, extrapolation.ZERO, **DOMAIN)
obstacle_mask = CenteredGrid(HardGeometryMask(obstacle.geometry), extrapolation.BOUNDARY, **DOMAIN)
pressure = None

for _ in view(velocity, obstacle_mask, play=True).range():
for _ in view(velocity, obstacle_mask, play=True, namespace=globals()).range():
obstacle = move_obstacle(obstacle)
velocity = advect.mac_cormack(velocity, velocity, DT)
velocity, pressure = fluid.make_incompressible(velocity, DOMAIN, (obstacle,), math.Solve('CG-adaptive', 1e-5, 1e-5, 10000, x0=pressure))
obstacle_mask = HardGeometryMask(obstacle.geometry) >> pressure
velocity, pressure = fluid.make_incompressible(velocity, (obstacle,))
obstacle_mask = HardGeometryMask(obstacle.geometry) @ pressure
13 changes: 6 additions & 7 deletions demos/network_training_pytorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
net = u_net(2, 2)
optimizer = optim.Adam(net.parameters(), lr=1e-3)

DOMAIN = Domain(x=64, y=64)
prediction = DOMAIN.vector_grid(0)
prediction_div = DOMAIN.scalar_grid(0)
viewer = view(play=False)
prediction = CenteredGrid((0, 0), extrapolation.BOUNDARY, x=64, y=64)
prediction_div = CenteredGrid(0, 0, x=64, y=64)
viewer = view(play=False, namespace=globals(), select='batch')

for step in viewer.range(100):
# Load or generate training data
data = DOMAIN.vector_grid(Noise(batch=8, vector=2))
data = CenteredGrid(Noise(batch(batch=8), channel(vector=2)), extrapolation.BOUNDARY, x=64, y=64)
# Initialize optimizer
optimizer.zero_grad()
# Prediction
Expand All @@ -25,9 +24,9 @@
prediction_div = field.divergence(prediction)
# Define loss
loss = field.l2_loss(prediction_div) + field.l2_loss(prediction - data)
viewer.log_scalars(loss=loss, div=field.mean(abs(prediction_div)), distance=math.vec_abs(field.mean(abs(prediction - data))))
viewer.log_scalars(loss=loss.mean, div=field.mean(abs(prediction_div)).mean, distance=math.vec_abs(field.mean(abs(prediction - data))).mean)
# Compute gradients and update weights
loss.native().backward()
loss.mean.backward()
optimizer.step()

torch.save(net.state_dict(), 'torch_net.pth')
Expand Down
Loading

0 comments on commit 4a85f8a

Please sign in to comment.