Wednesday, February 23, 2011

Physics Library: Doing Physics in .net Part II

Having just started getting into C#, I can already say that it’s a pleasure to work with – especially when used with its tightly coupled IDE, Visual Studio. Of course, being a physics/astronomy/computer science geek, I got straight to work doing some projectile physics in C# to see how it turned out. To get myself started on this blog, I’m going to write a couple of posts about doing physics in .net:

In part 1 I went over the benefits of F#’s units of measure over doing dimensionless math with C# (and most other languages). But if your project isn’t already in F#, it’s a little hard to benefit from it. Luckily, it’s easy to work between languages in .net managed code. You can build the F# library using the standalone installer and Visual Studio 2010 Shell. This is the F# code I left off with:


namespace FsPhysicsDemo

module Physics =
    [<Measure>] type m
    [<Measure>] type s

    let xf (xi : float<m>) (v : float<m/s>) (a : float<m/s^2>) (t : float<s>) =
        xi + v * t + 0.5 * a * t * t

    let v (a : float<m/s^2>) (t : float<s>) =
        a * t

    let deltaV (vi : float<m/s>) (vf : float<m/s>) =
        vf - vi

This works fine as a small library, but it’s a little annoying to work with. When the math was just done in C#, we had the benefit of it being object oriented—the parameters of the equation were all stored in the object—whereas with this code each parameter needs to be passed to a function. What makes it even worse is that we already have an object oriented projectile in C#, and then we go ahead and throw away the strengths of that setup:

class Projectile
    public const double g = -9.80;
    public double xi;
    public double yi;
    public double vxi;
    public double vyi;

    public double xf(double t)
        return Physics.xf(xi, vxi, 0.0, t);
Doesn’t seem worth the effort. So we’re going to make our library a little more complex, but first I’ll go over the structure of the library, so we can focus on the fun stuff later on.
  • The #light compiler directive: I won’t really go into this bit; it simplifies the syntax rules a little and runs a “lighter” version of the language—that is, without some unnecessary features that we won’t be using.
  • Namespaces: In order to be accessible from other C# languages, each compilation unit needs to have a namespace. They function in F# exactly like they do in C#. For this demo, all F# code will be unceremoniously lumped into the FsPhysicsDemo namespace.
  • Modules: Only types can appear on their own in a namespace. This is similar to C#, where only classes, structs, enums, etc. can appear in a namespace. A module to F# is basically what a static class is to C#. However, in F# they act as a “sub-namespace” – they can be “opened” with the equivalent of a using statement from F# (to C# it’s just a static class). All variables and functions must appear in a module.
  • Compilation/definition order: This one is important, and probably unfamiliar to someone used to the dynamic nature of .net. F# source files are compiled in a specific order, and the contents of A.fs cannot be known to B.fs unless A.fs is compiled first. Similarly, any type, function, module, etc. can only be used after it is defined in the source.
Since we’re working with units of measure, I’ll point out a couple of things about them as well:
  • They live until compile-time only. This is the reason we can use them from other .net languages which don’t understand what they are. When the source code is compiled, all values are stripped of their units and appear as dimensionless numbers.
  • A unit is a “type”, and must be declared as such. You can see this in the example F# code above: an F# type can be like a C# class. Sort of. Okay, bad example: but we have to declare units before we can attach them to numbers.
  • .Net library functions don’t know about, care about, or deal with them. This makes life a little more difficult when we need to use the Math library, since even though we can pass values with units into them, the return value is always dimensionless.
I’ll start off this library with a new source file, Units.fs. It’s going to contain our unit definitions, and because of the last point about units of measure about .net library functions, a few unit-safe functions:

namespace FsPhysicsDemo

[<Measure>] type m;
[<Measure>] type s;

module UnitSafeFunctions =
    open Core.LanguagePrimitives;
    open System;

    let sqr (x : float<'u>) = x * x

    let sqrt (x : float<'u ^ 2>) =
        let v = Math.Sqrt (float x)
        FloatWithMeasure<'u> v

Here you can see that we’ve placed our units into the FsPhysicsDemo namespace, not inside a module. Now (as you’d expect) any code in the same namespace can use the units. For this reason, this will be the first source file to be compiled.

We’ve also defined a UnitSafeFunctions module, which contains some functions that don’t clobber the units attached to any values we may need the square root of. We’ve opened the LanguagePrimitives module which provides the FloatWithMeasure function, and the System namespace, which is where we get the .net Math library from. The sqr function doesn’t do anything special, I just defined it so that we don’t have to repeat the term we want squared (which could be a pain if we want to square an entire expression). The more interesting function here is sqrt, which has:

  • Generic units, and unit components. In the function signature, you see this float<'u ^ 2> business. 'u is the generic unit – and we can also specify other parts (for example, we could have written float<'u/s>, specifying that we want something per second). In this case we’ve said that the function takes something squared.
  • Unit typecasting: Of course, it’s just regular typecasting. But the unit is part of the type, so we can cast x into a dimensionless float before we work with it. This is important when calling .net library functions on a physical unit: if you don’t explicitly tell F# that you want this value without the unit, it’ll tell you that the function call makes the function less generic. Since Math.Sqrt() would be taking a float<'u> and changing it into a float<1>, it will only allow 'u to be dimensionless.
  • Re-applying the correct unit. Since F# doesn’t allow you to use units on non-zero constants (e.g. multiplying v by 1.0<'u>) since that would break the type-system, we can use the FloatWithMeasure generic function which boxes a dimensionless float into a float with a unit.

Edit Apr. 06 2011: the sqrt function isn't necessary, F# has a built in sqrt function which is unit-safe, that I wasn't aware of. I'm leaving it here because it's a quick example of a few important features of F#'s units of measure, but you don't need to add this to your own code.

I'll keep the sqr function also – while it doesn't demonstrate anything the sqrt function doesn't already, it's a nice shorthand for x*x when x is a long expression.

Now that we’ve got a fundamental structure to build our library with, and some experience working with units of measure, it’s time to flesh it out a little in Part III.

No comments:

Post a Comment