FOX/ObjCryst++  1.10.X (development)
The Design of ObjCryst++

If you have any comment or suggestion about this you can drop me an email.

Also read Why is Object Oriented Programming good for Crystallographic Computing ?.

Overview of the Library (Crystallographic classes)

A Scatterer is the common denominator for any scattering object: all it includes is a function which gives a list of positions in fractional coordinates, with a scattering power associated to each position (Scatterer::GetScatteringComponentList()). It also includes a few functions for the display (3D) of the object.
All Scatterer can be derived from such a class: Atom, ZScatterer. The advantage of using inheritance is that all derived classes must re-use the functions declared in the base class, so that any function which knows what a generic 'Scatterer' object (but does not know what an Atom or ZScatterer is) can still use any derived class.
Further development example: currently there is no 'rigid body' object: if any developper wants to create such an object, he just needs to make sure he rewrites the function GetScatteringComponentList(). Thus without any modification, the Crystal and ScatteringData classes will automatically be able to use this new object... since this RigidBody object is derived from the Scatterer class (which Crystal and ScatteringData know).
This class can compute the scattering, resonant and thermic factor for any ScatteringData object (eg a list of reflections with some metric information). The three member function GetScatteringFactor(), GetTemperatureFactor(),GetResonantScattFactReal(), and GetResonantScattFactImag() can be used to get the corresponding factors for a list of reflections (and wavelengths for resonant terms) in a ScatteringData object.
The base class is designed to handle anisotropic factors: for this the index of the symetric position in the Spacegroup must be given.
Further development example: currently only the interface to handle anisotropy has been written, but no code or derived class. But no matter what kind of anisotropy is added, it will always work with the base class interface.
Note: why always use a ScatteringData object as input (to compute scattering factors, for example), rather than, say, a list of HKL or a list of sin(theta/lambda) ? The first reason is that from a ScatteringData you can extract all these hkl's and sin(theta)/lambda. The second reason is that with such an approach, no matter how complex the derived classes are, you can always use the same interface (for isotropic thermic factors as well as anharmonic !), so that any function written with only the knowledge of the base class can use any derived class.
a Crystal is a unit cell with an associated SpaceGroup with a list of Scatterer.
The ScatteringData is a base class which is basically a list of reflections with the ability to compute structure factors. The DiffractionDataSingleCrystal and PowderPatternDiffraction classes are derived from it.

Optimization design

The RefinableObj is the base class for almost all objects in the library. The advantage of such a design (see the inheritance tree on the ObjCryst::RefinableObj page) is that when you design an algorithm, you do not need to know what kind of object is refined. All you need to know is (i) how many parameters there are (ii) how to move these parameters (iii) how to access one or several 'cost function' for the optimized object (to characterize 'how good' the current configuration is). Indeed, the global optimization class (for simulated annealing and parallel tempering) does not include any of the crystallographic headers, and yet it can refine the crystal structures...
This design does not mean that only 'stupid' algorithms can be handled. Since the 'random moves' are handled by the refined objects, this 'random moves' can be very non-random (for example in the Crystal object, permutation of Scatterers is made from time to time...).
What if you are not interested in the RefinableObj functionnality ? You can simply ignore it, it will not do any harm. You can do other crystallographic work by 'forgetting' that a Crystal, PowderPattern, Atom is a RefinableObj. Even new derived objects do not have to declare their parameters as 'RefinablePar', if you want to save some memory.
what is currently lacking in RefinableObj is (i) a way to set constraints/restraints (currently there are only limits for individual parameters), the ability to have arrays of RefinablePar (to handle large structure without a significant memory penalty), and a design for analytical derivatives (well I've got a few ideas about this but this is not a priority yet...).