POV-Ray : Newsgroups : povray.off-topic : Liquid Physics Server Time
1 Oct 2024 18:30:49 EDT (-0400)
  Liquid Physics (Message 8 to 17 of 37)  
<<< Previous 7 Messages Goto Latest 10 Messages Next 10 Messages >>>
From: scott
Subject: Re: Liquid Physics
Date: 31 Mar 2008 10:38:17
Message: <47f10569$1@news.povray.org>
> Ah, right, so it's overloaded but that's not shown in the article?

Yup, it is in the source code however.

> (BTW, I thought printf() was only for C, not C++?)

Well it is for C, but anything you can do in C you can do in C++.  Not that 
it's a good idea though...

> Did the Haskell version look "readable"?

Not really, I tried to add some to your code to show what I meant with the 
other evaluate function, but I couldn't work out what was going on!

> One of the nice things about Haskell is that you can't make that mistake. 
> Since you never update anything in-place, you can't accidentally destroy a 
> result you need to use again later, and by left wondering where this 
> "wrong" data is coming from.

Seems logical, but without thinking about it too much, how would you keep 
track of the state of the system while it's running?  Are you basically 
saying that variables are write-once in Haskell?  SO I couldn't do something 
like:

currentState = Integrate(currentState);

I would need to instead do:

frame++
state[frame] = Integrate( state[frame-1] )


Post a reply to this message

From: Invisible
Subject: Re: Liquid Physics
Date: 31 Mar 2008 10:56:14
Message: <47f1099e@news.povray.org>
>> Ah, right, so it's overloaded but that's not shown in the article?
> 
> Yup, it is in the source code however.

Ah, OK. I thought it was just a typo or something...

>> (BTW, I thought printf() was only for C, not C++?)
> 
> Well it is for C, but anything you can do in C you can do in C++.  Not 
> that it's a good idea though...

Ah yes, C++ is a superset of C. But I was thinking (hoping?) that in C++ 
you have something less hellish than prinf() to play with...

>> Did the Haskell version look "readable"?
> 
> Not really, I tried to add some to your code to show what I meant with 
> the other evaluate function, but I couldn't work out what was going on!

No, Haskell doesn't support overloaded functions, so you'd have to make 
some moderately radical (oxymoron?!) alterations to make it work.

> Seems logical, but without thinking about it too much, how would you 
> keep track of the state of the system while it's running?

Ah, grasshopper, you are still thinking in imperative terms. ;-)

A true Haskeller would define a notionally infinite list which 
notionally contains the entire state of the system at every future point 
in time. Examining the elements of this list causes the integration to 
actually be performed, and assuming you let go of the start of the list, 
the GC will delete each state after it has been calculated.

In other words, define an infinite list, and then map some monadic 
function over it which displays the state of the screen or dumps it to a 
file or otherwise "does" something with it. And the result will be more 
or less like this C++ example.

Now, if you want to use *user input* to alter the integration as it 
proceeds, then you must take an somewhat different approach. (See below.)

> Are you 
> basically saying that variables are write-once in Haskell?

You *could* put it that way. It would be more correct to say that a 
variable is *defined* as having a specific value in Haskell. Consider this:

   let node1 = Node {next = node2, prev = node4}
       node2 = Node {next = node3, prev = node1}
       node3 = Node {next = node4, prev = node2}
       node4 = Node {next = node1, prev = node3}
   in ...

As you can see, it's not so much that you can only assign once to a 
variable, it's that you are *defining* what a variable's value *is*. The 
compiler will chase dependencies - even circular ones - and resolve them 
for you, figuring out what order to initialise everything and so forth.

(You can't really speak of assigning several values to the same variable 
"one after the other" because [pure] Haskell function don't have any 
notion of "time".)

> So I couldn't do something like:
> 
> currentState = Integrate(currentState);

No, you couldn't.

You could, however, write

   animate state = do
     dispaly state
     let new_state = integrate state
     animate new_state

which will display the initial state, call your integrate function to 
compute the next state, display that state, and so forth. (This method 
*does* allow you to accept user input and do something different based 
on what the user input is.)

A third way would be to use explicitly mutable state - something like this:

   state <- takeMVar my_state
   writeMVar (integrate state)

But there's no particular advantage in doing that here, and it just 
takes more typing to write out.

-- 
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*


Post a reply to this message

From: Nicolas Alvarez
Subject: Re: Liquid Physics
Date: 31 Mar 2008 11:13:14
Message: <47f10d9a$1@news.povray.org>
Invisible escribió:
>>> (BTW, I thought printf() was only for C, not C++?)
>>
>> Well it is for C, but anything you can do in C you can do in C++.  Not 
>> that it's a good idea though...
> 
> Ah yes, C++ is a superset of C. But I was thinking (hoping?) that in C++ 
> you have something less hellish than prinf() to play with...

You do.

C:
printf("Number is: %d\n", somenumber);

...and be careful types match between format string and arguments 
passed, and particularly that you passed *enough* arguments

C++:
cout << "Number is: " << somenumber << "\n";

...and there is no duplicated information that could get out-of-sync 
(format and arguments).

It's just you can still use the C way if you want to.


Post a reply to this message

From: Orchid XP v8
Subject: Re: Liquid Physics
Date: 31 Mar 2008 14:41:30
Message: <47f13e6a@news.povray.org>
>> Ah yes, C++ is a superset of C. But I was thinking (hoping?) that in 
>> C++ you have something less hellish than prinf() to play with...
> 
> You do.
> 
> C:
> printf("Number is: %d\n", somenumber);
> 
> ...and be careful types match between format string and arguments 
> passed, and particularly that you passed *enough* arguments
> 
> C++:
> cout << "Number is: " << somenumber << "\n";
> 
> ...and there is no duplicated information that could get out-of-sync 
> (format and arguments).
> 
> It's just you can still use the C way if you want to.

That does indeed seem like at least *some* improvement...

(In C, almost every single time anybody touched printf(), the compiler 
would utter something about "suspicious pointer conversion". To this day 
nobody knows what that actually means... Even the tutors didn't seem to 
know.)


Post a reply to this message

From: Kevin Wampler
Subject: Re: Liquid Physics
Date: 31 Mar 2008 15:46:34
Message: <47f14daa$1@news.povray.org>
Assuming that you're writing this simulation for graphics purposes 
rather than physical simulation where accuracy is a major concern 
there's some other methods that you might consider.

First, a bit of terminology.  There's two main paradigms I'm aware of 
which are used to simulate fluids: Eulerian and Lagrangian.  In the 
Eulerian approach you consider what's happening in a small fixed piece 
of space and look at how that changes based on the pieces of space 
around it.  In the Lagrangian approach, on the other hand, you consider 
what happens to pieces of the fluid as they move around.  The approach 
you describe is then a Lagrangian approach.

As you've noticed, stability can be a problem when you integrate out 
these sorts of equations to determine how the fluid should move over 
time.  Changing the integration mechanism (such as to a Runge-Kutta 
integrator) or decreasing the time-step can help this to a degree, but 
there exist fluid simulation methods which are unconditionally stable. 
That is, the simulation will never `blow up' no matter what you do, even 
with very large time steps in the simulation.

Probably the simplest such technique is the "Stable Fluids" algorithm 
invented by Jos Stam.  It uses some concepts from both the Eulerian and 
Lagrangian viewpoints, and is this referred to as a "semi-Lagrangian" 
method.  There's a very easy to ready paper on the algorithm that even 
gives some code for how to implement it:

http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf

The algorithm itself should be also pretty easy to implement, and is 
also useful in a educational sense as it uses some interesting 
techniques (such as, iirc, explicitly projecting the flow onto a 
divergence-free space to achieve an incompressible flow).

There are also some fully particle based fluid simulation methods which 
are more similar to yours in their apploach, but I'm not aware of any 
which are as simple to implement as the stable fluids algorithm.  You 
can check out some early examples:

http://graphics.ethz.ch/Downloads/Publications/Papers/2003/mue03b/p_Mue03b.pdf
http://www.cs.utah.edu/vissim/papers/ParticleFluid/ParticleFluidsLowRes.pdf

Or see a more recent paper on such a method at:

http://graphics.stanford.edu/~barta/research/adams-sig07.pdf

I suspect implementing one of these methods (or one of the newer 
Eulerian / semi-Lagrangian ones) would be more work than you care to 
spend, but I think stable fluids should be pretty reasonable to do 
without a crazy amount of work.

Note, however, that all the stuff I mentioned is based on simulating the 
Navier-Stokes equations.  If you're only looking for something that 
looks sortof-kinda like a fluid rather than behaving as a proper fluid 
then there's probably all sorts of shortcuts and hacks you can do, and 
in this case I agree with scott's suggestions so I'd start playing 
around with using a better integrator and/or adding some sort of 
artificial dampening.




Chambers wrote:
> So, just for fun I'm writing a quick liquid simulation.  Because I like 
> liquids, and I'm learning C#, that's why :)
> 
> Anyway, I'm running into some problems with the simulation.  Namely, a 
> certain instability.  Here's what I'm doing:
> 
> For each particle, I loop through all the other particles (i=0..count-1, 
> j=i+1..count), and check the distance between them (I use some 
> additional bounding to speed things up, but that's not important to my 
> problem here).  I compute the forces the two particles place on each 
> other, and then add them to each particles' total "force" value.
> 
> Once I'm done computing interactions, I loop through all the particles, 
> add the force to the velocity, and reset the force to zero.
> 
> The way I see it (and granted, I've never written a liquid sim before), 
> there are two forces I need to deal with, pertaining to two stages that 
> occur when liquid particles collide:
> 
> 1) When the particles are farther apart, they attract each other.  This 
> weak attraction would be the driving force behind surface tension.
> 
> 2) When the particles are closer together, they push against each other. 
>  This force must be much stronger than the first in order to maintain 
> some volume.
> 
> Now, so far I'm only actually implementing the second force above. 
> However, what I see is that the particles tend to settle pretty quickly 
> to the floor, and then they clump together.  Apparently, the particles 
> on the outside "force" the particles in the middle of the clump closer 
> and closer, until they suddenly explode and things go flying everywhere.
> 
> I *think* the problem has to do with conservation of momentum.  I 
> naively dealt with that by halving the force applied to each particle in 
> a pair (one half of the force gets applied to each), but that's not 
> quite right, is it?  The momentum of each particle shouldn't simply be 
> split evenly with every individual particle it collides with... rather, 
> I'll need to collect a list of interactions, and split this particle's 
> momentum between *all* of those other particles...
> 
> If anyone else here has dealt with similar issues, I'd appreciate 
> knowing if I'm on the right track...
>


Post a reply to this message

From: Chambers
Subject: Re: Liquid Physics
Date: 1 Apr 2008 00:27:05
Message: <47f1c7a9@news.povray.org>
scott wrote:
> The only difference is that in the evaluate function without the 
> supplied derivative, you don't need to update the state, simply return 
> the derivative at time t using the initial state.
> 
>>       b = evaluate (v,p) t (dt/2) a
>>       c = evaluate (v,p) t (dt/2) b
>>       d = evaluate (v,p) t dt     c
>>       dv = (fst a + 2 * (fst b + fst c) + fst d) / 6
>>       dp = (snd a + 2 * (snd b + snd c) + snd d) / 6
>>     in (v + dt*dv, p + dt*dp)

...

> 5) Take a weighted average of all the derivatives you just caluclated, 
> a-d, this gives the most accurate derivative.
> 
> 6) Use this weighted average to actually advance your state by dt using 
> normal Euler integration.

Here's a question: The derivative is based on the forces applied to the 
current particle.  The only way these forces change is when a particle 
collides with other particles (or ceases to collide).

So, for each of the above, am I to run a full collision detection?

-- 
...Ben Chambers
www.pacificwebguy.com


Post a reply to this message

From: Chambers
Subject: Re: Liquid Physics
Date: 1 Apr 2008 00:32:47
Message: <47f1c8ff@news.povray.org>
Invisible wrote:
> Ah, grasshopper, you are still thinking in imperative terms. ;-)

BTW, that grasshopper thing... does that come from the old TV show "Kung 
Fu"?  I could have sworn I saw it in a movie, though.  In fact, I asked 
at a couple local video stores what movie it was, and nobody knew... :(

-- 
...Ben Chambers
www.pacificwebguy.com


Post a reply to this message

From: Chambers
Subject: Re: Liquid Physics
Date: 1 Apr 2008 01:04:59
Message: <47f1d08b@news.povray.org>
scott wrote:
> Seems logical, but without thinking about it too much, how would you 
> keep track of the state of the system while it's running?  Are you 
> basically saying that variables are write-once in Haskell?

AFAIK, yes, since they're defined as immutable.

In other words, Haskell doesn't have variables, only constants.

-- 
...Ben Chambers
www.pacificwebguy.com


Post a reply to this message

From: scott
Subject: Re: Liquid Physics
Date: 1 Apr 2008 01:50:08
Message: <47f1db20$1@news.povray.org>
> A true Haskeller would define a notionally infinite list which notionally 
> contains the entire state of the system at every future point in time. 
> Examining the elements of this list causes the integration to actually be 
> performed, and assuming you let go of the start of the list, the GC will 
> delete each state after it has been calculated.

What's that like on performance though?  What would it be like if your state 
block consisted of hundreds of 4D vectors, and the system was being updated 
at 1000 Hz?  Would the GC be noticeable?

> (You can't really speak of assigning several values to the same variable 
> "one after the other" because [pure] Haskell function don't have any 
> notion of "time".)

Ah ok I see, so it's like you every variable is defined as "const" in the 
C++ sense, you can't modify it.

>   animate state = do
>     dispaly state
>     let new_state = integrate state
>     animate new_state

I assume that the compiler is clever enough to not keep on the stack any 
data that is not needed, ie old versions of state that are no longer needed. 
If you attempted that method in C++ you would fill up the RAM very quickly 
;-)


Post a reply to this message

From: scott
Subject: Re: Liquid Physics
Date: 1 Apr 2008 01:57:49
Message: <47f1dced$1@news.povray.org>
> Here's a question: The derivative is based on the forces applied to the 
> current particle.  The only way these forces change is when a particle 
> collides with other particles (or ceases to collide).
>
> So, for each of the above, am I to run a full collision detection?

Yes, inside the "acceleration" function, which is called from each 
"evaluate" you put your code for calculating the acceleration.  This, in 
your case, would presumably call the routine to check for collisions and 
work out the forces for all the particles.

Note that you need to put all the positions and velocities for every 
particle inside some state block and evaluate them in parallel using this 
algorithm - you can't do one at a time it doesn't work like that.

You may say that this is 4x as much work per integration step, but it will 
allow your time-step to be far more than 4x bigger and still be completely 
stable.


Post a reply to this message

<<< Previous 7 Messages Goto Latest 10 Messages Next 10 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.