Felix
elasticluster
Felix makes sense to be run on the clusters as it is fortran mpi parallelized.
Here is described how to setup and start a cluster with elasticluster.
elasticluster is a framework to setup a cluster similar to starcluster.
installation
It must be installed from source on the machines which will create the clusters.
cd elasticluster
git clone https://github.com/elasticluster/elasticluster.git src
cd src
pip install -e .
configuration
Then it must be configured as followed in the docs. The configuration files are used to placed in .elasticluster/config.d and define :
- the cloud which is
openstackat stfc - the login credentials to the nodes which essentially defines the ssh keys used to access the nodes
- the cluster setup which uses
ansibleto automate the nodes setup andslurmas the grid engine. - the cluster section which defines different cluster templates for specific clusters which will be used with the command
elasticluster startcluster <cluster_template>. THe templates can be seen withelasticluster list-templates
It is important to source the openstack login credentials source CCP4-ED-openrc.sh before using elasticluster.
cloud section
[cloud/openstack]
provider=openstack
login It is simpler to create the key pairs from the stfcloud openstack interface and place them into your .ssh folder :
[login/<user_id>]
image_user=<username>
image_sudo=True
user_key_name=test1 #name of the key pair
user_key_private=~/.ssh/test1.pem #key pair private
user_key_public=~/.ssh/test1.pub #publickey
setup
[setup/gridengine-ubuntu]
provider=ansible
frontend_groups=gridengine_master,ganglia_master
compute_groups=gridengine_worker,ganglia_monitor
# install NIS/YP to manage cluster users
global_var_multiuser_cluster=yes
global_var_ansible_python_interpreter=/usr/bin/python2.7
# for Ganglia, selinux needs to be disabled
disable_selinux=yes
[setup/slurm-ubuntu]
provider=ansible
frontend_groups=slurm_master,ganglia_master,ganglia_monitor
compute_groups=slurm_worker,ganglia_monitor
# install NIS/YP to manage cluster users
global_var_multiuser_cluster=yes
# ansible requires python2, which is default on SL7
global_var_ansible_python_interpreter=/usr/bin/python2.7
# allow reboot
global_allow_reboot=yes
# for Ganglia, selinux needs to be disabled
disable_selinux=yes
cluster
[cluster/ccp4cluster]
cloud=openstack
login=<user_id>
setup=gridengine-sl7
frontend_nodes=1
compute_nodes=2
ssh_to=frontend
network_ids=b283fd53-a801-403a-8b6d-e6590d429774
image_id=93b61781-e8ab-4f3d-8c16-2bacef60c623
[cluster/ccp4cluster/frontend]
flavor=l3.tiny
security_group=default
[cluster/ccp4cluster/compute]
security_group=default
flavor=l3.tiny
starting a cluster
a cluster can now be started with :
elasticluster ccp4cluster -n <cluster_name>
and to run an ansible playbook to install things on each nodes :
elasticluster setup <cluster_name> -- --playbook.yaml
a playbook looks like :
installation and compilation
dependencies
general compilers and libraries
sudo apt-get install gfortran mpi libopenmpi-dev libblas-dev liblapack-dev
fft library
mkdir fftw
cd fftw
wget http://www.fftw.org/fftw-3.3.9.tar.gz
tar -xvzf fftw-3.3.9.tar.gz
cd fftw-3.3.9
./configure --enable-shared
make
sudo make install
compilation
getting the continousED repo
git@github.com:WarwickMicroscopy/Felix.git
git checkout cRED
change the compiler in src/user.mk to gfortran PLATFORM=OPT64NGNU.
now the simple make should work.
running an example
code walkthrough
Flow
//felixrefine.f90
CALL ReadInpFile(IErr)
CALL read_cif(IErr)
CALL ReadHklFile(IErr)
IF (ISimFLAG.EQ.0) CALL ReadExperimentalImages(IErr)
CALL SetScatteringFactors(IScatterFactorMethodFLAG,IErr)
CALL ReciprocalLattice(IErr)
CALL UniqueAtomPositions(IErr)
// Count the no. of reflections INhkl that make up the pool of g-vectors counted according to their magnitudes and including symmetry equivalents
CALL HKLCount(IHKLMAXValue,RZDirC,INhkl,RHOLZAcceptanceAngle,IErr) // sets INhkl
CALL HKLMake(IHKLMAXValue,RZDirC,RHOLZAcceptanceAngle,IErr)
CALL HKLSort(Rhkl,INhkl,IErr)
CALL HKLList(IErr) //Assign numbers to different reflections -> IOutputReflections, INoOfLacbedPatterns
CALL gVectors(IErr)
CALL StructureFactorInitialisation(IErr)
/*Refinement parameters*/
CALL Simulate(IErr)
IF (ISimFLAG.EQ.1) THEN
DO ind = 1,IThicknessCount
CALL WriteIterationOutput(Iter,ind,IErr)
ELSE
CALL FigureOfMeritAndThickness(Iter,IThicknessIndex,IErr)
SELECT CASE(IRefineMethodFLAG)
CASE(1) CALL SimplexRefinement
Simulation
Parallelization
//felix/felixrefine.f90:
/* MPI initialization*/
CALL MPI_Init(IErr4)
IF(l_alert(INT(REAL(IErr4)),"felixrefine","MPI_Init")) CALL abort
CALL MPI_Comm_rank(MPI_COMM_WORLD,my_rank,IErr4) ! get rank of the current process
IF(l_alert(INT(REAL(IErr4)),"felixrefine","MPI_Comm_rank")) CALL abort
CALL MPI_Comm_size(MPI_COMM_WORLD,p,IErr4) ! get size of the current communicator
IF(l_alert(INT(REAL(IErr4)),"felixrefine","MPI_Comm_size")) CALL abort
CALL message(LS,"total number of MPI ranks ", p, ", screen messages via rank", my_rank)
/* The pixels to be calculated by this core*/
ILocalPixelCountMin= (IPixelTotal*(my_rank)/p)+1
ILocalPixelCountMax= (IPixelTotal*(my_rank+1)/p)
Simulate
//refinement_control.f90
SUBROUTINE Simulate(IErr)
CALL message(LS,"Bloch wave calculation...")
DO knd = ILocalPixelCountMin,ILocalPixelCountMax,1
jnd = IPixelLocations(knd,1)
ind = IPixelLocations(knd,2)
CALL BlochCoefficientCalculation(ind,jnd,knd,ILocalPixelCountMin)
CALL MPI_GATHERV(RIndividualReflections,RSimulatedPatterns)
// 2*IPixelCount^2 diagonalizations
DO jnd = 1,2*IPixelCount
DO knd = 1,2*IPixelCount
ind = ind+1
RImageSimi(jnd,knd,:,:) = RSimulatedPatterns(:,:,ind)
Output images
//write_output_mod.f90
DO ind = 1,INoOfLacbedPatterns
RImageToWrite = RImageSimi(:,:,ind,IThicknessIndex)
filename = SChemicalFormula "_" hString,kString,lString ".bin"
DO jnd = 1,2*IPixelCount
WRITE(IChOutWIImage,rec=jnd) RImageToWrite(jnd,:)
Bloch calculation
def
CUgSgMatrix: matrix to diagonalizeCEigenValues,CEigenVectors: eigen values and eigen vectorsRIndividualReflections: intensitiesIPixelNumber=kndIPixelCount: number of pixel defined by user
main
//bloch_mod.f90
SUBROUTINE BlochCoefficientCalculation
RTiltedK(1)= (REAL(IYPixelIndex,RKIND)-REAL(IPixelCount,RKIND)-0.5)*RDeltaK
// y-position in k-space
RTiltedK(2)= (REAL(IXPixelIndex,RKIND)-REAL(IPixelCount,RKIND)-0.5)*RDeltaK
RTiltedK(3)= SQRT(RBigK**2 - RTiltedK(1)**2 - RTiltedK(2)**2)
RKn = DOT_PRODUCT(RTiltedK,RNormDirM)
RkPrime(1)=DOT_PRODUCT(RTiltedK,RgPool(knd,:))/RgPoolMag(knd)
RkPrime(3) = SQRT(RBigK**2-RkPrime(1)**2)
RDevPara(knd)=-SIGN(ONE,(2*DOT_PRODUCT(RgPool(knd,:),RTiltedK)+RgPoolMag(knd)**2))*&
RgPoolMag(knd)*SQRT(2*(RBigK**2-DOT_PRODUCT(Rk0,RkPrime)))/RBigK
/*CUgSgMatrix=CUgMa*/
CALL ZGEMM('N','N',INhkl,nBeams,INhkl,CONE,CUgMat, &
INhkl,CBeamTranspose,INhkl,CZERO,CUgMatPartial,INhkl)
CALL ZGEMM('N','N',nBeams,nBeams,INhkl,CONE,CBeamProjectionMatrix, &
nBeams,CUgMatPartial,INhkl,CZERO,CUgSgMatrix,nBeams)
/*excitation error */
CALL StrongAndWeakBeamsDetermination(RDevPara,CUgMat)
CALL EigenSpectrum(nBeams,CUgSgMatrix,CEigenValues,CEigenVectors)
CALL ZGEEV('N','V', IMatrixDimension, MatrixToBeDiagonalised, IMatrixDimension,&
EigenValues, 0,1, EigenVectors,IMatrixDimension, &
CWorkSpace, WorkSpaceDimension, WorkSpace, IErr )
CALL INVERT(nBeams,CDummyEigenVectors(:,:),CInvertedEigenVectors,IErr)
DO IThicknessIndex=1,IThicknessCount,1
CALL CreateWaveFunctions(RThickness,RFullWaveIntensity,&
INhkl,CEigenVectors,CInvertedEigenVectors,CEigenValues)
RIndividualReflections(1:INoOfLacbedPatterns,IThicknessIndex,&
(IPixelNumber-IFirstPixelToCalculate)+1) = &
RFullWaveIntensity(1:INoOfLacbedPatterns)
Excitation error
Formula used : , , , ,
//bloch_mod.f90
SUBROUTINE BlochCoefficientCalculation
DO ind=1,nBeams
CUgSgMatrix(ind,ind) = TWO*RBigK*RDevPara(IStrongBeamList(ind))/(TWOPI*TWOPI)
ENDDO
// ! add the weak beams perturbatively for the 1st column (sumC) and
// ! the diagonal elements (sumD)
intensity
SUBROUTINE CreateWaveFunctions
CAlphaWeightingCoefficients = MATMUL(CInvertedEigenVectors,CPsi0)
DO hnd=1,nBeams //! This is a diagonal matrix
CEigenValueDependentTerms(hnd,hnd) = EXP(CIMAGONE*CMPLX(RThickness,ZERO,CKIND)*CEigenValues(hnd))
CWaveFunctions = MATMUL(MATMUL(CEigenVectors,CEigenValueDependentTerms), CAlphaWeightingCoefficients)
DO hnd=1,nBeams
RWaveIntensity(hnd)=CONJG(CWaveFunctions(hnd)) * CWaveFunctions(hnd)
RFullWaveIntensity(IStrongBeamList(hnd))=RWaveIntensity(hnd)
Strong/weak beam determination
SUBROUTINE StrongAndWeakBeamsDetermination
RMaxSg = 0.005
RMinPertStrong=0.0025/RMaxSg// Gives additional beams based on perturbation strength
CALL message(LXL,dbg7,"Strong Beam List",IStrongBeamList)
CALL message(LXL,dbg7,"Sg limit for strong beams = ",RMaxSg)
CALL message(LXL,dbg7,"Smallest strong perturbation strength = ",RMinPertStrong)
CALL message(LXL,dbg7,"weak beams",SUM(IWeak))
CALL message(LXL,dbg7,"Smallest weak perturbation strength = ",RMinPertWeak)
CALL message(LXL,dbg7,"Weak Beam List",IWeakBeamList)
Structure factor
Pool of reflections
//setup_reflections_mod.f90
SUBROUTINE HKLMake(RTol, IErr)
DO Ih=-inda,inda
DO Ik=-indb,indb
DO Il=-indc,indc
CALL SelectionRules(Ih, Ik, Il, ISel, IErr)
Rhkl(knd,:)=REAL((/ Ih,Ik,Il /),RKIND)
G vectors and miller indices
//crstallography_mod.f90
SUBROUTINE gVectors(IErr)
//calculate g-vector pool, the magnitudes and component parallel to specimen surface
DO ind=1,INhkl
DO jnd=1,ITHREE
RgPool(ind,jnd) = Rhkl(ind,1)*RarVecM(jnd) + &
Rhkl(ind,2)*RbrVecM(jnd) + Rhkl(ind,3)*RcrVecM(jnd)
// Calculate matrix of g-vectors that corresponds to the Ug matrix
DO ind=1,INhkl
DO jnd=1,INhkl
RgMatrix(ind,jnd,:)= RgPool(ind,:)-RgPool(jnd,:)
Read Coordinates from cif
//read_cif_mod.f90
SUBROUTINE read_cif(IErr)
f2 = numb_('_atom_site_fract_x', x, sx)
RBasisAtomPosition(ind,1)= x
f2 = numb_('_atom_site_fract_y', y, sy)
RBasisAtomPosition(ind,2)= y
f2 = numb_('_atom_site_fract_z', z, sz)
RBasisAtomPosition(ind,3)= z
Read symmetries from cif
//read_cif_mod.f90
SUBROUTINE read_cif(IErr)
! count how many symmetry elements
f1 = char_('_symmetry_equiv_pos_as_xyz', name)
ISymCount=ISymCount+1
IF(loop_ .NEQV. .TRUE.) EXIT
f1 = char_('_symmetry_equiv_pos_as_xyz', name)
DO ind=1,ITHREE
Ipos= SCAN(name, "zZ")
IF(name(Ipos-2:Ipos-2)=="-") IFRACminus=-1
RSymVec(ISymCount,ind)=IFRACminus*REAL(Inum)/REAL(Idenom)
RSymMat(ISymCount, ind,2)=IoneI
Construct all coordinates
SUBROUTINE UniqueAtomPositions(IErr)
//apply symmetry elements to generate all equivalent positions
DO ind=1, SIZE(RSymVec,1)
DO jnd=1, SIZE(RBasisAtomPosition,1)
RAllAtomPosition(knd,:)=MATMUL(RSymMat(ind,:,:),RBasisAtomPosition(jnd,:))+ RSymVec(ind,:)
RAtomPosition(1,:)= RAllAtomPosition(1,:)
//calculate from Fractional Coordinates and Lattice Vectors
//In microscope reference frame, in Angstrom units
DO ind=1,INAtomsUnitCell
DO jnd=1,ITHREE
RAtomCoordinate(ind,jnd)= RAtomPosition(ind,1)*RaVecM(jnd) +
RAtomPosition(ind,2)*RbVecM(jnd) +
RAtomPosition(ind,3)*RcVecM(jnd)
Compute structure factor
//felixrefine.f90
RRelativisticCorrection = ONE/SQRT( ONE - (RElectronVelocity/RSpeedOfLight)**2 )
RElectronWaveVectorMagnitude=TWOPI/RElectronWaveLength
//ug_matrix_mod.f90:
RBigK= SQRT(RElectronWaveVectorMagnitude**2)//!-RMeanInnerPotential)
SUBROUTINE StructureFactorInitialisation(IErr)
CALL message ( LM, dbg3, "K (Angstroms) = ",RBigK )
CALL UgMatrix(IErr)
SUBROUTINE UgMatrix(IErr)
RPreFactor=RRelativisticCorrection/(PI*RVolume)
DO ind=2,INhkl
DO jnd=1,ind-1
RCurrentGMagnitude = SQRT(DOT_PRODUCT(RgMatrix(ind,jnd,:),RgMatrix(ind,jnd,:)))
DO knd=1,INAtomsUnitCell
CALL AtomicScatteringFactor(RScatteringFactor,IErr)
CUgMatNoAbs(ind,jnd)=CUgMatNoAbs(ind,jnd)+
RPreFactor*RScatteringFactor*EXP(
CIMAGONE*DOT_PRODUCT(RgMatrix(ind,jnd,:),RAtomCoordinate(knd,:)) )
CUgMat = CUgMatNoAbs + CUgMatPrime
CALL message( LM,dbg3, "Ug matrix, without absorption (nm^-2)" )!LM, dbg3
DO ind = 1,40
IF(IWriteFLAG.GE.2) WRITE(SPrintString,FMT='(3(I3,1X),A2,1X,6(F7.4,1X,F7.4,2X))') NINT(Rhkl(ind,:)),": ",100*CUgMatNoAbs(ind,1:6)
SUBROUTINE AtomicScatteringFactor(RScatteringFactor,IErr)
SELECT CASE (IScatterFactorMethodFLAG)
CASE(0) ! Kirkland Method using 3 Gaussians and 3 Lorentzians
// Kirkland scattering factor is in Angstrom units
// atomic number and g-vector passed as global variables
RScatteringFactor = Kirkland(RCurrentGMagnitude)
FUNCTION Kirkland(Rg)
Rq = Rg / TWOPI
DO ind = 1,3
Ra=RScattFactors(ICurrentZ,ind*2-1);
Rb=RScattFactors(ICurrentZ,ind*2);
Rc=RScattFactors(ICurrentZ,ind*2+5);
Rd=RScattFactors(ICurrentZ,ind*2+6);
Kirkland = Kirkland + Ra/((Rq^2)+Rb) + Rc*EXP(-(Rd*Rq^2))
Write StructureFactors
//write_output_mod.f90
WRITE(filename,*) "StructureFactors.txt"
DO ind = 1,INhkl
WRITE(IChOut,FMT='(3I5.1,2(1X,F13.9),2(1X,E14.6))') NINT(Rhkl(ind,:)),&
RgPool(ind,1),RgPool(ind,2),CUgMat(ind,1)
Misc
read cif file
//read_cif_file_mod.f90
f1 = char_('_chemical_formula_sum', name)
f1 = numb_('_cell_angle_alpha', cela, siga)
f1 = numb_('_symmetry_Int_tables_number',numb,sx)
!get atomic number
!coordinates of the basis
f2 = numb_('_atom_site_fract_x', x, sx)
! count how many symmetry elements
f1 = char_('_symmetry_equiv_pos_as_xyz', name)
read inp file
variables read from felix.inp into RIndependentVariable:
//read_files_mod.f90
ILine= ILine+1; READ(IChInp,'(27X,I15.1)',ERR=20,END=30) IWriteFLAG
ILine= ILine+1; READ(IChInp,'(27X,I15.1)',ERR=20,END=30) IPixelCount
//felix/refinementcontrol_mod.f90:
RConvergenceAngle = RIndependentVariable(ind)
set parameters
//felix/setup_reflections_mod.f90:
SUBROUTINE ReadHklFile(IErr)
OPEN(Unit = IChInp,FILE="felix.hkl",STATUS='OLD',ERR=10)
IHKLSelectFLAG=1
INoOfLacbedPatterns = ILine
CALL message ( LXL, dbg7, "Number of experimental images to load = ", INoOfLacbedPatterns)
RInputHKLs(ILine,1) = REAL(h,RKIND)
RInputHKLs(ILine,2) = REAL(k,RKIND)
RInputHKLs(ILine,3) = REAL(l,RKIND)
SUBROUTINE HKLList( IErr )
INoOfLacbedPatterns = IFind
//felix/crystallography_mod.f90:!
RDeltaK = RConvergenceAngle/REAL(IPixelCount,RKIND)
RNormDirM = MATMUL(RTMatO2M,MATMUL(RTMatC2O,RNormDirC))
RNormDirM = RNormDirM/SQRT(DOT_PRODUCT(RNormDirM,RNormDirM))
//felix/felixrefine.f90:
RDeltaK = TWOPI*RConvergenceAngle/REAL(IPixelCount,RKIND)
refinement
//felixrefine.f90
IF(ISimFLAG.EQ.0) THEN
IF(IRefineMode(1).EQ.1) THEN // It's a Ug refinement, A
IF (IRefineMode(3).EQ.1) THEN // Occupancy, C
INoOfVariables = jnd-1
Debug
In felix.inp, IWriteFLAG can be assigned different INTEGER values to affect which messages are printed. The digit of the INTEGER affects the minimum priority of the message printed: 0 = LS, 2 = LM, 4 = LL, 8 = LXL
IHolzFLAG: high order laue zone tech(not functional apparently )IBlochMethodFLAG: using koch method