The Differential Geometry module for SymPy already supports some interesting basic operations. However, it would be appropriate to describe its structure before giving any examples.

First of all, there are the `Manifold`

and `Patch`

classes which are
just placeholders. They contain all the coordinate charts that are
defined on the patch and do not provide, for instance, any topological
information. This leads us to the `CoordSystem`

class which contains all
the coordinate transformation logic. For example, if I want to define
the $ \mathbb{R}^2 $ euclidean manifold together with the polar
and Cartesian coordinate systems I would do:

```
R2 = Manifold('R^2', 2)
# Patch and coordinate systems.
R2_origin = Patch('R^2_o', R2)
R2_r = CoordSystem('R^2_r', R2_origin)
R2_p = CoordSystem('R^2_p', R2_origin)
# Connecting the coordinate charts.
x, y, r, theta = [Dummy(s) for s in ['x', 'y', 'r', 'theta']]
R2_r.connect_to(R2_p, [x, y],
[sqrt(x**2 + y**2), atan2(y, x)],
inverse=False, fill_in_gaps=False)
R2_p.connect_to(R2_r, [r, theta],
[r*cos(theta), r*sin(theta)],
inverse=False, fill_in_gaps=False)
```

All following examples will be about the $ \mathbb{R}^2 $
manifold which is already implemented in the code for the module. Also,
notice the use of the `inverse`

and `fill_in_gaps`

flags. When they are
set to `True`

the `CoordSystem`

classes try to automatically deduce the
inverse transformations using SymPy’s `solve`

function.

Now that we have a manifold we would like to create some fields on it
and define some points that belong to the manifold. The points are
implemented in the `Point`

class. You need to specify some coordinates
when you define the point, however after that the object is completely coordinate-system-idependent.

```
# You need to specify coordinates in some coordinate system
p = Point(R2_p, [r0, theta0])
```

Then one can define fields. `ScalarField`

takes points to real numbers
and `VectorField`

is an operator on `ScalarField`

taking a scalar field
to another scalar field by applying a directional derivative. For
example, here `x`

and `y`

are the scalar fields taking a point and
returning it’s coordinate and `d_dx`

and `d_dy`

are the vector fields
\( \frac{\partial}{\partial x}\) and \(
\frac{\partial}{\partial y}\). `R2_r`

is the Cartesian coordinate
system and `R2_p`

is the polar one.

```
R2_r.x(p) == r0*cos(theta0)
# R2_r.d_dx(R2_r.x) is a also scalar field
R2_r.d_dx(R2_r.x)(p) == 1
```

Looking at how can these fields be defined:

```
# For a ScalarField you provide the transformation in some coordinate
system
R2_r.x = ScalarField(R2_r, [x0, y0], x0)
# / | ^-------- the result
# the coord system the coordinates
# For a VectorField you provide the components in some coordinate
system
R2_r.d_dx = VectorField(R2_r, [x0, y0], [1, 0])
# / | ^-------- the components
# the coord system the coordinates
```

Obviously one can define much more interesting fields. For instance the potential due to a point charge at the origin is:

```
potential = ScalarField(R2_p, [r0, thata0], -1/r0)
# And to reiterate, the definition does not limit you
# to use it only in this coordinate system. For instance:
potential(R2_r.point([x0, y0])) == 1/sqrt(x0**2 + y0**2)
```

However there is another more intuitive way to do it:

```
# R2_p.r is the scalar field that takes a point and returns the r
coordinate
potential2 = 1/R2_p.r
potential2(R2_r.point([x0, y0])) == 1/sqrt(x0**2 + y0**2))
```

And this new object `potential2`

is not an instance of `ScalarField`

. It
is actually a normal SymPy expression tree that contains a `ScalarField`

somewhere in its leafs (namely in this case it is `Pow(R2_p.r, -1)`

).
However, due to the change to one of the base classes of SymPy that I
did in this pull request it is now possible for such tree to be a
python callable, by recursively applying the argument to each callable
leaf in the tree. This change is still debated and it may be reverted.

Vector fields can also be build in this manner. However, they pose a problem. What happens when you multiply a vector field and a scalar field? This operation should give another vector field. And here is a possible problem with the approach of recursively callable expressions trees:

```
# Naively this operation will call a scalar field on
# another scalar field which is nonsense:
(R2_r.x * R2_r.d_dx)(R2_r.x) == R2_r.x(R2_r.x) *
R2_r.d_dx(R2_r.x)
# nonsense----^
```

The current solution is for `scalar_field(not_point)`

to return the
callable itself. Thus we have:

```
(R2_r.x * R2_r.d_dx)(R2_r.x) == R2_r.x * R2_r.d_dx(R2_r.x)
#\________________/ \______/
\_______________________/
# vector field ^---scalar fields---^
```

This way there is no need for complicated logic in
`__mul__ nor is there need for addition subclasses of Expr`

in order to
accommodate this behavior.

There is not much more to be said about the structure of the module. There are some other nice things already implemented like integral curves, however I will discuss these in a later post.

Among the things that should be done at some point:

- Should vector fields be callable on points? If yes, what the result should be? An abstract vector, a tuple of coordinates in a certain coordinate system, something else?
- There are many expressions generated by this code that are not simple enough. I should work on the simplification routines and on the differential geometry module itself in order to get more canonical expressions.
- The last point is also valid about the solvers: some coordinate transformations are too complicated for the solvers to find the inverse transformation.
`Manifold`

and`Patch`

have`name`

attributes. Are these necessary? What is the role of`name`

attributes in SymPy besides printing?- Start using
`Lambda`

where applicable. - Follow better the class structure of SymPy.